1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
140 //================================================================================
142 * \brief Initializes members by an existing element
143 * \param [in] elem - the source element
144 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146 //================================================================================
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 myType = elem->GetType();
154 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156 myIsPoly = elem->IsPoly();
159 myIsQuad = elem->IsQuadratic();
160 if ( myType == SMDSAbs_Volume && !basicOnly )
162 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163 myPolyhedQuantities.swap( quant );
167 else if ( myType == SMDSAbs_Ball && !basicOnly )
169 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175 //=======================================================================
179 //=======================================================================
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183 const ElemFeatures& features)
185 SMDS_MeshElement* e = 0;
186 int nbnode = node.size();
187 SMESHDS_Mesh* mesh = GetMeshDS();
188 const int ID = features.myID;
190 switch ( features.myType ) {
192 if ( !features.myIsPoly ) {
194 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195 else e = mesh->AddFace (node[0], node[1], node[2] );
197 else if (nbnode == 4) {
198 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
207 else if (nbnode == 7) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6] );
213 else if (nbnode == 8) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7] );
219 else if (nbnode == 9) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], node[8], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8] );
226 else if ( !features.myIsQuad )
228 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229 else e = mesh->AddPolygonalFace (node );
231 else if ( nbnode % 2 == 0 ) // just a protection
233 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234 else e = mesh->AddQuadPolygonalFace (node );
239 if ( !features.myIsPoly ) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
244 else if (nbnode == 5) {
245 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
250 else if (nbnode == 6) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 8) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7] );
262 else if (nbnode == 10) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
270 else if (nbnode == 12) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10], node[11], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11] );
278 else if (nbnode == 13) {
279 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
283 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10],node[11],
288 else if (nbnode == 15) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
292 node[12],node[13],node[14],ID);
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
296 node[12],node[13],node[14] );
298 else if (nbnode == 20) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],node[15],
303 node[16],node[17],node[18],node[19],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],node[15],
308 node[16],node[17],node[18],node[19] );
310 else if (nbnode == 27) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],
316 node[20],node[21],node[22],node[23],
317 node[24],node[25],node[26], ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19],
323 node[20],node[21],node[22],node[23],
324 node[24],node[25],node[26] );
327 else if ( !features.myIsQuad )
329 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
334 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
341 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342 else e = mesh->AddEdge (node[0], node[1] );
344 else if ( nbnode == 3 ) {
345 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346 else e = mesh->AddEdge (node[0], node[1], node[2] );
350 case SMDSAbs_0DElement:
352 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353 else e = mesh->Add0DElement (node[0] );
358 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
363 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364 else e = mesh->AddBall (node[0], features.myBallDiameter );
369 if ( e ) myLastCreatedElems.Append( e );
373 //=======================================================================
377 //=======================================================================
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380 const ElemFeatures& features)
382 vector<const SMDS_MeshNode*> nodes;
383 nodes.reserve( nodeIDs.size() );
384 vector<int>::const_iterator id = nodeIDs.begin();
385 while ( id != nodeIDs.end() ) {
386 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387 nodes.push_back( node );
391 return AddElement( nodes, features );
394 //=======================================================================
396 //purpose : Remove a node or an element.
397 // Modify a compute state of sub-meshes which become empty
398 //=======================================================================
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403 myLastCreatedElems.Clear();
404 myLastCreatedNodes.Clear();
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object except those
466 * nodes on which a 0D element already exists.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems )
476 SMDS_ElemIteratorPtr elemIt;
477 vector< const SMDS_MeshElement* > allNodes;
478 if ( elements.empty() )
480 allNodes.reserve( GetMeshDS()->NbNodes() );
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482 while ( elemIt->more() )
483 allNodes.push_back( elemIt->next() );
485 elemIt = elemSetIterator( allNodes );
489 elemIt = elemSetIterator( elements );
492 while ( elemIt->more() )
494 const SMDS_MeshElement* e = elemIt->next();
495 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496 while ( nodeIt->more() )
498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
501 all0DElems.insert( it0D->next() );
503 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504 all0DElems.insert( myLastCreatedElems.Last() );
510 //=======================================================================
511 //function : FindShape
512 //purpose : Return an index of the shape theElem is on
513 // or zero if a shape not found
514 //=======================================================================
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 myLastCreatedElems.Clear();
519 myLastCreatedNodes.Clear();
521 SMESHDS_Mesh * aMesh = GetMeshDS();
522 if ( aMesh->ShapeToMesh().IsNull() )
525 int aShapeID = theElem->getshapeId();
529 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530 if ( sm->Contains( theElem ))
533 if ( theElem->GetType() == SMDSAbs_Node ) {
534 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
537 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
540 TopoDS_Shape aShape; // the shape a node of theElem is on
541 if ( theElem->GetType() != SMDSAbs_Node )
543 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544 while ( nodeIt->more() ) {
545 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546 if ((aShapeID = node->getshapeId()) > 0) {
547 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548 if ( sm->Contains( theElem ))
550 if ( aShape.IsNull() )
551 aShape = aMesh->IndexToShape( aShapeID );
557 // None of nodes is on a proper shape,
558 // find the shape among ancestors of aShape on which a node is
559 if ( !aShape.IsNull() ) {
560 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561 for ( ; ancIt.More(); ancIt.Next() ) {
562 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563 if ( sm && sm->Contains( theElem ))
564 return aMesh->ShapeToIndex( ancIt.Value() );
569 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570 while ( const SMESHDS_SubMesh* sm = smIt->next() )
571 if ( sm->Contains( theElem ))
578 //=======================================================================
579 //function : IsMedium
581 //=======================================================================
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
584 const SMDSAbs_ElementType typeToCheck)
586 bool isMedium = false;
587 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588 while (it->more() && !isMedium ) {
589 const SMDS_MeshElement* elem = it->next();
590 isMedium = elem->IsMediumNode(node);
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose : Shift nodes in the array corresponded to quadratic triangle
598 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
603 const SMDS_MeshNode* nd1 = aNodes[0];
604 aNodes[0] = aNodes[1];
605 aNodes[1] = aNodes[2];
607 const SMDS_MeshNode* nd2 = aNodes[3];
608 aNodes[3] = aNodes[4];
609 aNodes[4] = aNodes[5];
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose : return number of the edges connected with the theNode.
616 // if theEdges has connections with the other type of the
617 // elements, return -1
618 //=======================================================================
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
622 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
624 // while(elemIt->more()) {
629 return theNode->NbInverseElements();
632 //=======================================================================
633 //function : getNodesFromTwoTria
635 //=======================================================================
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2,
639 vector< const SMDS_MeshNode*>& N1,
640 vector< const SMDS_MeshNode*>& N2)
642 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643 if ( N1.size() < 6 ) return false;
644 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645 if ( N2.size() < 6 ) return false;
647 int sames[3] = {-1,-1,-1};
659 if(nbsames!=2) return false;
661 shiftNodesQuadTria(N1);
663 shiftNodesQuadTria(N1);
666 i = sames[0] + sames[1] + sames[2];
668 shiftNodesQuadTria(N2);
670 // now we receive following N1 and N2 (using numeration as in the image below)
671 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
672 // i.e. first nodes from both arrays form a new diagonal
676 //=======================================================================
677 //function : InverseDiag
678 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
679 // but having other common link.
680 // Return False if args are improper
681 //=======================================================================
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684 const SMDS_MeshElement * theTria2 )
686 MESSAGE("InverseDiag");
687 myLastCreatedElems.Clear();
688 myLastCreatedNodes.Clear();
690 if (!theTria1 || !theTria2)
693 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694 if (!F1) return false;
695 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696 if (!F2) return false;
697 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
700 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
705 // put nodes in array and find out indices of the same ones
706 const SMDS_MeshNode* aNodes [6];
707 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
709 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710 while ( it->more() ) {
711 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
713 if ( i > 2 ) // theTria2
714 // find same node of theTria1
715 for ( int j = 0; j < 3; j++ )
716 if ( aNodes[ i ] == aNodes[ j ]) {
725 return false; // theTria1 is not a triangle
726 it = theTria2->nodesIterator();
728 if ( i == 6 && it->more() )
729 return false; // theTria2 is not a triangle
732 // find indices of 1,2 and of A,B in theTria1
733 int iA = -1, iB = 0, i1 = 0, i2 = 0;
734 for ( i = 0; i < 6; i++ ) {
735 if ( sameInd [ i ] == -1 ) {
740 if ( iA >= 0) iB = i;
744 // nodes 1 and 2 should not be the same
745 if ( aNodes[ i1 ] == aNodes[ i2 ] )
749 aNodes[ iA ] = aNodes[ i2 ];
751 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
753 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
758 } // end if(F1 && F2)
760 // check case of quadratic faces
761 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778 vector< const SMDS_MeshNode* > N1;
779 vector< const SMDS_MeshNode* > N2;
780 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
782 // now we receive following N1 and N2 (using numeration as above image)
783 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
784 // i.e. first nodes from both arrays determ new diagonal
786 vector< const SMDS_MeshNode*> N1new( N1.size() );
787 vector< const SMDS_MeshNode*> N2new( N2.size() );
788 N1new.back() = N1.back(); // central node of biquadratic
789 N2new.back() = N2.back();
790 N1new[0] = N1[0]; N2new[0] = N1[0];
791 N1new[1] = N2[0]; N2new[1] = N1[1];
792 N1new[2] = N2[1]; N2new[2] = N2[0];
793 N1new[3] = N1[4]; N2new[3] = N1[3];
794 N1new[4] = N2[3]; N2new[4] = N2[5];
795 N1new[5] = N1[5]; N2new[5] = N1[4];
796 // change nodes in faces
797 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
800 // move the central node of biquadratic triangle
801 SMESH_MesherHelper helper( *GetMesh() );
802 for ( int is2nd = 0; is2nd < 2; ++is2nd )
804 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
805 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806 if ( nodes.size() < 7 )
808 helper.SetSubShape( tria->getshapeId() );
809 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
813 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814 SMESH_TNodeXYZ( nodes[4] ) +
815 SMESH_TNodeXYZ( nodes[5] )) / 3.;
820 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
824 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825 xyz = S->Value( uv.X(), uv.Y() );
826 xyz.Transform( loc );
827 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
828 nodes[6]->getshapeId() > 0 )
829 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
831 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
836 //=======================================================================
837 //function : findTriangles
838 //purpose : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
841 static bool findTriangles(const SMDS_MeshNode * theNode1,
842 const SMDS_MeshNode * theNode2,
843 const SMDS_MeshElement*& theTria1,
844 const SMDS_MeshElement*& theTria2)
846 if ( !theNode1 || !theNode2 ) return false;
848 theTria1 = theTria2 = 0;
850 set< const SMDS_MeshElement* > emap;
851 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( elem->NbCornerNodes() == 3 )
857 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( emap.count( elem )) {
868 // theTria1 must be element with minimum ID
869 if ( theTria2->GetID() < theTria1->GetID() )
870 std::swap( theTria2, theTria1 );
878 //=======================================================================
879 //function : InverseDiag
880 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
881 // with ones built on the same 4 nodes but having other common link.
882 // Return false if proper faces not found
883 //=======================================================================
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886 const SMDS_MeshNode * theNode2)
888 myLastCreatedElems.Clear();
889 myLastCreatedNodes.Clear();
891 MESSAGE( "::InverseDiag()" );
893 const SMDS_MeshElement *tr1, *tr2;
894 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
897 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898 if (!F1) return false;
899 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900 if (!F2) return false;
901 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
904 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
909 // put nodes in array
910 // and find indices of 1,2 and of A in tr1 and of B in tr2
911 int i, iA1 = 0, i1 = 0;
912 const SMDS_MeshNode* aNodes1 [3];
913 SMDS_ElemIteratorPtr it;
914 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes1[ i ] == theNode1 )
917 iA1 = i; // node A in tr1
918 else if ( aNodes1[ i ] != theNode2 )
922 const SMDS_MeshNode* aNodes2 [3];
923 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes2[ i ] == theNode2 )
926 iB2 = i; // node B in tr2
927 else if ( aNodes2[ i ] != theNode1 )
931 // nodes 1 and 2 should not be the same
932 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936 aNodes1[ iA1 ] = aNodes2[ i2 ];
938 aNodes2[ iB2 ] = aNodes1[ i1 ];
940 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946 // check case of quadratic faces
947 return InverseDiag(tr1,tr2);
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
953 // fusion of triangles tr1 and tr2 having shared link on
954 // theNode1 and theNode2
955 //=======================================================================
957 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
958 const SMDS_MeshNode * theNode1,
959 const SMDS_MeshNode * theNode2,
960 const SMDS_MeshElement * tr1,
961 const SMDS_MeshElement * tr2 )
963 if( tr1->NbNodes() != tr2->NbNodes() )
965 // find the 4-th node to insert into tr1
966 const SMDS_MeshNode* n4 = 0;
967 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
969 while ( !n4 && i<3 ) {
970 const SMDS_MeshNode * n = cast2Node( it->next() );
972 bool isDiag = ( n == theNode1 || n == theNode2 );
976 // Make an array of nodes to be in a quadrangle
977 int iNode = 0, iFirstDiag = -1;
978 it = tr1->nodesIterator();
981 const SMDS_MeshNode * n = cast2Node( it->next() );
983 bool isDiag = ( n == theNode1 || n == theNode2 );
985 if ( iFirstDiag < 0 )
987 else if ( iNode - iFirstDiag == 1 )
988 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
990 else if ( n == n4 ) {
991 return false; // tr1 and tr2 should not have all the same nodes
993 theQuadNodes[ iNode++ ] = n;
995 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996 theQuadNodes[ iNode ] = n4;
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 // with a quadrangle built on the same 4 nodes.
1005 // Return false if proper faces not found
1006 //=======================================================================
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009 const SMDS_MeshNode * theNode2)
1011 myLastCreatedElems.Clear();
1012 myLastCreatedNodes.Clear();
1014 MESSAGE( "::DeleteDiag()" );
1016 const SMDS_MeshElement *tr1, *tr2;
1017 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021 if (!F1) return false;
1022 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023 if (!F2) return false;
1024 SMESHDS_Mesh * aMesh = GetMeshDS();
1026 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029 const SMDS_MeshNode* aNodes [ 4 ];
1030 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033 const SMDS_MeshElement* newElem = 0;
1034 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035 myLastCreatedElems.Append(newElem);
1036 AddToSameGroups( newElem, tr1, aMesh );
1037 int aShapeId = tr1->getshapeId();
1040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042 aMesh->RemoveElement( tr1 );
1043 aMesh->RemoveElement( tr2 );
1048 // check case of quadratic faces
1049 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064 vector< const SMDS_MeshNode* > N1;
1065 vector< const SMDS_MeshNode* > N2;
1066 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068 // now we receive following N1 and N2 (using numeration as above image)
1069 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1070 // i.e. first nodes from both arrays determ new diagonal
1072 const SMDS_MeshNode* aNodes[8];
1082 const SMDS_MeshElement* newElem = 0;
1083 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085 myLastCreatedElems.Append(newElem);
1086 AddToSameGroups( newElem, tr1, aMesh );
1087 int aShapeId = tr1->getshapeId();
1090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092 aMesh->RemoveElement( tr1 );
1093 aMesh->RemoveElement( tr2 );
1095 // remove middle node (9)
1096 GetMeshDS()->RemoveNode( N1[4] );
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose : Reverse theElement orientation
1104 //=======================================================================
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 MESSAGE("Reorient");
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that sould be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const size_t nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 helper.SetIsQuadratic ( nodes.size() > 4 );
1605 helper.SetIsBiQuadratic( nodes.size() == 9 );
1606 if ( helper.GetIsQuadratic() )
1607 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1609 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1611 for ( int i = 0; i < 4; ++i )
1613 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617 myLastCreatedElems.Append( tria );
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose : Find better diagonal for cutting.
1625 //=======================================================================
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1628 SMESH::Controls::NumericalFunctorPtr theCrit)
1630 myLastCreatedElems.Clear();
1631 myLastCreatedNodes.Clear();
1636 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639 if( theQuad->NbNodes()==4 ||
1640 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642 // retrieve element nodes
1643 const SMDS_MeshNode* aNodes [4];
1644 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646 //while (itN->more())
1648 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650 // compare two sets of possible triangles
1651 double aBadRate1, aBadRate2; // to what extent a set is bad
1652 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662 return 1; // diagonal 1-3
1664 return 2; // diagonal 2-4
1671 // Methods of splitting volumes into tetra
1673 const int theHexTo5_1[5*4+1] =
1675 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1677 const int theHexTo5_2[5*4+1] =
1679 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1681 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683 const int theHexTo6_1[6*4+1] =
1685 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1687 const int theHexTo6_2[6*4+1] =
1689 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1691 const int theHexTo6_3[6*4+1] =
1693 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1695 const int theHexTo6_4[6*4+1] =
1697 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1699 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701 const int thePyraTo2_1[2*4+1] =
1703 0, 1, 2, 4, 0, 2, 3, 4, -1
1705 const int thePyraTo2_2[2*4+1] =
1707 1, 2, 3, 4, 1, 3, 0, 4, -1
1709 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711 const int thePentaTo3_1[3*4+1] =
1713 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1715 const int thePentaTo3_2[3*4+1] =
1717 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1719 const int thePentaTo3_3[3*4+1] =
1721 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1723 const int thePentaTo3_4[3*4+1] =
1725 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1727 const int thePentaTo3_5[3*4+1] =
1729 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1731 const int thePentaTo3_6[3*4+1] =
1733 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1735 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738 // Methods of splitting hexahedron into prisms
1740 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1744 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1748 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1753 const int theHexTo2Prisms_BT_1[6*2+1] =
1755 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1757 const int theHexTo2Prisms_BT_2[6*2+1] =
1759 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1761 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763 const int theHexTo2Prisms_LR_1[6*2+1] =
1765 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1767 const int theHexTo2Prisms_LR_2[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773 const int theHexTo2Prisms_FB_1[6*2+1] =
1775 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1777 const int theHexTo2Prisms_FB_2[6*2+1] =
1779 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1781 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1790 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797 bool _baryNode; //!< additional node is to be created at cell barycenter
1798 bool _ownConn; //!< to delete _connectivity in destructor
1799 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804 bool hasFacet( const TTriangleFacet& facet ) const
1806 if ( _nbCorners == 4 )
1808 const int* tetConn = _connectivity;
1809 for ( ; tetConn[0] >= 0; tetConn += 4 )
1810 if (( facet.contains( tetConn[0] ) +
1811 facet.contains( tetConn[1] ) +
1812 facet.contains( tetConn[2] ) +
1813 facet.contains( tetConn[3] )) == 3 )
1816 else // prism, _nbCorners == 6
1818 const int* prismConn = _connectivity;
1819 for ( ; prismConn[0] >= 0; prismConn += 6 )
1821 if (( facet.contains( prismConn[0] ) &&
1822 facet.contains( prismConn[1] ) &&
1823 facet.contains( prismConn[2] ))
1825 ( facet.contains( prismConn[3] ) &&
1826 facet.contains( prismConn[4] ) &&
1827 facet.contains( prismConn[5] )))
1835 //=======================================================================
1837 * \brief return TSplitMethod for the given element to split into tetrahedra
1839 //=======================================================================
1841 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846 // an edge and a face barycenter; tertaherdons are based on triangles and
1847 // a volume barycenter
1848 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850 // Find out how adjacent volumes are split
1852 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858 if ( nbNodes < 4 ) continue;
1860 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861 const int* nInd = vol.GetFaceNodesIndices( iF );
1864 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871 int iCom = 0; // common node of triangle faces to split into
1872 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1875 nInd[ iQ * ( (iCom+1)%nbNodes )],
1876 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )],
1879 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882 triaSplits.push_back( t012 );
1883 triaSplits.push_back( t023 );
1888 if ( !triaSplits.empty() )
1889 hasAdjacentSplits = true;
1892 // Among variants of split method select one compliant with adjacent volumes
1894 TSplitMethod method;
1895 if ( !vol.Element()->IsPoly() && !is24TetMode )
1897 int nbVariants = 2, nbTet = 0;
1898 const int** connVariants = 0;
1899 switch ( vol.Element()->GetEntityType() )
1901 case SMDSEntity_Hexa:
1902 case SMDSEntity_Quad_Hexa:
1903 case SMDSEntity_TriQuad_Hexa:
1904 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905 connVariants = theHexTo5, nbTet = 5;
1907 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909 case SMDSEntity_Pyramid:
1910 case SMDSEntity_Quad_Pyramid:
1911 connVariants = thePyraTo2; nbTet = 2;
1913 case SMDSEntity_Penta:
1914 case SMDSEntity_Quad_Penta:
1915 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1920 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1922 // check method compliancy with adjacent tetras,
1923 // all found splits must be among facets of tetras described by this method
1924 method = TSplitMethod( nbTet, connVariants[variant] );
1925 if ( hasAdjacentSplits && method._nbSplits > 0 )
1927 bool facetCreated = true;
1928 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932 facetCreated = method.hasFacet( *facet );
1934 if ( !facetCreated )
1935 method = TSplitMethod(0); // incompatible method
1939 if ( method._nbSplits < 1 )
1941 // No standard method is applicable, use a generic solution:
1942 // each facet of a volume is split into triangles and
1943 // each of triangles and a volume barycenter form a tetrahedron.
1945 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947 int* connectivity = new int[ maxTetConnSize + 1 ];
1948 method._connectivity = connectivity;
1949 method._ownConn = true;
1950 method._baryNode = !isHex27; // to create central node or not
1953 int baryCenInd = vol.NbNodes() - int( isHex27 );
1954 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957 const int* nInd = vol.GetFaceNodesIndices( iF );
1958 // find common node of triangle facets of tetra to create
1959 int iCommon = 0; // index in linear numeration
1960 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961 if ( !triaSplits.empty() )
1964 const TTriangleFacet* facet = &triaSplits.front();
1965 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970 else if ( nbNodes > 3 && !is24TetMode )
1972 // find the best method of splitting into triangles by aspect ratio
1973 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974 map< double, int > badness2iCommon;
1975 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1983 nodes[ iQ*((iLast-1)%nbNodes)],
1984 nodes[ iQ*((iLast )%nbNodes)]);
1985 badness += getBadRate( &tria, aspectRatio );
1987 badness2iCommon.insert( make_pair( badness, iCommon ));
1989 // use iCommon with lowest badness
1990 iCommon = badness2iCommon.begin()->second;
1992 if ( iCommon >= nbNodes )
1993 iCommon = 0; // something wrong
1995 // fill connectivity of tetrahedra based on a current face
1996 int nbTet = nbNodes - 2;
1997 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007 method._faceBaryNode[ iF ] = 0;
2008 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011 for ( int i = 0; i < nbTet; ++i )
2013 int i1 = i, i2 = (i+1) % nbNodes;
2014 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = faceBaryCenInd;
2018 connectivity[ connSize++ ] = baryCenInd;
2023 for ( int i = 0; i < nbTet; ++i )
2025 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030 connectivity[ connSize++ ] = baryCenInd;
2033 method._nbSplits += nbTet;
2035 } // loop on volume faces
2037 connectivity[ connSize++ ] = -1;
2039 } // end of generic solution
2043 //=======================================================================
2045 * \brief return TSplitMethod to split haxhedron into prisms
2047 //=======================================================================
2049 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050 const int methodFlags,
2051 const int facetToSplit)
2053 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059 static TSplitMethod to4methods[4]; // order BT, LR, FB
2060 if ( to4methods[iF]._nbSplits == 0 )
2064 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078 default: return to4methods[3];
2080 to4methods[iF]._nbSplits = 4;
2081 to4methods[iF]._nbCorners = 6;
2083 return to4methods[iF];
2085 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087 TSplitMethod method;
2089 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091 const int nbVariants = 2, nbSplits = 2;
2092 const int** connVariants = 0;
2094 case 0: connVariants = theHexTo2Prisms_BT; break;
2095 case 1: connVariants = theHexTo2Prisms_LR; break;
2096 case 2: connVariants = theHexTo2Prisms_FB; break;
2097 default: return method;
2100 // look for prisms adjacent via facetToSplit and an opposite one
2101 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105 if ( nbNodes != 4 ) return method;
2107 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 // there are adjacent prism
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2121 // check method compliancy with adjacent prisms,
2122 // the found prism facets must be among facets of prisms described by current method
2123 method._nbSplits = nbSplits;
2124 method._nbCorners = 6;
2125 method._connectivity = connVariants[ variant ];
2126 if ( method.hasFacet( *t ))
2131 // No adjacent prisms. Select a variant with a best aspect ratio.
2133 double badness[2] = { 0, 0 };
2134 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135 const SMDS_MeshNode** nodes = vol.GetNodes();
2136 for ( int variant = 0; variant < nbVariants; ++variant )
2137 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2142 method._connectivity = connVariants[ variant ];
2143 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150 badness[ variant ] += getBadRate( &tria, aspectRatio );
2152 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154 method._nbSplits = nbSplits;
2155 method._nbCorners = 6;
2156 method._connectivity = connVariants[ iBetter ];
2161 //================================================================================
2163 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165 //================================================================================
2167 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2168 const SMDSAbs_GeometryType geom ) const
2170 // find the tetrahedron including the three nodes of facet
2171 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175 while ( volIt1->more() )
2177 const SMDS_MeshElement* v = volIt1->next();
2178 if ( v->GetGeomType() != geom )
2180 const int lastCornerInd = v->NbCornerNodes() - 1;
2181 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182 continue; // medium node not allowed
2183 const int ind2 = v->GetNodeIndex( n2 );
2184 if ( ind2 < 0 || lastCornerInd < ind2 )
2186 const int ind3 = v->GetNodeIndex( n3 );
2187 if ( ind3 < 0 || lastCornerInd < ind3 )
2194 //=======================================================================
2196 * \brief A key of a face of volume
2198 //=======================================================================
2200 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204 TIDSortedNodeSet sortedNodes;
2205 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206 int nbNodes = vol.NbFaceNodes( iF );
2207 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208 for ( int i = 0; i < nbNodes; i += iQ )
2209 sortedNodes.insert( fNodes[i] );
2210 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211 first.first = (*(n++))->GetID();
2212 first.second = (*(n++))->GetID();
2213 second.first = (*(n++))->GetID();
2214 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose : Split volume elements into tetrahedra or prisms.
2222 // If facet ID < 0, element is split into tetrahedra,
2223 // else a hexahedron is split into prisms so that the given facet is
2224 // split into triangles
2225 //=======================================================================
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228 const int theMethodFlags)
2230 SMDS_VolumeTool volTool;
2231 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232 fHelper.ToFixNodeParameters( true );
2234 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237 SMESH_SequenceOfElemPtr newNodes, newElems;
2239 // map face of volume to it's baricenrtic node
2240 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2242 vector<const SMDS_MeshElement* > splitVols;
2244 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245 for ( ; elem2facet != theElems.end(); ++elem2facet )
2247 const SMDS_MeshElement* elem = elem2facet->first;
2248 const int facetToSplit = elem2facet->second;
2249 if ( elem->GetType() != SMDSAbs_Volume )
2251 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2257 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2258 getTetraSplitMethod( volTool, theMethodFlags ) :
2259 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260 if ( splitMethod._nbSplits < 1 ) continue;
2262 // find submesh to add new tetras to
2263 if ( !subMesh || !subMesh->Contains( elem ))
2265 int shapeID = FindShape( elem );
2266 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267 subMesh = GetMeshDS()->MeshElements( shapeID );
2270 if ( elem->IsQuadratic() )
2273 // add quadratic links to the helper
2274 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2276 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278 for ( int iN = 0; iN < nbN; iN += iQ )
2279 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2281 helper.SetIsQuadratic( true );
2286 helper.SetIsQuadratic( false );
2288 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289 volTool.GetNodes() + elem->NbNodes() );
2290 helper.SetElementsOnShape( true );
2291 if ( splitMethod._baryNode )
2293 // make a node at barycenter
2294 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296 nodes.push_back( gcNode );
2297 newNodes.Append( gcNode );
2299 if ( !splitMethod._faceBaryNode.empty() )
2301 // make or find baricentric nodes of faces
2302 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2305 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306 volFace2BaryNode.insert
2307 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2313 nodes.push_back( iF_n->second = f_n->second );
2318 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319 const int* volConn = splitMethod._connectivity;
2320 if ( splitMethod._nbCorners == 4 ) // tetra
2321 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323 nodes[ volConn[1] ],
2324 nodes[ volConn[2] ],
2325 nodes[ volConn[3] ]));
2327 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329 nodes[ volConn[1] ],
2330 nodes[ volConn[2] ],
2331 nodes[ volConn[3] ],
2332 nodes[ volConn[4] ],
2333 nodes[ volConn[5] ]));
2335 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2337 // Split faces on sides of the split volume
2339 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2342 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343 if ( nbNodes < 4 ) continue;
2345 // find an existing face
2346 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349 /*noMedium=*/false))
2352 helper.SetElementsOnShape( false );
2353 vector< const SMDS_MeshElement* > triangles;
2355 // find submesh to add new triangles in
2356 if ( !fSubMesh || !fSubMesh->Contains( face ))
2358 int shapeID = FindShape( face );
2359 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2361 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362 if ( iF_n != splitMethod._faceBaryNode.end() )
2364 const SMDS_MeshNode *baryNode = iF_n->second;
2365 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2367 const SMDS_MeshNode* n1 = fNodes[iN];
2368 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369 const SMDS_MeshNode *n3 = baryNode;
2370 if ( !volTool.IsFaceExternal( iF ))
2372 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2374 if ( fSubMesh ) // update position of the bary node on geometry
2377 subMesh->RemoveNode( baryNode, false );
2378 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2382 fHelper.SetSubShape( s );
2383 gp_XY uv( 1e100, 1e100 );
2385 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389 // node is too far from the surface
2390 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2399 // among possible triangles create ones discribed by split method
2400 const int* nInd = volTool.GetFaceNodesIndices( iF );
2401 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402 int iCom = 0; // common node of triangle faces to split into
2403 list< TTriangleFacet > facets;
2404 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2406 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2407 nInd[ iQ * ( (iCom+1)%nbNodes )],
2408 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2410 nInd[ iQ * ( (iCom+2)%nbNodes )],
2411 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2414 facets.push_back( t012 );
2415 facets.push_back( t023 );
2416 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2418 nInd[ iQ * ((iLast-1)%nbNodes )],
2419 nInd[ iQ * ((iLast )%nbNodes )]));
2423 list< TTriangleFacet >::iterator facet = facets.begin();
2424 if ( facet == facets.end() )
2426 for ( ; facet != facets.end(); ++facet )
2428 if ( !volTool.IsFaceExternal( iF ))
2429 swap( facet->_n2, facet->_n3 );
2430 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431 volNodes[ facet->_n2 ],
2432 volNodes[ facet->_n3 ]));
2435 for ( size_t i = 0; i < triangles.size(); ++i )
2437 if ( !triangles[ i ]) continue;
2439 fSubMesh->AddElement( triangles[ i ]);
2440 newElems.Append( triangles[ i ]);
2442 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2445 } // while a face based on facet nodes exists
2446 } // loop on volume faces to split them into triangles
2448 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2450 if ( geomType == SMDSEntity_TriQuad_Hexa )
2452 // remove medium nodes that could become free
2453 for ( int i = 20; i < volTool.NbNodes(); ++i )
2454 if ( volNodes[i]->NbInverseElements() == 0 )
2455 GetMeshDS()->RemoveNode( volNodes[i] );
2457 } // loop on volumes to split
2459 myLastCreatedNodes = newNodes;
2460 myLastCreatedElems = newElems;
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose : For hexahedra that will be split into prisms, finds facets to
2466 // split into triangles. Only hexahedra adjacent to the one closest
2467 // to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in] theFacetNormal - facet normal
2470 //param [out] theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474 const gp_Ax1& theFacetNormal,
2475 TFacetOfElem & theFacets)
2477 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2479 // Find a hexa closest to the location of theFacetNormal
2481 const SMDS_MeshElement* startHex;
2483 // get SMDS_ElemIteratorPtr on theHexas
2484 typedef const SMDS_MeshElement* TValue;
2485 typedef TIDSortedElemSet::iterator TSetIterator;
2486 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2487 typedef SMDS_MeshElement::GeomFilter TFilter;
2488 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490 ( new TElemSetIter( theHexas.begin(),
2492 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2494 SMESH_ElementSearcher* searcher =
2495 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2497 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2502 throw SALOME_Exception( THIS_METHOD "startHex not found");
2505 // Select a facet of startHex by theFacetNormal
2507 SMDS_VolumeTool vTool( startHex );
2508 double norm[3], dot, maxDot = 0;
2510 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2513 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2521 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2523 // Fill theFacets starting from facetID of startHex
2525 // facets used for seach of volumes adjacent to already treated ones
2526 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2528 TFacetMap facetsToCheck;
2530 set<const SMDS_MeshNode*> facetNodes;
2531 const SMDS_MeshElement* curHex;
2533 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2537 // move in two directions from startHex via facetID
2538 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541 int curFacet = facetID;
2542 if ( is2nd ) // do not treat startHex twice
2544 vTool.Set( curHex );
2545 if ( vTool.IsFreeFace( curFacet, &curHex ))
2551 vTool.GetFaceNodes( curFacet, facetNodes );
2552 vTool.Set( curHex );
2553 curFacet = vTool.GetFaceIndex( facetNodes );
2558 // store a facet to split
2559 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2561 theFacets.insert( make_pair( curHex, -1 ));
2564 if ( !allHex && !theHexas.count( curHex ))
2567 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568 theFacets.insert( make_pair( curHex, curFacet ));
2569 if ( !facetIt2isNew.second )
2572 // remember not-to-split facets in facetsToCheck
2573 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2576 if ( iF == curFacet && iF == oppFacet )
2578 TVolumeFaceKey facetKey ( vTool, iF );
2579 TElemFacets elemFacet( facetIt2isNew.first, iF );
2580 pair< TFacetMap::iterator, bool > it2isnew =
2581 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582 if ( !it2isnew.second )
2583 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2585 // pass to a volume adjacent via oppFacet
2586 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2592 // get a new curFacet
2593 vTool.GetFaceNodes( oppFacet, facetNodes );
2594 vTool.Set( curHex );
2595 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598 } // move in two directions from startHex via facetID
2600 // Find a new startHex by facetsToCheck
2604 TFacetMap::iterator fIt = facetsToCheck.begin();
2605 while ( !startHex && fIt != facetsToCheck.end() )
2607 const TElemFacets& elemFacets = fIt->second;
2608 const SMDS_MeshElement* hex = elemFacets.first->first;
2609 int splitFacet = elemFacets.first->second;
2610 int lateralFacet = elemFacets.second;
2611 facetsToCheck.erase( fIt );
2612 fIt = facetsToCheck.begin();
2615 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2616 curHex->GetGeomType() != SMDSGeom_HEXA )
2618 if ( !allHex && !theHexas.count( curHex ))
2623 // find a facet of startHex to split
2625 set<const SMDS_MeshNode*> lateralNodes;
2626 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627 vTool.GetFaceNodes( splitFacet, facetNodes );
2628 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629 vTool.Set( startHex );
2630 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2632 // look for a facet of startHex having common nodes with facetNodes
2633 // but not lateralFacet
2634 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2636 if ( iF == lateralFacet )
2638 int nbCommonNodes = 0;
2639 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641 nbCommonNodes += facetNodes.count( nn[ iN ]);
2643 if ( nbCommonNodes >= 2 )
2650 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2652 } // while ( startHex )
2659 //================================================================================
2661 * \brief Selects nodes of several elements according to a given interlace
2662 * \param [in] srcNodes - nodes to select from
2663 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664 * \param [in] interlace - indices of nodes for all elements
2665 * \param [in] nbElems - nb of elements
2666 * \param [in] nbNodes - nb of nodes in each element
2667 * \param [in] mesh - the mesh
2668 * \param [out] elemQueue - a list to push elements found by the selected nodes
2669 * \param [in] type - type of elements to look for
2671 //================================================================================
2673 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674 vector< const SMDS_MeshNode* >* tgtNodesVec,
2675 const int* interlace,
2678 SMESHDS_Mesh* mesh = 0,
2679 list< const SMDS_MeshElement* >* elemQueue=0,
2680 SMDSAbs_ElementType type=SMDSAbs_All)
2682 for ( int iE = 0; iE < nbElems; ++iE )
2684 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685 const int* select = & interlace[iE*nbNodes];
2686 elemNodes.resize( nbNodes );
2687 for ( int iN = 0; iN < nbNodes; ++iN )
2688 elemNodes[iN] = srcNodes[ select[ iN ]];
2690 const SMDS_MeshElement* e;
2692 for ( int iE = 0; iE < nbElems; ++iE )
2693 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694 elemQueue->push_back( e );
2698 //=======================================================================
2700 * Split bi-quadratic elements into linear ones without creation of additional nodes
2701 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2702 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2705 * will be split in order to keep the mesh conformal.
2706 * \param elems - elements to split
2708 //=======================================================================
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2712 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713 vector<const SMDS_MeshElement* > splitElems;
2714 list< const SMDS_MeshElement* > elemQueue;
2715 list< const SMDS_MeshElement* >::iterator elemIt;
2717 SMESHDS_Mesh * mesh = GetMeshDS();
2718 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719 int nbElems, nbNodes;
2721 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725 elemQueue.push_back( *elemSetIt );
2726 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2728 const SMDS_MeshElement* elem = *elemIt;
2729 switch( elem->GetEntityType() )
2731 case SMDSEntity_TriQuad_Hexa: // HEX27
2733 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 nbElems = nbNodes = 8;
2735 elemType = & hexaType;
2737 // get nodes for new elements
2738 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2739 { 1,9,20,8, 17,22,26,21 },
2740 { 2,10,20,9, 18,23,26,22 },
2741 { 3,11,20,10, 19,24,26,23 },
2742 { 16,21,26,24, 4,12,25,15 },
2743 { 17,22,26,21, 5,13,25,12 },
2744 { 18,23,26,22, 6,14,25,13 },
2745 { 19,24,26,23, 7,15,25,14 }};
2746 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2748 // add boundary faces to elemQueue
2749 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2750 { 4,5,6,7, 12,13,14,15, 25 },
2751 { 0,1,5,4, 8,17,12,16, 21 },
2752 { 1,2,6,5, 9,18,13,17, 22 },
2753 { 2,3,7,6, 10,19,14,18, 23 },
2754 { 3,0,4,7, 11,16,15,19, 24 }};
2755 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2757 // add boundary segments to elemQueue
2758 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764 case SMDSEntity_BiQuad_Triangle: // TRIA7
2766 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769 elemType = & quadType;
2771 // get nodes for new elements
2772 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775 // add boundary segments to elemQueue
2776 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2782 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785 elemType = & quadType;
2787 // get nodes for new elements
2788 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2791 // add boundary segments to elemQueue
2792 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796 case SMDSEntity_Quad_Edge:
2798 if ( elemIt == elemQueue.begin() )
2799 continue; // an elem is in theElems
2800 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803 elemType = & segType;
2805 // get nodes for new elements
2806 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2811 } // switch( elem->GetEntityType() )
2813 // Create new elements
2815 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2819 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822 //elemType->SetID( -1 );
2824 for ( int iE = 0; iE < nbElems; ++iE )
2825 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828 ReplaceElemInGroups( elem, splitElems, mesh );
2831 for ( size_t i = 0; i < splitElems.size(); ++i )
2832 subMesh->AddElement( splitElems[i] );
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843 const SMDS_MeshElement* elemInGroups,
2844 SMESHDS_Mesh * aMesh)
2846 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847 if (!groups.empty()) {
2848 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849 for ( ; grIt != groups.end(); grIt++ ) {
2850 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851 if ( group && group->Contains( elemInGroups ))
2852 group->SMDSGroup().Add( elemToAdd );
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863 SMESHDS_Mesh * aMesh)
2865 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866 if (!groups.empty())
2868 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869 for (; GrIt != groups.end(); GrIt++)
2871 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872 if (!grp || grp->IsEmpty()) continue;
2873 grp->SMDSGroup().Remove(removeelem);
2878 //================================================================================
2880 * \brief Replace elemToRm by elemToAdd in the all groups
2882 //================================================================================
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885 const SMDS_MeshElement* elemToAdd,
2886 SMESHDS_Mesh * aMesh)
2888 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889 if (!groups.empty()) {
2890 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891 for ( ; grIt != groups.end(); grIt++ ) {
2892 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894 group->SMDSGroup().Add( elemToAdd );
2899 //================================================================================
2901 * \brief Replace elemToRm by elemToAdd in the all groups
2903 //================================================================================
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2906 const vector<const SMDS_MeshElement*>& elemToAdd,
2907 SMESHDS_Mesh * aMesh)
2909 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910 if (!groups.empty())
2912 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913 for ( ; grIt != groups.end(); grIt++ ) {
2914 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2917 group->SMDSGroup().Add( elemToAdd[ i ] );
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose : Cut quadrangles into triangles.
2925 // theCrit is used to select a diagonal to cut
2926 //=======================================================================
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929 const bool the13Diag)
2931 myLastCreatedElems.Clear();
2932 myLastCreatedNodes.Clear();
2934 MESSAGE( "::QuadToTri()" );
2936 SMESHDS_Mesh * aMesh = GetMeshDS();
2938 Handle(Geom_Surface) surface;
2939 SMESH_MesherHelper helper( *GetMesh() );
2941 TIDSortedElemSet::iterator itElem;
2942 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2944 const SMDS_MeshElement* elem = *itElem;
2945 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2948 if ( elem->NbNodes() == 4 ) {
2949 // retrieve element nodes
2950 const SMDS_MeshNode* aNodes [4];
2951 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953 while ( itN->more() )
2954 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956 int aShapeId = FindShape( elem );
2957 const SMDS_MeshElement* newElem1 = 0;
2958 const SMDS_MeshElement* newElem2 = 0;
2960 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2964 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967 myLastCreatedElems.Append(newElem1);
2968 myLastCreatedElems.Append(newElem2);
2969 // put a new triangle on the same shape and add to the same groups
2972 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975 AddToSameGroups( newElem1, elem, aMesh );
2976 AddToSameGroups( newElem2, elem, aMesh );
2977 aMesh->RemoveElement( elem );
2980 // Quadratic quadrangle
2982 else if ( elem->NbNodes() >= 8 )
2984 // get surface elem is on
2985 int aShapeId = FindShape( elem );
2986 if ( aShapeId != helper.GetSubShapeID() ) {
2990 shape = aMesh->IndexToShape( aShapeId );
2991 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992 TopoDS_Face face = TopoDS::Face( shape );
2993 surface = BRep_Tool::Surface( face );
2994 if ( !surface.IsNull() )
2995 helper.SetSubShape( shape );
2999 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3000 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001 for ( int i = 0; itN->more(); ++i )
3002 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3004 const SMDS_MeshNode* centrNode = aNodes[8];
3005 if ( centrNode == 0 )
3007 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3008 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3010 myLastCreatedNodes.Append(centrNode);
3013 // create a new element
3014 const SMDS_MeshElement* newElem1 = 0;
3015 const SMDS_MeshElement* newElem2 = 0;
3017 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3018 aNodes[6], aNodes[7], centrNode );
3019 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3020 centrNode, aNodes[4], aNodes[5] );
3023 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3024 aNodes[7], aNodes[4], centrNode );
3025 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3026 centrNode, aNodes[5], aNodes[6] );
3028 myLastCreatedElems.Append(newElem1);
3029 myLastCreatedElems.Append(newElem2);
3030 // put a new triangle on the same shape and add to the same groups
3033 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3034 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3036 AddToSameGroups( newElem1, elem, aMesh );
3037 AddToSameGroups( newElem2, elem, aMesh );
3038 aMesh->RemoveElement( elem );
3045 //=======================================================================
3046 //function : getAngle
3048 //=======================================================================
3050 double getAngle(const SMDS_MeshElement * tr1,
3051 const SMDS_MeshElement * tr2,
3052 const SMDS_MeshNode * n1,
3053 const SMDS_MeshNode * n2)
3055 double angle = 2. * M_PI; // bad angle
3058 SMESH::Controls::TSequenceOfXYZ P1, P2;
3059 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3060 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3063 if(!tr1->IsQuadratic())
3064 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3066 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3067 if ( N1.SquareMagnitude() <= gp::Resolution() )
3069 if(!tr2->IsQuadratic())
3070 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3072 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3073 if ( N2.SquareMagnitude() <= gp::Resolution() )
3076 // find the first diagonal node n1 in the triangles:
3077 // take in account a diagonal link orientation
3078 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3079 for ( int t = 0; t < 2; t++ ) {
3080 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3081 int i = 0, iDiag = -1;
3082 while ( it->more()) {
3083 const SMDS_MeshElement *n = it->next();
3084 if ( n == n1 || n == n2 ) {
3088 if ( i - iDiag == 1 )
3089 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3098 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3101 angle = N1.Angle( N2 );
3106 // =================================================
3107 // class generating a unique ID for a pair of nodes
3108 // and able to return nodes by that ID
3109 // =================================================
3113 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3114 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3117 long GetLinkID (const SMDS_MeshNode * n1,
3118 const SMDS_MeshNode * n2) const
3120 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3123 bool GetNodes (const long theLinkID,
3124 const SMDS_MeshNode* & theNode1,
3125 const SMDS_MeshNode* & theNode2) const
3127 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3128 if ( !theNode1 ) return false;
3129 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3130 if ( !theNode2 ) return false;
3136 const SMESHDS_Mesh* myMesh;
3141 //=======================================================================
3142 //function : TriToQuad
3143 //purpose : Fuse neighbour triangles into quadrangles.
3144 // theCrit is used to select a neighbour to fuse with.
3145 // theMaxAngle is a max angle between element normals at which
3146 // fusion is still performed.
3147 //=======================================================================
3149 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3150 SMESH::Controls::NumericalFunctorPtr theCrit,
3151 const double theMaxAngle)
3153 myLastCreatedElems.Clear();
3154 myLastCreatedNodes.Clear();
3156 MESSAGE( "::TriToQuad()" );
3158 if ( !theCrit.get() )
3161 SMESHDS_Mesh * aMesh = GetMeshDS();
3163 // Prepare data for algo: build
3164 // 1. map of elements with their linkIDs
3165 // 2. map of linkIDs with their elements
3167 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3168 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3169 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3170 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3172 TIDSortedElemSet::iterator itElem;
3173 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3175 const SMDS_MeshElement* elem = *itElem;
3176 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3177 bool IsTria = ( elem->NbCornerNodes()==3 );
3178 if (!IsTria) continue;
3180 // retrieve element nodes
3181 const SMDS_MeshNode* aNodes [4];
3182 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3185 aNodes[ i++ ] = itN->next();
3186 aNodes[ 3 ] = aNodes[ 0 ];
3189 for ( i = 0; i < 3; i++ ) {
3190 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3191 // check if elements sharing a link can be fused
3192 itLE = mapLi_listEl.find( link );
3193 if ( itLE != mapLi_listEl.end() ) {
3194 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3196 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3197 //if ( FindShape( elem ) != FindShape( elem2 ))
3198 // continue; // do not fuse triangles laying on different shapes
3199 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3200 continue; // avoid making badly shaped quads
3201 (*itLE).second.push_back( elem );
3204 mapLi_listEl[ link ].push_back( elem );
3206 mapEl_setLi [ elem ].insert( link );
3209 // Clean the maps from the links shared by a sole element, ie
3210 // links to which only one element is bound in mapLi_listEl
3212 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3213 int nbElems = (*itLE).second.size();
3214 if ( nbElems < 2 ) {
3215 const SMDS_MeshElement* elem = (*itLE).second.front();
3216 SMESH_TLink link = (*itLE).first;
3217 mapEl_setLi[ elem ].erase( link );
3218 if ( mapEl_setLi[ elem ].empty() )
3219 mapEl_setLi.erase( elem );
3223 // Algo: fuse triangles into quadrangles
3225 while ( ! mapEl_setLi.empty() ) {
3226 // Look for the start element:
3227 // the element having the least nb of shared links
3228 const SMDS_MeshElement* startElem = 0;
3230 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3231 int nbLinks = (*itEL).second.size();
3232 if ( nbLinks < minNbLinks ) {
3233 startElem = (*itEL).first;
3234 minNbLinks = nbLinks;
3235 if ( minNbLinks == 1 )
3240 // search elements to fuse starting from startElem or links of elements
3241 // fused earlyer - startLinks
3242 list< SMESH_TLink > startLinks;
3243 while ( startElem || !startLinks.empty() ) {
3244 while ( !startElem && !startLinks.empty() ) {
3245 // Get an element to start, by a link
3246 SMESH_TLink linkId = startLinks.front();
3247 startLinks.pop_front();
3248 itLE = mapLi_listEl.find( linkId );
3249 if ( itLE != mapLi_listEl.end() ) {
3250 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3251 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3252 for ( ; itE != listElem.end() ; itE++ )
3253 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3255 mapLi_listEl.erase( itLE );
3260 // Get candidates to be fused
3261 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3262 const SMESH_TLink *link12, *link13;
3264 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3265 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3266 ASSERT( !setLi.empty() );
3267 set< SMESH_TLink >::iterator itLi;
3268 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3270 const SMESH_TLink & link = (*itLi);
3271 itLE = mapLi_listEl.find( link );
3272 if ( itLE == mapLi_listEl.end() )
3275 const SMDS_MeshElement* elem = (*itLE).second.front();
3277 elem = (*itLE).second.back();
3278 mapLi_listEl.erase( itLE );
3279 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3290 // add other links of elem to list of links to re-start from
3291 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3292 set< SMESH_TLink >::iterator it;
3293 for ( it = links.begin(); it != links.end(); it++ ) {
3294 const SMESH_TLink& link2 = (*it);
3295 if ( link2 != link )
3296 startLinks.push_back( link2 );
3300 // Get nodes of possible quadrangles
3301 const SMDS_MeshNode *n12 [4], *n13 [4];
3302 bool Ok12 = false, Ok13 = false;
3303 const SMDS_MeshNode *linkNode1, *linkNode2;
3305 linkNode1 = link12->first;
3306 linkNode2 = link12->second;
3307 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3311 linkNode1 = link13->first;
3312 linkNode2 = link13->second;
3313 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3317 // Choose a pair to fuse
3318 if ( Ok12 && Ok13 ) {
3319 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3320 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3321 double aBadRate12 = getBadRate( &quad12, theCrit );
3322 double aBadRate13 = getBadRate( &quad13, theCrit );
3323 if ( aBadRate13 < aBadRate12 )
3330 // and remove fused elems and remove links from the maps
3331 mapEl_setLi.erase( tr1 );
3334 mapEl_setLi.erase( tr2 );
3335 mapLi_listEl.erase( *link12 );
3336 if ( tr1->NbNodes() == 3 )
3338 const SMDS_MeshElement* newElem = 0;
3339 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3340 myLastCreatedElems.Append(newElem);
3341 AddToSameGroups( newElem, tr1, aMesh );
3342 int aShapeId = tr1->getshapeId();
3344 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3345 aMesh->RemoveElement( tr1 );
3346 aMesh->RemoveElement( tr2 );
3349 vector< const SMDS_MeshNode* > N1;
3350 vector< const SMDS_MeshNode* > N2;
3351 getNodesFromTwoTria(tr1,tr2,N1,N2);
3352 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3353 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3354 // i.e. first nodes from both arrays form a new diagonal
3355 const SMDS_MeshNode* aNodes[8];
3364 const SMDS_MeshElement* newElem = 0;
3365 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3366 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3367 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3369 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3371 myLastCreatedElems.Append(newElem);
3372 AddToSameGroups( newElem, tr1, aMesh );
3373 int aShapeId = tr1->getshapeId();
3375 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3376 aMesh->RemoveElement( tr1 );
3377 aMesh->RemoveElement( tr2 );
3378 // remove middle node (9)
3379 if ( N1[4]->NbInverseElements() == 0 )
3380 aMesh->RemoveNode( N1[4] );
3381 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3382 aMesh->RemoveNode( N1[6] );
3383 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3384 aMesh->RemoveNode( N2[6] );
3389 mapEl_setLi.erase( tr3 );
3390 mapLi_listEl.erase( *link13 );
3391 if ( tr1->NbNodes() == 3 ) {
3392 const SMDS_MeshElement* newElem = 0;
3393 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3394 myLastCreatedElems.Append(newElem);
3395 AddToSameGroups( newElem, tr1, aMesh );
3396 int aShapeId = tr1->getshapeId();
3398 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3399 aMesh->RemoveElement( tr1 );
3400 aMesh->RemoveElement( tr3 );
3403 vector< const SMDS_MeshNode* > N1;
3404 vector< const SMDS_MeshNode* > N2;
3405 getNodesFromTwoTria(tr1,tr3,N1,N2);
3406 // now we receive following N1 and N2 (using numeration as above image)
3407 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3408 // i.e. first nodes from both arrays form a new diagonal
3409 const SMDS_MeshNode* aNodes[8];
3418 const SMDS_MeshElement* newElem = 0;
3419 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3420 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3421 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3423 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3425 myLastCreatedElems.Append(newElem);
3426 AddToSameGroups( newElem, tr1, aMesh );
3427 int aShapeId = tr1->getshapeId();
3429 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3430 aMesh->RemoveElement( tr1 );
3431 aMesh->RemoveElement( tr3 );
3432 // remove middle node (9)
3433 if ( N1[4]->NbInverseElements() == 0 )
3434 aMesh->RemoveNode( N1[4] );
3435 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3436 aMesh->RemoveNode( N1[6] );
3437 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3438 aMesh->RemoveNode( N2[6] );
3442 // Next element to fuse: the rejected one
3444 startElem = Ok12 ? tr3 : tr2;
3446 } // if ( startElem )
3447 } // while ( startElem || !startLinks.empty() )
3448 } // while ( ! mapEl_setLi.empty() )
3454 /*#define DUMPSO(txt) \
3455 // cout << txt << endl;
3456 //=============================================================================
3460 //=============================================================================
3461 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3465 int tmp = idNodes[ i1 ];
3466 idNodes[ i1 ] = idNodes[ i2 ];
3467 idNodes[ i2 ] = tmp;
3468 gp_Pnt Ptmp = P[ i1 ];
3471 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3474 //=======================================================================
3475 //function : SortQuadNodes
3476 //purpose : Set 4 nodes of a quadrangle face in a good order.
3477 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3479 //=======================================================================
3481 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3486 for ( i = 0; i < 4; i++ ) {
3487 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3489 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3492 gp_Vec V1(P[0], P[1]);
3493 gp_Vec V2(P[0], P[2]);
3494 gp_Vec V3(P[0], P[3]);
3496 gp_Vec Cross1 = V1 ^ V2;
3497 gp_Vec Cross2 = V2 ^ V3;
3500 if (Cross1.Dot(Cross2) < 0)
3505 if (Cross1.Dot(Cross2) < 0)
3509 swap ( i, i + 1, idNodes, P );
3511 // for ( int ii = 0; ii < 4; ii++ ) {
3512 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3513 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3519 //=======================================================================
3520 //function : SortHexaNodes
3521 //purpose : Set 8 nodes of a hexahedron in a good order.
3522 // Return success status
3523 //=======================================================================
3525 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3530 DUMPSO( "INPUT: ========================================");
3531 for ( i = 0; i < 8; i++ ) {
3532 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3533 if ( !n ) return false;
3534 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3535 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3537 DUMPSO( "========================================");
3540 set<int> faceNodes; // ids of bottom face nodes, to be found
3541 set<int> checkedId1; // ids of tried 2-nd nodes
3542 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3543 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3544 int iMin, iLoop1 = 0;
3546 // Loop to try the 2-nd nodes
3548 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3550 // Find not checked 2-nd node
3551 for ( i = 1; i < 8; i++ )
3552 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3553 int id1 = idNodes[i];
3554 swap ( 1, i, idNodes, P );
3555 checkedId1.insert ( id1 );
3559 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3560 // ie that all but meybe one (id3 which is on the same face) nodes
3561 // lay on the same side from the triangle plane.
3563 bool manyInPlane = false; // more than 4 nodes lay in plane
3565 while ( ++iLoop2 < 6 ) {
3567 // get 1-2-3 plane coeffs
3568 Standard_Real A, B, C, D;
3569 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3570 if ( N.SquareMagnitude() > gp::Resolution() )
3572 gp_Pln pln ( P[0], N );
3573 pln.Coefficients( A, B, C, D );
3575 // find the node (iMin) closest to pln
3576 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3578 for ( i = 3; i < 8; i++ ) {
3579 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3580 if ( fabs( dist[i] ) < minDist ) {
3581 minDist = fabs( dist[i] );
3584 if ( fabs( dist[i] ) <= tol )
3585 idInPln.insert( idNodes[i] );
3588 // there should not be more than 4 nodes in bottom plane
3589 if ( idInPln.size() > 1 )
3591 DUMPSO( "### idInPln.size() = " << idInPln.size());
3592 // idInPlane does not contain the first 3 nodes
3593 if ( manyInPlane || idInPln.size() == 5)
3594 return false; // all nodes in one plane
3597 // set the 1-st node to be not in plane
3598 for ( i = 3; i < 8; i++ ) {
3599 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3600 DUMPSO( "### Reset 0-th node");
3601 swap( 0, i, idNodes, P );
3606 // reset to re-check second nodes
3607 leastDist = DBL_MAX;
3611 break; // from iLoop2;
3614 // check that the other 4 nodes are on the same side
3615 bool sameSide = true;
3616 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3617 for ( i = 3; sameSide && i < 8; i++ ) {
3619 sameSide = ( isNeg == dist[i] <= 0.);
3622 // keep best solution
3623 if ( sameSide && minDist < leastDist ) {
3624 leastDist = minDist;
3626 faceNodes.insert( idNodes[ 1 ] );
3627 faceNodes.insert( idNodes[ 2 ] );
3628 faceNodes.insert( idNodes[ iMin ] );
3629 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3630 << " leastDist = " << leastDist);
3631 if ( leastDist <= DBL_MIN )
3636 // set next 3-d node to check
3637 int iNext = 2 + iLoop2;
3639 DUMPSO( "Try 2-nd");
3640 swap ( 2, iNext, idNodes, P );
3642 } // while ( iLoop2 < 6 )
3645 if ( faceNodes.empty() ) return false;
3647 // Put the faceNodes in proper places
3648 for ( i = 4; i < 8; i++ ) {
3649 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3650 // find a place to put
3652 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3654 DUMPSO( "Set faceNodes");
3655 swap ( iTo, i, idNodes, P );
3660 // Set nodes of the found bottom face in good order
3661 DUMPSO( " Found bottom face: ");
3662 i = SortQuadNodes( theMesh, idNodes );
3664 gp_Pnt Ptmp = P[ i ];
3669 // for ( int ii = 0; ii < 4; ii++ ) {
3670 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3671 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3674 // Gravity center of the top and bottom faces
3675 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3676 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3678 // Get direction from the bottom to the top face
3679 gp_Vec upDir ( aGCb, aGCt );
3680 Standard_Real upDirSize = upDir.Magnitude();
3681 if ( upDirSize <= gp::Resolution() ) return false;
3684 // Assure that the bottom face normal points up
3685 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3686 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3687 if ( Nb.Dot( upDir ) < 0 ) {
3688 DUMPSO( "Reverse bottom face");
3689 swap( 1, 3, idNodes, P );
3692 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3693 Standard_Real minDist = DBL_MAX;
3694 for ( i = 4; i < 8; i++ ) {
3695 // projection of P[i] to the plane defined by P[0] and upDir
3696 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3697 Standard_Real sqDist = P[0].SquareDistance( Pp );
3698 if ( sqDist < minDist ) {
3703 DUMPSO( "Set 4-th");
3704 swap ( 4, iMin, idNodes, P );
3706 // Set nodes of the top face in good order
3707 DUMPSO( "Sort top face");
3708 i = SortQuadNodes( theMesh, &idNodes[4] );
3711 gp_Pnt Ptmp = P[ i ];
3716 // Assure that direction of the top face normal is from the bottom face
3717 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3718 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3719 if ( Nt.Dot( upDir ) < 0 ) {
3720 DUMPSO( "Reverse top face");
3721 swap( 5, 7, idNodes, P );
3724 // DUMPSO( "OUTPUT: ========================================");
3725 // for ( i = 0; i < 8; i++ ) {
3726 // float *p = ugrid->GetPoint(idNodes[i]);
3727 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3733 //================================================================================
3735 * \brief Return nodes linked to the given one
3736 * \param theNode - the node
3737 * \param linkedNodes - the found nodes
3738 * \param type - the type of elements to check
3740 * Medium nodes are ignored
3742 //================================================================================
3744 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3745 TIDSortedElemSet & linkedNodes,
3746 SMDSAbs_ElementType type )
3748 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3749 while ( elemIt->more() )
3751 const SMDS_MeshElement* elem = elemIt->next();
3752 if(elem->GetType() == SMDSAbs_0DElement)
3755 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3756 if ( elem->GetType() == SMDSAbs_Volume )
3758 SMDS_VolumeTool vol( elem );
3759 while ( nodeIt->more() ) {
3760 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3761 if ( theNode != n && vol.IsLinked( theNode, n ))
3762 linkedNodes.insert( n );
3767 for ( int i = 0; nodeIt->more(); ++i ) {
3768 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3769 if ( n == theNode ) {
3770 int iBefore = i - 1;
3772 if ( elem->IsQuadratic() ) {
3773 int nb = elem->NbNodes() / 2;
3774 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3775 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3777 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3778 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3785 //=======================================================================
3786 //function : laplacianSmooth
3787 //purpose : pulls theNode toward the center of surrounding nodes directly
3788 // connected to that node along an element edge
3789 //=======================================================================
3791 void laplacianSmooth(const SMDS_MeshNode* theNode,
3792 const Handle(Geom_Surface)& theSurface,
3793 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3795 // find surrounding nodes
3797 TIDSortedElemSet nodeSet;
3798 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3800 // compute new coodrs
3802 double coord[] = { 0., 0., 0. };
3803 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3804 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3805 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3806 if ( theSurface.IsNull() ) { // smooth in 3D
3807 coord[0] += node->X();
3808 coord[1] += node->Y();
3809 coord[2] += node->Z();
3811 else { // smooth in 2D
3812 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3813 gp_XY* uv = theUVMap[ node ];
3814 coord[0] += uv->X();
3815 coord[1] += uv->Y();
3818 int nbNodes = nodeSet.size();
3821 coord[0] /= nbNodes;
3822 coord[1] /= nbNodes;
3824 if ( !theSurface.IsNull() ) {
3825 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3826 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3827 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3833 coord[2] /= nbNodes;
3837 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3840 //=======================================================================
3841 //function : centroidalSmooth
3842 //purpose : pulls theNode toward the element-area-weighted centroid of the
3843 // surrounding elements
3844 //=======================================================================
3846 void centroidalSmooth(const SMDS_MeshNode* theNode,
3847 const Handle(Geom_Surface)& theSurface,
3848 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3850 gp_XYZ aNewXYZ(0.,0.,0.);
3851 SMESH::Controls::Area anAreaFunc;
3852 double totalArea = 0.;
3857 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3858 while ( elemIt->more() )
3860 const SMDS_MeshElement* elem = elemIt->next();
3863 gp_XYZ elemCenter(0.,0.,0.);
3864 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3865 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3866 int nn = elem->NbNodes();
3867 if(elem->IsQuadratic()) nn = nn/2;
3869 //while ( itN->more() ) {
3871 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3873 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3874 aNodePoints.push_back( aP );
3875 if ( !theSurface.IsNull() ) { // smooth in 2D
3876 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3877 gp_XY* uv = theUVMap[ aNode ];
3878 aP.SetCoord( uv->X(), uv->Y(), 0. );
3882 double elemArea = anAreaFunc.GetValue( aNodePoints );
3883 totalArea += elemArea;
3885 aNewXYZ += elemCenter * elemArea;
3887 aNewXYZ /= totalArea;
3888 if ( !theSurface.IsNull() ) {
3889 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3890 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3895 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3898 //=======================================================================
3899 //function : getClosestUV
3900 //purpose : return UV of closest projection
3901 //=======================================================================
3903 static bool getClosestUV (Extrema_GenExtPS& projector,
3904 const gp_Pnt& point,
3907 projector.Perform( point );
3908 if ( projector.IsDone() ) {
3909 double u, v, minVal = DBL_MAX;
3910 for ( int i = projector.NbExt(); i > 0; i-- )
3911 if ( projector.SquareDistance( i ) < minVal ) {
3912 minVal = projector.SquareDistance( i );
3913 projector.Point( i ).Parameter( u, v );
3915 result.SetCoord( u, v );
3921 //=======================================================================
3923 //purpose : Smooth theElements during theNbIterations or until a worst
3924 // element has aspect ratio <= theTgtAspectRatio.
3925 // Aspect Ratio varies in range [1.0, inf].
3926 // If theElements is empty, the whole mesh is smoothed.
3927 // theFixedNodes contains additionally fixed nodes. Nodes built
3928 // on edges and boundary nodes are always fixed.
3929 //=======================================================================
3931 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3932 set<const SMDS_MeshNode*> & theFixedNodes,
3933 const SmoothMethod theSmoothMethod,
3934 const int theNbIterations,
3935 double theTgtAspectRatio,
3938 myLastCreatedElems.Clear();
3939 myLastCreatedNodes.Clear();
3941 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3943 if ( theTgtAspectRatio < 1.0 )
3944 theTgtAspectRatio = 1.0;
3946 const double disttol = 1.e-16;
3948 SMESH::Controls::AspectRatio aQualityFunc;
3950 SMESHDS_Mesh* aMesh = GetMeshDS();
3952 if ( theElems.empty() ) {
3953 // add all faces to theElems
3954 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3955 while ( fIt->more() ) {
3956 const SMDS_MeshElement* face = fIt->next();
3957 theElems.insert( theElems.end(), face );
3960 // get all face ids theElems are on
3961 set< int > faceIdSet;
3962 TIDSortedElemSet::iterator itElem;
3964 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3965 int fId = FindShape( *itElem );
3966 // check that corresponding submesh exists and a shape is face
3968 faceIdSet.find( fId ) == faceIdSet.end() &&
3969 aMesh->MeshElements( fId )) {
3970 TopoDS_Shape F = aMesh->IndexToShape( fId );
3971 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3972 faceIdSet.insert( fId );
3975 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3977 // ===============================================
3978 // smooth elements on each TopoDS_Face separately
3979 // ===============================================
3981 SMESH_MesherHelper helper( *GetMesh() );
3983 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3984 for ( ; fId != faceIdSet.rend(); ++fId )
3986 // get face surface and submesh
3987 Handle(Geom_Surface) surface;
3988 SMESHDS_SubMesh* faceSubMesh = 0;
3991 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3992 bool isUPeriodic = false, isVPeriodic = false;
3995 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3996 surface = BRep_Tool::Surface( face );
3997 faceSubMesh = aMesh->MeshElements( *fId );
3998 fToler2 = BRep_Tool::Tolerance( face );
3999 fToler2 *= fToler2 * 10.;
4000 isUPeriodic = surface->IsUPeriodic();
4003 isVPeriodic = surface->IsVPeriodic();
4006 surface->Bounds( u1, u2, v1, v2 );
4007 helper.SetSubShape( face );
4009 // ---------------------------------------------------------
4010 // for elements on a face, find movable and fixed nodes and
4011 // compute UV for them
4012 // ---------------------------------------------------------
4013 bool checkBoundaryNodes = false;
4014 bool isQuadratic = false;
4015 set<const SMDS_MeshNode*> setMovableNodes;
4016 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4017 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4018 list< const SMDS_MeshElement* > elemsOnFace;
4020 Extrema_GenExtPS projector;
4021 GeomAdaptor_Surface surfAdaptor;
4022 if ( !surface.IsNull() ) {
4023 surfAdaptor.Load( surface );
4024 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4026 int nbElemOnFace = 0;
4027 itElem = theElems.begin();
4028 // loop on not yet smoothed elements: look for elems on a face
4029 while ( itElem != theElems.end() )
4031 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4032 break; // all elements found
4034 const SMDS_MeshElement* elem = *itElem;
4035 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4036 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4040 elemsOnFace.push_back( elem );
4041 theElems.erase( itElem++ );
4045 isQuadratic = elem->IsQuadratic();
4047 // get movable nodes of elem
4048 const SMDS_MeshNode* node;
4049 SMDS_TypeOfPosition posType;
4050 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4051 int nn = 0, nbn = elem->NbNodes();
4052 if(elem->IsQuadratic())
4054 while ( nn++ < nbn ) {
4055 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4056 const SMDS_PositionPtr& pos = node->GetPosition();
4057 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4058 if (posType != SMDS_TOP_EDGE &&
4059 posType != SMDS_TOP_VERTEX &&
4060 theFixedNodes.find( node ) == theFixedNodes.end())
4062 // check if all faces around the node are on faceSubMesh
4063 // because a node on edge may be bound to face
4064 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4066 if ( faceSubMesh ) {
4067 while ( eIt->more() && all ) {
4068 const SMDS_MeshElement* e = eIt->next();
4069 all = faceSubMesh->Contains( e );
4073 setMovableNodes.insert( node );
4075 checkBoundaryNodes = true;
4077 if ( posType == SMDS_TOP_3DSPACE )
4078 checkBoundaryNodes = true;
4081 if ( surface.IsNull() )
4084 // get nodes to check UV
4085 list< const SMDS_MeshNode* > uvCheckNodes;
4086 const SMDS_MeshNode* nodeInFace = 0;
4087 itN = elem->nodesIterator();
4088 nn = 0; nbn = elem->NbNodes();
4089 if(elem->IsQuadratic())
4091 while ( nn++ < nbn ) {
4092 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4093 if ( node->GetPosition()->GetDim() == 2 )
4095 if ( uvMap.find( node ) == uvMap.end() )
4096 uvCheckNodes.push_back( node );
4097 // add nodes of elems sharing node
4098 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4099 // while ( eIt->more() ) {
4100 // const SMDS_MeshElement* e = eIt->next();
4101 // if ( e != elem ) {
4102 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4103 // while ( nIt->more() ) {
4104 // const SMDS_MeshNode* n =
4105 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4106 // if ( uvMap.find( n ) == uvMap.end() )
4107 // uvCheckNodes.push_back( n );
4113 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4114 for ( ; n != uvCheckNodes.end(); ++n ) {
4117 const SMDS_PositionPtr& pos = node->GetPosition();
4118 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4122 bool toCheck = true;
4123 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4125 // compute not existing UV
4126 bool project = ( posType == SMDS_TOP_3DSPACE );
4127 // double dist1 = DBL_MAX, dist2 = 0;
4128 // if ( posType != SMDS_TOP_3DSPACE ) {
4129 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4130 // project = dist1 > fToler2;
4132 if ( project ) { // compute new UV
4134 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4135 if ( !getClosestUV( projector, pNode, newUV )) {
4136 MESSAGE("Node Projection Failed " << node);
4140 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4142 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4144 // if ( posType != SMDS_TOP_3DSPACE )
4145 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4146 // if ( dist2 < dist1 )
4150 // store UV in the map
4151 listUV.push_back( uv );
4152 uvMap.insert( make_pair( node, &listUV.back() ));
4154 } // loop on not yet smoothed elements
4156 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4157 checkBoundaryNodes = true;
4159 // fix nodes on mesh boundary
4161 if ( checkBoundaryNodes ) {
4162 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4163 map< SMESH_TLink, int >::iterator link_nb;
4164 // put all elements links to linkNbMap
4165 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4166 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4167 const SMDS_MeshElement* elem = (*elemIt);
4168 int nbn = elem->NbCornerNodes();
4169 // loop on elem links: insert them in linkNbMap
4170 for ( int iN = 0; iN < nbn; ++iN ) {
4171 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4172 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4173 SMESH_TLink link( n1, n2 );
4174 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4178 // remove nodes that are in links encountered only once from setMovableNodes
4179 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4180 if ( link_nb->second == 1 ) {
4181 setMovableNodes.erase( link_nb->first.node1() );
4182 setMovableNodes.erase( link_nb->first.node2() );
4187 // -----------------------------------------------------
4188 // for nodes on seam edge, compute one more UV ( uvMap2 );
4189 // find movable nodes linked to nodes on seam and which
4190 // are to be smoothed using the second UV ( uvMap2 )
4191 // -----------------------------------------------------
4193 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4194 if ( !surface.IsNull() ) {
4195 TopExp_Explorer eExp( face, TopAbs_EDGE );
4196 for ( ; eExp.More(); eExp.Next() ) {
4197 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4198 if ( !BRep_Tool::IsClosed( edge, face ))
4200 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4201 if ( !sm ) continue;
4202 // find out which parameter varies for a node on seam
4205 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206 if ( pcurve.IsNull() ) continue;
4207 uv1 = pcurve->Value( f );
4209 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4210 if ( pcurve.IsNull() ) continue;
4211 uv2 = pcurve->Value( f );
4212 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4214 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4215 std::swap( uv1, uv2 );
4216 // get nodes on seam and its vertices
4217 list< const SMDS_MeshNode* > seamNodes;
4218 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4219 while ( nSeamIt->more() ) {
4220 const SMDS_MeshNode* node = nSeamIt->next();
4221 if ( !isQuadratic || !IsMedium( node ))
4222 seamNodes.push_back( node );
4224 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4225 for ( ; vExp.More(); vExp.Next() ) {
4226 sm = aMesh->MeshElements( vExp.Current() );
4228 nSeamIt = sm->GetNodes();
4229 while ( nSeamIt->more() )
4230 seamNodes.push_back( nSeamIt->next() );
4233 // loop on nodes on seam
4234 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4235 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4236 const SMDS_MeshNode* nSeam = *noSeIt;
4237 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4238 if ( n_uv == uvMap.end() )
4241 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4242 // set the second UV
4243 listUV.push_back( *n_uv->second );
4244 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4245 if ( uvMap2.empty() )
4246 uvMap2 = uvMap; // copy the uvMap contents
4247 uvMap2[ nSeam ] = &listUV.back();
4249 // collect movable nodes linked to ones on seam in nodesNearSeam
4250 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4251 while ( eIt->more() ) {
4252 const SMDS_MeshElement* e = eIt->next();
4253 int nbUseMap1 = 0, nbUseMap2 = 0;
4254 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4255 int nn = 0, nbn = e->NbNodes();
4256 if(e->IsQuadratic()) nbn = nbn/2;
4257 while ( nn++ < nbn )
4259 const SMDS_MeshNode* n =
4260 static_cast<const SMDS_MeshNode*>( nIt->next() );
4262 setMovableNodes.find( n ) == setMovableNodes.end() )
4264 // add only nodes being closer to uv2 than to uv1
4265 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4266 // 0.5 * ( n->Y() + nSeam->Y() ),
4267 // 0.5 * ( n->Z() + nSeam->Z() ));
4269 // getClosestUV( projector, pMid, uv );
4270 double x = uvMap[ n ]->Coord( iPar );
4271 if ( Abs( uv1.Coord( iPar ) - x ) >
4272 Abs( uv2.Coord( iPar ) - x )) {
4273 nodesNearSeam.insert( n );
4279 // for centroidalSmooth all element nodes must
4280 // be on one side of a seam
4281 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4282 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4284 while ( nn++ < nbn ) {
4285 const SMDS_MeshNode* n =
4286 static_cast<const SMDS_MeshNode*>( nIt->next() );
4287 setMovableNodes.erase( n );
4291 } // loop on nodes on seam
4292 } // loop on edge of a face
4293 } // if ( !face.IsNull() )
4295 if ( setMovableNodes.empty() ) {
4296 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4297 continue; // goto next face
4305 double maxRatio = -1., maxDisplacement = -1.;
4306 set<const SMDS_MeshNode*>::iterator nodeToMove;
4307 for ( it = 0; it < theNbIterations; it++ ) {
4308 maxDisplacement = 0.;
4309 nodeToMove = setMovableNodes.begin();
4310 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4311 const SMDS_MeshNode* node = (*nodeToMove);
4312 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4315 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4316 if ( theSmoothMethod == LAPLACIAN )
4317 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4319 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4321 // node displacement
4322 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4323 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4324 if ( aDispl > maxDisplacement )
4325 maxDisplacement = aDispl;
4327 // no node movement => exit
4328 //if ( maxDisplacement < 1.e-16 ) {
4329 if ( maxDisplacement < disttol ) {
4330 MESSAGE("-- no node movement --");
4334 // check elements quality
4336 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4337 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4338 const SMDS_MeshElement* elem = (*elemIt);
4339 if ( !elem || elem->GetType() != SMDSAbs_Face )
4341 SMESH::Controls::TSequenceOfXYZ aPoints;
4342 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4343 double aValue = aQualityFunc.GetValue( aPoints );
4344 if ( aValue > maxRatio )
4348 if ( maxRatio <= theTgtAspectRatio ) {
4349 MESSAGE("-- quality achived --");
4352 if (it+1 == theNbIterations) {
4353 MESSAGE("-- Iteration limit exceeded --");
4355 } // smoothing iterations
4357 MESSAGE(" Face id: " << *fId <<
4358 " Nb iterstions: " << it <<
4359 " Displacement: " << maxDisplacement <<
4360 " Aspect Ratio " << maxRatio);
4362 // ---------------------------------------
4363 // new nodes positions are computed,
4364 // record movement in DS and set new UV
4365 // ---------------------------------------
4366 nodeToMove = setMovableNodes.begin();
4367 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4368 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4369 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4370 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4371 if ( node_uv != uvMap.end() ) {
4372 gp_XY* uv = node_uv->second;
4374 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4378 // move medium nodes of quadratic elements
4381 vector<const SMDS_MeshNode*> nodes;
4383 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4384 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4386 const SMDS_MeshElement* QF = *elemIt;
4387 if ( QF->IsQuadratic() )
4389 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4390 SMDS_MeshElement::iterator() );
4391 nodes.push_back( nodes[0] );
4393 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4395 if ( !surface.IsNull() )
4397 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4398 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4399 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4400 xyz = surface->Value( uv.X(), uv.Y() );
4403 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4405 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4406 // we have to move a medium node
4407 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4413 } // loop on face ids
4419 //=======================================================================
4420 //function : isReverse
4421 //purpose : Return true if normal of prevNodes is not co-directied with
4422 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4423 // iNotSame is where prevNodes and nextNodes are different.
4424 // If result is true then future volume orientation is OK
4425 //=======================================================================
4427 bool isReverse(const SMDS_MeshElement* face,
4428 const vector<const SMDS_MeshNode*>& prevNodes,
4429 const vector<const SMDS_MeshNode*>& nextNodes,
4433 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4434 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4435 gp_XYZ extrDir( pN - pP ), faceNorm;
4436 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4438 return faceNorm * extrDir < 0.0;
4441 //================================================================================
4443 * \brief Assure that theElemSets[0] holds elements, not nodes
4445 //================================================================================
4447 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4449 if ( !theElemSets[0].empty() &&
4450 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4452 std::swap( theElemSets[0], theElemSets[1] );
4454 else if ( !theElemSets[1].empty() &&
4455 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4457 std::swap( theElemSets[0], theElemSets[1] );
4462 //=======================================================================
4464 * \brief Create elements by sweeping an element
4465 * \param elem - element to sweep
4466 * \param newNodesItVec - nodes generated from each node of the element
4467 * \param newElems - generated elements
4468 * \param nbSteps - number of sweeping steps
4469 * \param srcElements - to append elem for each generated element
4471 //=======================================================================
4473 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4474 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4475 list<const SMDS_MeshElement*>& newElems,
4476 const size_t nbSteps,
4477 SMESH_SequenceOfElemPtr& srcElements)
4479 //MESSAGE("sweepElement " << nbSteps);
4480 SMESHDS_Mesh* aMesh = GetMeshDS();
4482 const int nbNodes = elem->NbNodes();
4483 const int nbCorners = elem->NbCornerNodes();
4484 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4485 polyhedron creation !!! */
4486 // Loop on elem nodes:
4487 // find new nodes and detect same nodes indices
4488 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4489 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4490 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4491 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4493 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4494 vector<int> sames(nbNodes);
4495 vector<bool> isSingleNode(nbNodes);
4497 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4498 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4499 const SMDS_MeshNode* node = nnIt->first;
4500 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4501 if ( listNewNodes.empty() )
4504 itNN [ iNode ] = listNewNodes.begin();
4505 prevNod[ iNode ] = node;
4506 nextNod[ iNode ] = listNewNodes.front();
4508 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4509 corner node of linear */
4510 if ( prevNod[ iNode ] != nextNod [ iNode ])
4511 nbDouble += !isSingleNode[iNode];
4513 if( iNode < nbCorners ) { // check corners only
4514 if ( prevNod[ iNode ] == nextNod [ iNode ])
4515 sames[nbSame++] = iNode;
4517 iNotSameNode = iNode;
4521 if ( nbSame == nbNodes || nbSame > 2) {
4522 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4526 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4528 // fix nodes order to have bottom normal external
4529 if ( baseType == SMDSEntity_Polygon )
4531 std::reverse( itNN.begin(), itNN.end() );
4532 std::reverse( prevNod.begin(), prevNod.end() );
4533 std::reverse( midlNod.begin(), midlNod.end() );
4534 std::reverse( nextNod.begin(), nextNod.end() );
4535 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4539 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4540 SMDS_MeshCell::applyInterlace( ind, itNN );
4541 SMDS_MeshCell::applyInterlace( ind, prevNod );
4542 SMDS_MeshCell::applyInterlace( ind, nextNod );
4543 SMDS_MeshCell::applyInterlace( ind, midlNod );
4544 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4547 sames[nbSame] = iNotSameNode;
4548 for ( int j = 0; j <= nbSame; ++j )
4549 for ( size_t i = 0; i < ind.size(); ++i )
4550 if ( ind[i] == sames[j] )
4555 iNotSameNode = sames[nbSame];
4559 else if ( elem->GetType() == SMDSAbs_Edge )
4561 // orient a new face same as adjacent one
4563 const SMDS_MeshElement* e;
4564 TIDSortedElemSet dummy;
4565 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4566 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4567 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4569 // there is an adjacent face, check order of nodes in it
4570 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4573 std::swap( itNN[0], itNN[1] );
4574 std::swap( prevNod[0], prevNod[1] );
4575 std::swap( nextNod[0], nextNod[1] );
4576 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4578 sames[0] = 1 - sames[0];
4579 iNotSameNode = 1 - iNotSameNode;
4584 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4586 iSameNode = sames[ nbSame-1 ];
4587 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4588 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4589 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4592 if ( baseType == SMDSEntity_Polygon )
4594 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4595 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4597 else if ( baseType == SMDSEntity_Quad_Polygon )
4599 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4600 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4603 // make new elements
4604 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4607 for ( iNode = 0; iNode < nbNodes; iNode++ )
4609 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4610 nextNod[ iNode ] = *itNN[ iNode ]++;
4613 SMDS_MeshElement* aNewElem = 0;
4614 /*if(!elem->IsPoly())*/ {
4615 switch ( baseType ) {
4617 case SMDSEntity_Node: { // sweep NODE
4618 if ( nbSame == 0 ) {
4619 if ( isSingleNode[0] )
4620 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4622 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4628 case SMDSEntity_Edge: { // sweep EDGE
4629 if ( nbDouble == 0 )
4631 if ( nbSame == 0 ) // ---> quadrangle
4632 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4633 nextNod[ 1 ], nextNod[ 0 ] );
4634 else // ---> triangle
4635 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4636 nextNod[ iNotSameNode ] );
4638 else // ---> polygon
4640 vector<const SMDS_MeshNode*> poly_nodes;
4641 poly_nodes.push_back( prevNod[0] );
4642 poly_nodes.push_back( prevNod[1] );
4643 if ( prevNod[1] != nextNod[1] )
4645 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4646 poly_nodes.push_back( nextNod[1] );
4648 if ( prevNod[0] != nextNod[0] )
4650 poly_nodes.push_back( nextNod[0] );
4651 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4653 switch ( poly_nodes.size() ) {
4655 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4658 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4659 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4662 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4667 case SMDSEntity_Triangle: // TRIANGLE --->
4669 if ( nbDouble > 0 ) break;
4670 if ( nbSame == 0 ) // ---> pentahedron
4671 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4672 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4674 else if ( nbSame == 1 ) // ---> pyramid
4675 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4676 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4677 nextNod[ iSameNode ]);
4679 else // 2 same nodes: ---> tetrahedron
4680 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4681 nextNod[ iNotSameNode ]);
4684 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4688 if ( nbDouble+nbSame == 2 )
4690 if(nbSame==0) { // ---> quadratic quadrangle
4691 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4692 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4694 else { //(nbSame==1) // ---> quadratic triangle
4696 return; // medium node on axis
4698 else if(sames[0]==0)
4699 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4700 prevNod[2], midlNod[1], nextNod[2] );
4702 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4703 prevNod[2], nextNod[2], midlNod[0]);
4706 else if ( nbDouble == 3 )
4708 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4709 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4717 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4718 if ( nbDouble > 0 ) break;
4720 if ( nbSame == 0 ) // ---> hexahedron
4721 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4722 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4724 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4725 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4726 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4727 nextNod[ iSameNode ]);
4728 newElems.push_back( aNewElem );
4729 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4730 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4731 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4733 else if ( nbSame == 2 ) { // ---> pentahedron
4734 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4735 // iBeforeSame is same too
4736 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4737 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4738 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4740 // iAfterSame is same too
4741 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4742 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4743 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4747 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4748 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4749 if ( nbDouble+nbSame != 3 ) break;
4751 // ---> pentahedron with 15 nodes
4752 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4753 nextNod[0], nextNod[1], nextNod[2],
4754 prevNod[3], prevNod[4], prevNod[5],
4755 nextNod[3], nextNod[4], nextNod[5],
4756 midlNod[0], midlNod[1], midlNod[2]);
4758 else if(nbSame==1) {
4759 // ---> 2d order pyramid of 13 nodes
4760 int apex = iSameNode;
4761 int i0 = ( apex + 1 ) % nbCorners;
4762 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4766 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4767 nextNod[i0], nextNod[i1], prevNod[apex],
4768 prevNod[i01], midlNod[i0],
4769 nextNod[i01], midlNod[i1],
4770 prevNod[i1a], prevNod[i0a],
4771 nextNod[i0a], nextNod[i1a]);
4773 else if(nbSame==2) {
4774 // ---> 2d order tetrahedron of 10 nodes
4775 int n1 = iNotSameNode;
4776 int n2 = ( n1 + 1 ) % nbCorners;
4777 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4781 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4782 prevNod[n12], prevNod[n23], prevNod[n31],
4783 midlNod[n1], nextNod[n12], nextNod[n31]);
4787 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4789 if ( nbDouble != 4 ) break;
4790 // ---> hexahedron with 20 nodes
4791 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4792 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4793 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4794 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4795 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4797 else if(nbSame==1) {
4798 // ---> pyramid + pentahedron - can not be created since it is needed
4799 // additional middle node at the center of face
4800 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4803 else if( nbSame == 2 ) {
4804 if ( nbDouble != 2 ) break;
4805 // ---> 2d order Pentahedron with 15 nodes
4807 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4808 // iBeforeSame is same too
4815 // iAfterSame is same too
4825 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4826 prevNod[n4], prevNod[n5], nextNod[n5],
4827 prevNod[n12], midlNod[n2], nextNod[n12],
4828 prevNod[n45], midlNod[n5], nextNod[n45],
4829 prevNod[n14], prevNod[n25], nextNod[n25]);
4833 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4835 if( nbSame == 0 && nbDouble == 9 ) {
4836 // ---> tri-quadratic hexahedron with 27 nodes
4837 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4838 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4839 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4840 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4841 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4842 prevNod[8], // bottom center
4843 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4844 nextNod[8], // top center
4845 midlNod[8]);// elem center
4853 case SMDSEntity_Polygon: { // sweep POLYGON
4855 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4856 // ---> hexagonal prism
4857 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4858 prevNod[3], prevNod[4], prevNod[5],
4859 nextNod[0], nextNod[1], nextNod[2],
4860 nextNod[3], nextNod[4], nextNod[5]);
4864 case SMDSEntity_Ball:
4869 } // switch ( baseType )
4872 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4874 if ( baseType != SMDSEntity_Polygon )
4876 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4877 SMDS_MeshCell::applyInterlace( ind, prevNod );
4878 SMDS_MeshCell::applyInterlace( ind, nextNod );
4879 SMDS_MeshCell::applyInterlace( ind, midlNod );
4880 SMDS_MeshCell::applyInterlace( ind, itNN );
4881 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4882 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4884 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4885 vector<int> quantities (nbNodes + 2);
4886 polyedre_nodes.clear();
4890 for (int inode = 0; inode < nbNodes; inode++)
4891 polyedre_nodes.push_back( prevNod[inode] );
4892 quantities.push_back( nbNodes );
4895 polyedre_nodes.push_back( nextNod[0] );
4896 for (int inode = nbNodes; inode-1; --inode )
4897 polyedre_nodes.push_back( nextNod[inode-1] );
4898 quantities.push_back( nbNodes );
4906 const int iQuad = elem->IsQuadratic();
4907 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4909 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4910 int inextface = (iface+1+iQuad) % nbNodes;
4911 int imid = (iface+1) % nbNodes;
4912 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4913 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4914 polyedre_nodes.push_back( prevNod[iface] ); // 1
4915 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4917 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4918 polyedre_nodes.push_back( nextNod[iface] ); // 2
4920 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4921 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4923 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4924 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4926 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4927 if ( nbFaceNodes > 2 )
4928 quantities.push_back( nbFaceNodes );
4929 else // degenerated face
4930 polyedre_nodes.resize( prevNbNodes );
4932 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4934 } // try to create a polyherdal prism
4937 newElems.push_back( aNewElem );
4938 myLastCreatedElems.Append(aNewElem);
4939 srcElements.Append( elem );
4942 // set new prev nodes
4943 for ( iNode = 0; iNode < nbNodes; iNode++ )
4944 prevNod[ iNode ] = nextNod[ iNode ];
4949 //=======================================================================
4951 * \brief Create 1D and 2D elements around swept elements
4952 * \param mapNewNodes - source nodes and ones generated from them
4953 * \param newElemsMap - source elements and ones generated from them
4954 * \param elemNewNodesMap - nodes generated from each node of each element
4955 * \param elemSet - all swept elements
4956 * \param nbSteps - number of sweeping steps
4957 * \param srcElements - to append elem for each generated element
4959 //=======================================================================
4961 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4962 TTElemOfElemListMap & newElemsMap,
4963 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4964 TIDSortedElemSet& elemSet,
4966 SMESH_SequenceOfElemPtr& srcElements)
4968 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4969 SMESHDS_Mesh* aMesh = GetMeshDS();
4971 // Find nodes belonging to only one initial element - sweep them into edges.
4973 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4974 for ( ; nList != mapNewNodes.end(); nList++ )
4976 const SMDS_MeshNode* node =
4977 static_cast<const SMDS_MeshNode*>( nList->first );
4978 if ( newElemsMap.count( node ))
4979 continue; // node was extruded into edge
4980 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4981 int nbInitElems = 0;
4982 const SMDS_MeshElement* el = 0;
4983 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4984 while ( eIt->more() && nbInitElems < 2 ) {
4985 const SMDS_MeshElement* e = eIt->next();
4986 SMDSAbs_ElementType type = e->GetType();
4987 if ( type == SMDSAbs_Volume ||
4991 if ( type > highType ) {
4998 if ( nbInitElems == 1 ) {
4999 bool NotCreateEdge = el && el->IsMediumNode(node);
5000 if(!NotCreateEdge) {
5001 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5002 list<const SMDS_MeshElement*> newEdges;
5003 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5008 // Make a ceiling for each element ie an equal element of last new nodes.
5009 // Find free links of faces - make edges and sweep them into faces.
5011 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5013 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5014 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5015 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5017 const SMDS_MeshElement* elem = itElem->first;
5018 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5020 if(itElem->second.size()==0) continue;
5022 const bool isQuadratic = elem->IsQuadratic();
5024 if ( elem->GetType() == SMDSAbs_Edge ) {
5025 // create a ceiling edge
5026 if ( !isQuadratic ) {
5027 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5028 vecNewNodes[ 1 ]->second.back())) {
5029 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5030 vecNewNodes[ 1 ]->second.back()));
5031 srcElements.Append( elem );
5035 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5036 vecNewNodes[ 1 ]->second.back(),
5037 vecNewNodes[ 2 ]->second.back())) {
5038 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5039 vecNewNodes[ 1 ]->second.back(),
5040 vecNewNodes[ 2 ]->second.back()));
5041 srcElements.Append( elem );
5045 if ( elem->GetType() != SMDSAbs_Face )
5048 bool hasFreeLinks = false;
5050 TIDSortedElemSet avoidSet;
5051 avoidSet.insert( elem );
5053 set<const SMDS_MeshNode*> aFaceLastNodes;
5054 int iNode, nbNodes = vecNewNodes.size();
5055 if ( !isQuadratic ) {
5056 // loop on the face nodes
5057 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5058 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5059 // look for free links of the face
5060 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5061 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5062 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5063 // check if a link n1-n2 is free
5064 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5065 hasFreeLinks = true;
5066 // make a new edge and a ceiling for a new edge
5067 const SMDS_MeshElement* edge;
5068 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5069 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5070 srcElements.Append( myLastCreatedElems.Last() );
5072 n1 = vecNewNodes[ iNode ]->second.back();
5073 n2 = vecNewNodes[ iNext ]->second.back();
5074 if ( !aMesh->FindEdge( n1, n2 )) {
5075 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5076 srcElements.Append( edge );
5081 else { // elem is quadratic face
5082 int nbn = nbNodes/2;
5083 for ( iNode = 0; iNode < nbn; iNode++ ) {
5084 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5085 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5086 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5087 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5088 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5089 // check if a link is free
5090 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5091 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5092 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5093 hasFreeLinks = true;
5094 // make an edge and a ceiling for a new edge
5096 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5097 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5098 srcElements.Append( elem );
5100 n1 = vecNewNodes[ iNode ]->second.back();
5101 n2 = vecNewNodes[ iNext ]->second.back();
5102 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5103 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5104 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5105 srcElements.Append( elem );
5109 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5110 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5114 // sweep free links into faces
5116 if ( hasFreeLinks ) {
5117 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5118 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5120 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5121 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5122 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5123 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5124 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5126 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5127 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5128 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5130 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5131 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5132 std::advance( v, volNb );
5133 // find indices of free faces of a volume and their source edges
5134 list< int > freeInd;
5135 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5136 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5137 int iF, nbF = vTool.NbFaces();
5138 for ( iF = 0; iF < nbF; iF ++ ) {
5139 if (vTool.IsFreeFace( iF ) &&
5140 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5141 initNodeSet != faceNodeSet) // except an initial face
5143 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5145 if ( faceNodeSet == initNodeSetNoCenter )
5147 freeInd.push_back( iF );
5148 // find source edge of a free face iF
5149 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5150 vector<const SMDS_MeshNode*>::iterator lastCommom;
5151 commonNodes.resize( nbNodes, 0 );
5152 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5153 initNodeSet.begin(), initNodeSet.end(),
5154 commonNodes.begin());
5155 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5156 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5158 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5160 if ( !srcEdges.back() )
5162 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5163 << iF << " of volume #" << vTool.ID() << endl;
5168 if ( freeInd.empty() )
5171 // create wall faces for all steps;
5172 // if such a face has been already created by sweep of edge,
5173 // assure that its orientation is OK
5174 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5176 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5177 vTool.SetExternalNormal();
5178 const int nextShift = vTool.IsForward() ? +1 : -1;
5179 list< int >::iterator ind = freeInd.begin();
5180 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5181 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5183 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5184 int nbn = vTool.NbFaceNodes( *ind );
5185 const SMDS_MeshElement * f = 0;
5186 if ( nbn == 3 ) ///// triangle
5188 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5190 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5192 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5194 nodes[ 1 + nextShift ] };
5196 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5198 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5202 else if ( nbn == 4 ) ///// quadrangle
5204 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5206 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5208 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5209 nodes[ 2 ], nodes[ 2+nextShift ] };
5211 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5213 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5214 newOrder[ 2 ], newOrder[ 3 ]));
5217 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5219 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5221 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5223 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5225 nodes[2 + 2*nextShift],
5226 nodes[3 - 2*nextShift],
5228 nodes[3 + 2*nextShift]};
5230 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5232 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5240 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5242 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5243 nodes[1], nodes[3], nodes[5], nodes[7] );
5245 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5247 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5248 nodes[4 - 2*nextShift],
5250 nodes[4 + 2*nextShift],
5252 nodes[5 - 2*nextShift],
5254 nodes[5 + 2*nextShift] };
5256 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5258 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5259 newOrder[ 2 ], newOrder[ 3 ],
5260 newOrder[ 4 ], newOrder[ 5 ],
5261 newOrder[ 6 ], newOrder[ 7 ]));
5264 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5266 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5267 SMDSAbs_Face, /*noMedium=*/false);
5269 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5271 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5272 nodes[4 - 2*nextShift],
5274 nodes[4 + 2*nextShift],
5276 nodes[5 - 2*nextShift],
5278 nodes[5 + 2*nextShift],
5281 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5283 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5284 newOrder[ 2 ], newOrder[ 3 ],
5285 newOrder[ 4 ], newOrder[ 5 ],
5286 newOrder[ 6 ], newOrder[ 7 ],
5290 else //////// polygon
5292 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5293 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5295 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5297 if ( !vTool.IsForward() )
5298 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5300 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5302 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5306 while ( srcElements.Length() < myLastCreatedElems.Length() )
5307 srcElements.Append( *srcEdge );
5309 } // loop on free faces
5311 // go to the next volume
5313 while ( iVol++ < nbVolumesByStep ) v++;
5316 } // loop on volumes of one step
5317 } // sweep free links into faces
5319 // Make a ceiling face with a normal external to a volume
5321 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5322 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5323 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5325 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5326 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5327 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5331 lastVol.SetExternalNormal();
5332 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5333 const int nbn = lastVol.NbFaceNodes( iF );
5334 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5335 if ( !hasFreeLinks ||
5336 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5338 const vector<int>& interlace =
5339 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5340 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5342 AddElement( nodeVec, anyFace.Init( elem ));
5344 while ( srcElements.Length() < myLastCreatedElems.Length() )
5345 srcElements.Append( elem );
5348 } // loop on swept elements
5351 //=======================================================================
5352 //function : RotationSweep
5354 //=======================================================================
5356 SMESH_MeshEditor::PGroupIDs
5357 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5358 const gp_Ax1& theAxis,
5359 const double theAngle,
5360 const int theNbSteps,
5361 const double theTol,
5362 const bool theMakeGroups,
5363 const bool theMakeWalls)
5365 myLastCreatedElems.Clear();
5366 myLastCreatedNodes.Clear();
5368 // source elements for each generated one
5369 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5371 MESSAGE( "RotationSweep()");
5373 aTrsf.SetRotation( theAxis, theAngle );
5375 aTrsf2.SetRotation( theAxis, theAngle/2. );
5377 gp_Lin aLine( theAxis );
5378 double aSqTol = theTol * theTol;
5380 SMESHDS_Mesh* aMesh = GetMeshDS();
5382 TNodeOfNodeListMap mapNewNodes;
5383 TElemOfVecOfNnlmiMap mapElemNewNodes;
5384 TTElemOfElemListMap newElemsMap;
5386 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5387 myMesh->NbFaces(ORDER_QUADRATIC) +
5388 myMesh->NbVolumes(ORDER_QUADRATIC) );
5389 // loop on theElemSets
5390 setElemsFirst( theElemSets );
5391 TIDSortedElemSet::iterator itElem;
5392 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5394 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5395 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5396 const SMDS_MeshElement* elem = *itElem;
5397 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5399 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5400 newNodesItVec.reserve( elem->NbNodes() );
5402 // loop on elem nodes
5403 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5404 while ( itN->more() )
5406 const SMDS_MeshNode* node = cast2Node( itN->next() );
5408 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5410 aXYZ.Coord( coord[0], coord[1], coord[2] );
5411 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5413 // check if a node has been already sweeped
5414 TNodeOfNodeListMapItr nIt =
5415 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5416 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5417 if ( listNewNodes.empty() )
5419 // check if we are to create medium nodes between corner ones
5420 bool needMediumNodes = false;
5421 if ( isQuadraticMesh )
5423 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5424 while (it->more() && !needMediumNodes )
5426 const SMDS_MeshElement* invElem = it->next();
5427 if ( invElem != elem && !theElems.count( invElem )) continue;
5428 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5429 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5430 needMediumNodes = true;
5435 const SMDS_MeshNode * newNode = node;
5436 for ( int i = 0; i < theNbSteps; i++ ) {
5438 if ( needMediumNodes ) // create a medium node
5440 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5441 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5442 myLastCreatedNodes.Append(newNode);
5443 srcNodes.Append( node );
5444 listNewNodes.push_back( newNode );
5445 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5448 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5450 // create a corner node
5451 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5452 myLastCreatedNodes.Append(newNode);
5453 srcNodes.Append( node );
5454 listNewNodes.push_back( newNode );
5457 listNewNodes.push_back( newNode );
5458 // if ( needMediumNodes )
5459 // listNewNodes.push_back( newNode );
5463 newNodesItVec.push_back( nIt );
5465 // make new elements
5466 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5471 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5473 PGroupIDs newGroupIDs;
5474 if ( theMakeGroups )
5475 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5480 //=======================================================================
5481 //function : ExtrusParam
5482 //purpose : standard construction
5483 //=======================================================================
5485 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5486 const int theNbSteps,
5488 const double theTolerance):
5490 myFlags( theFlags ),
5491 myTolerance( theTolerance ),
5492 myElemsToUse( NULL )
5494 mySteps = new TColStd_HSequenceOfReal;
5495 const double stepSize = theStep.Magnitude();
5496 for (int i=1; i<=theNbSteps; i++ )
5497 mySteps->Append( stepSize );
5499 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5500 ( theTolerance > 0 ))
5502 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5506 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5510 //=======================================================================
5511 //function : ExtrusParam
5512 //purpose : steps are given explicitly
5513 //=======================================================================
5515 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5516 Handle(TColStd_HSequenceOfReal) theSteps,
5518 const double theTolerance):
5520 mySteps( theSteps ),
5521 myFlags( theFlags ),
5522 myTolerance( theTolerance ),
5523 myElemsToUse( NULL )
5525 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5526 ( theTolerance > 0 ))
5528 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5532 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5536 //=======================================================================
5537 //function : ExtrusParam
5538 //purpose : for extrusion by normal
5539 //=======================================================================
5541 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5542 const int theNbSteps,
5546 mySteps( new TColStd_HSequenceOfReal ),
5547 myFlags( theFlags ),
5549 myElemsToUse( NULL )
5551 for (int i = 0; i < theNbSteps; i++ )
5552 mySteps->Append( theStepSize );
5556 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5560 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5564 //=======================================================================
5565 //function : ExtrusParam::SetElementsToUse
5566 //purpose : stores elements to use for extrusion by normal, depending on
5567 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5568 //=======================================================================
5570 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5572 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5575 //=======================================================================
5576 //function : ExtrusParam::beginStepIter
5577 //purpose : prepare iteration on steps
5578 //=======================================================================
5580 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5582 myWithMediumNodes = withMediumNodes;
5586 //=======================================================================
5587 //function : ExtrusParam::moreSteps
5588 //purpose : are there more steps?
5589 //=======================================================================
5591 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5593 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5595 //=======================================================================
5596 //function : ExtrusParam::nextStep
5597 //purpose : returns the next step
5598 //=======================================================================
5600 double SMESH_MeshEditor::ExtrusParam::nextStep()
5603 if ( !myCurSteps.empty() )
5605 res = myCurSteps.back();
5606 myCurSteps.pop_back();
5608 else if ( myNextStep <= mySteps->Length() )
5610 myCurSteps.push_back( mySteps->Value( myNextStep ));
5612 if ( myWithMediumNodes )
5614 myCurSteps.back() /= 2.;
5615 myCurSteps.push_back( myCurSteps.back() );
5622 //=======================================================================
5623 //function : ExtrusParam::makeNodesByDir
5624 //purpose : create nodes for standard extrusion
5625 //=======================================================================
5627 int SMESH_MeshEditor::ExtrusParam::
5628 makeNodesByDir( SMESHDS_Mesh* mesh,
5629 const SMDS_MeshNode* srcNode,
5630 std::list<const SMDS_MeshNode*> & newNodes,
5631 const bool makeMediumNodes)
5633 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5636 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5638 p += myDir.XYZ() * nextStep();
5639 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5640 newNodes.push_back( newNode );
5645 //=======================================================================
5646 //function : ExtrusParam::makeNodesByDirAndSew
5647 //purpose : create nodes for standard extrusion with sewing
5648 //=======================================================================
5650 int SMESH_MeshEditor::ExtrusParam::
5651 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5652 const SMDS_MeshNode* srcNode,
5653 std::list<const SMDS_MeshNode*> & newNodes,
5654 const bool makeMediumNodes)
5656 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5659 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5661 P1 += myDir.XYZ() * nextStep();
5663 // try to search in sequence of existing nodes
5664 // if myNodes.Length()>0 we 'nave to use given sequence
5665 // else - use all nodes of mesh
5666 const SMDS_MeshNode * node = 0;
5667 if ( myNodes.Length() > 0 ) {
5669 for(i=1; i<=myNodes.Length(); i++) {
5670 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5671 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5673 node = myNodes.Value(i);
5679 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5680 while(itn->more()) {
5681 SMESH_TNodeXYZ P2( itn->next() );
5682 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5691 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5693 newNodes.push_back( node );
5700 //=======================================================================
5701 //function : ExtrusParam::makeNodesByNormal2D
5702 //purpose : create nodes for extrusion using normals of faces
5703 //=======================================================================
5705 int SMESH_MeshEditor::ExtrusParam::
5706 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5707 const SMDS_MeshNode* srcNode,
5708 std::list<const SMDS_MeshNode*> & newNodes,
5709 const bool makeMediumNodes)
5711 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5713 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5715 // get normals to faces sharing srcNode
5716 vector< gp_XYZ > norms, baryCenters;
5717 gp_XYZ norm, avgNorm( 0,0,0 );
5718 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5719 while ( faceIt->more() )
5721 const SMDS_MeshElement* face = faceIt->next();
5722 if ( myElemsToUse && !myElemsToUse->count( face ))
5724 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5726 norms.push_back( norm );
5728 if ( !alongAvgNorm )
5732 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5733 bc += SMESH_TNodeXYZ( nIt->next() );
5734 baryCenters.push_back( bc / nbN );
5739 if ( norms.empty() ) return 0;
5741 double normSize = avgNorm.Modulus();
5742 if ( normSize < std::numeric_limits<double>::min() )
5745 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5748 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5751 avgNorm /= normSize;
5754 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5757 double stepSize = nextStep();
5759 if ( norms.size() > 1 )
5761 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5763 // translate plane of a face
5764 baryCenters[ iF ] += norms[ iF ] * stepSize;
5766 // find point of intersection of the face plane located at baryCenters[ iF ]
5767 // and avgNorm located at pNew
5768 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5769 double dot = ( norms[ iF ] * avgNorm );
5770 if ( dot < std::numeric_limits<double>::min() )
5771 dot = stepSize * 1e-3;
5772 double step = -( norms[ iF ] * pNew + d ) / dot;
5773 pNew += step * avgNorm;
5778 pNew += stepSize * avgNorm;
5782 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5783 newNodes.push_back( newNode );
5788 //=======================================================================
5789 //function : ExtrusParam::makeNodesByNormal1D
5790 //purpose : create nodes for extrusion using normals of edges
5791 //=======================================================================
5793 int SMESH_MeshEditor::ExtrusParam::
5794 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5795 const SMDS_MeshNode* srcNode,
5796 std::list<const SMDS_MeshNode*> & newNodes,
5797 const bool makeMediumNodes)
5799 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5803 //=======================================================================
5804 //function : ExtrusionSweep
5806 //=======================================================================
5808 SMESH_MeshEditor::PGroupIDs
5809 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5810 const gp_Vec& theStep,
5811 const int theNbSteps,
5812 TTElemOfElemListMap& newElemsMap,
5814 const double theTolerance)
5816 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5817 return ExtrusionSweep( theElems, aParams, newElemsMap );
5821 //=======================================================================
5822 //function : ExtrusionSweep
5824 //=======================================================================
5826 SMESH_MeshEditor::PGroupIDs
5827 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5828 ExtrusParam& theParams,
5829 TTElemOfElemListMap& newElemsMap)
5831 myLastCreatedElems.Clear();
5832 myLastCreatedNodes.Clear();
5834 // source elements for each generated one
5835 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5837 //SMESHDS_Mesh* aMesh = GetMeshDS();
5839 setElemsFirst( theElemSets );
5840 const int nbSteps = theParams.NbSteps();
5841 theParams.SetElementsToUse( theElemSets[0] );
5843 TNodeOfNodeListMap mapNewNodes;
5844 //TNodeOfNodeVecMap mapNewNodes;
5845 TElemOfVecOfNnlmiMap mapElemNewNodes;
5846 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5848 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5849 myMesh->NbFaces(ORDER_QUADRATIC) +
5850 myMesh->NbVolumes(ORDER_QUADRATIC) );
5852 TIDSortedElemSet::iterator itElem;
5853 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5855 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5856 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5858 // check element type
5859 const SMDS_MeshElement* elem = *itElem;
5860 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5863 const size_t nbNodes = elem->NbNodes();
5864 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5865 newNodesItVec.reserve( nbNodes );
5867 // loop on elem nodes
5868 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5869 while ( itN->more() )
5871 // check if a node has been already sweeped
5872 const SMDS_MeshNode* node = cast2Node( itN->next() );
5873 TNodeOfNodeListMap::iterator nIt =
5874 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5875 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5876 if ( listNewNodes.empty() )
5880 // check if we are to create medium nodes between corner ones
5881 bool needMediumNodes = false;
5882 if ( isQuadraticMesh )
5884 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5885 while (it->more() && !needMediumNodes )
5887 const SMDS_MeshElement* invElem = it->next();
5888 if ( invElem != elem && !theElems.count( invElem )) continue;
5889 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5890 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5891 needMediumNodes = true;
5894 // create nodes for all steps
5895 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5897 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5898 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5900 myLastCreatedNodes.Append( *newNodesIt );
5901 srcNodes.Append( node );
5906 break; // newNodesItVec will be shorter than nbNodes
5909 newNodesItVec.push_back( nIt );
5911 // make new elements
5912 if ( newNodesItVec.size() == nbNodes )
5913 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5917 if ( theParams.ToMakeBoundary() ) {
5918 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5920 PGroupIDs newGroupIDs;
5921 if ( theParams.ToMakeGroups() )
5922 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5927 //=======================================================================
5928 //function : ExtrusionAlongTrack
5930 //=======================================================================
5931 SMESH_MeshEditor::Extrusion_Error
5932 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5933 SMESH_subMesh* theTrack,
5934 const SMDS_MeshNode* theN1,
5935 const bool theHasAngles,
5936 list<double>& theAngles,
5937 const bool theLinearVariation,
5938 const bool theHasRefPoint,
5939 const gp_Pnt& theRefPoint,
5940 const bool theMakeGroups)
5942 MESSAGE("ExtrusionAlongTrack");
5943 myLastCreatedElems.Clear();
5944 myLastCreatedNodes.Clear();
5947 std::list<double> aPrms;
5948 TIDSortedElemSet::iterator itElem;
5951 TopoDS_Edge aTrackEdge;
5952 TopoDS_Vertex aV1, aV2;
5954 SMDS_ElemIteratorPtr aItE;
5955 SMDS_NodeIteratorPtr aItN;
5956 SMDSAbs_ElementType aTypeE;
5958 TNodeOfNodeListMap mapNewNodes;
5961 aNbE = theElements[0].size() + theElements[1].size();
5964 return EXTR_NO_ELEMENTS;
5966 // 1.1 Track Pattern
5969 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5971 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5972 theHasAngles, theAngles, theLinearVariation,
5973 theHasRefPoint, theRefPoint, theMakeGroups );
5975 aItE = pSubMeshDS->GetElements();
5976 while ( aItE->more() ) {
5977 const SMDS_MeshElement* pE = aItE->next();
5978 aTypeE = pE->GetType();
5979 // Pattern must contain links only
5980 if ( aTypeE != SMDSAbs_Edge )
5981 return EXTR_PATH_NOT_EDGE;
5984 list<SMESH_MeshEditor_PathPoint> fullList;
5986 const TopoDS_Shape& aS = theTrack->GetSubShape();
5987 // Sub-shape for the Pattern must be an Edge or Wire
5988 if( aS.ShapeType() == TopAbs_EDGE ) {
5989 aTrackEdge = TopoDS::Edge( aS );
5990 // the Edge must not be degenerated
5991 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5992 return EXTR_BAD_PATH_SHAPE;
5993 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5994 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5995 const SMDS_MeshNode* aN1 = aItN->next();
5996 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5997 const SMDS_MeshNode* aN2 = aItN->next();
5998 // starting node must be aN1 or aN2
5999 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6000 return EXTR_BAD_STARTING_NODE;
6001 aItN = pSubMeshDS->GetNodes();
6002 while ( aItN->more() ) {
6003 const SMDS_MeshNode* pNode = aItN->next();
6004 const SMDS_EdgePosition* pEPos =
6005 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6006 double aT = pEPos->GetUParameter();
6007 aPrms.push_back( aT );
6009 //Extrusion_Error err =
6010 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6011 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6012 list< SMESH_subMesh* > LSM;
6013 TopTools_SequenceOfShape Edges;
6014 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6015 while(itSM->more()) {
6016 SMESH_subMesh* SM = itSM->next();
6018 const TopoDS_Shape& aS = SM->GetSubShape();
6021 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6022 int startNid = theN1->GetID();
6023 TColStd_MapOfInteger UsedNums;
6025 int NbEdges = Edges.Length();
6027 for(; i<=NbEdges; i++) {
6029 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6030 for(; itLSM!=LSM.end(); itLSM++) {
6032 if(UsedNums.Contains(k)) continue;
6033 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6034 SMESH_subMesh* locTrack = *itLSM;
6035 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6036 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6037 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6038 const SMDS_MeshNode* aN1 = aItN->next();
6039 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6040 const SMDS_MeshNode* aN2 = aItN->next();
6041 // starting node must be aN1 or aN2
6042 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6043 // 2. Collect parameters on the track edge
6045 aItN = locMeshDS->GetNodes();
6046 while ( aItN->more() ) {
6047 const SMDS_MeshNode* pNode = aItN->next();
6048 const SMDS_EdgePosition* pEPos =
6049 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6050 double aT = pEPos->GetUParameter();
6051 aPrms.push_back( aT );
6053 list<SMESH_MeshEditor_PathPoint> LPP;
6054 //Extrusion_Error err =
6055 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6056 LLPPs.push_back(LPP);
6058 // update startN for search following egde
6059 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6060 else startNid = aN1->GetID();
6064 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6065 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6066 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6067 for(; itPP!=firstList.end(); itPP++) {
6068 fullList.push_back( *itPP );
6070 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6071 fullList.pop_back();
6073 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6074 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6075 itPP = currList.begin();
6076 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6077 gp_Dir D1 = PP1.Tangent();
6078 gp_Dir D2 = PP2.Tangent();
6079 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6080 (D1.Z()+D2.Z())/2 ) );
6081 PP1.SetTangent(Dnew);
6082 fullList.push_back(PP1);
6084 for(; itPP!=firstList.end(); itPP++) {
6085 fullList.push_back( *itPP );
6087 PP1 = fullList.back();
6088 fullList.pop_back();
6090 // if wire not closed
6091 fullList.push_back(PP1);
6095 return EXTR_BAD_PATH_SHAPE;
6098 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6099 theHasRefPoint, theRefPoint, theMakeGroups);
6103 //=======================================================================
6104 //function : ExtrusionAlongTrack
6106 //=======================================================================
6107 SMESH_MeshEditor::Extrusion_Error
6108 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6109 SMESH_Mesh* theTrack,
6110 const SMDS_MeshNode* theN1,
6111 const bool theHasAngles,
6112 list<double>& theAngles,
6113 const bool theLinearVariation,
6114 const bool theHasRefPoint,
6115 const gp_Pnt& theRefPoint,
6116 const bool theMakeGroups)
6118 myLastCreatedElems.Clear();
6119 myLastCreatedNodes.Clear();
6122 std::list<double> aPrms;
6123 TIDSortedElemSet::iterator itElem;
6126 TopoDS_Edge aTrackEdge;
6127 TopoDS_Vertex aV1, aV2;
6129 SMDS_ElemIteratorPtr aItE;
6130 SMDS_NodeIteratorPtr aItN;
6131 SMDSAbs_ElementType aTypeE;
6133 TNodeOfNodeListMap mapNewNodes;
6136 aNbE = theElements[0].size() + theElements[1].size();
6139 return EXTR_NO_ELEMENTS;
6141 // 1.1 Track Pattern
6144 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6146 aItE = pMeshDS->elementsIterator();
6147 while ( aItE->more() ) {
6148 const SMDS_MeshElement* pE = aItE->next();
6149 aTypeE = pE->GetType();
6150 // Pattern must contain links only
6151 if ( aTypeE != SMDSAbs_Edge )
6152 return EXTR_PATH_NOT_EDGE;
6155 list<SMESH_MeshEditor_PathPoint> fullList;
6157 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6159 if ( !theTrack->HasShapeToMesh() ) {
6160 //Mesh without shape
6161 const SMDS_MeshNode* currentNode = NULL;
6162 const SMDS_MeshNode* prevNode = theN1;
6163 std::vector<const SMDS_MeshNode*> aNodesList;
6164 aNodesList.push_back(theN1);
6165 int nbEdges = 0, conn=0;
6166 const SMDS_MeshElement* prevElem = NULL;
6167 const SMDS_MeshElement* currentElem = NULL;
6168 int totalNbEdges = theTrack->NbEdges();
6169 SMDS_ElemIteratorPtr nIt;
6172 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6173 return EXTR_BAD_STARTING_NODE;
6176 conn = nbEdgeConnectivity(theN1);
6178 return EXTR_PATH_NOT_EDGE;
6180 aItE = theN1->GetInverseElementIterator();
6181 prevElem = aItE->next();
6182 currentElem = prevElem;
6184 if(totalNbEdges == 1 ) {
6185 nIt = currentElem->nodesIterator();
6186 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6187 if(currentNode == prevNode)
6188 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6189 aNodesList.push_back(currentNode);
6191 nIt = currentElem->nodesIterator();
6192 while( nIt->more() ) {
6193 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6194 if(currentNode == prevNode)
6195 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6196 aNodesList.push_back(currentNode);
6198 //case of the closed mesh
6199 if(currentNode == theN1) {
6204 conn = nbEdgeConnectivity(currentNode);
6206 return EXTR_PATH_NOT_EDGE;
6207 }else if( conn == 1 && nbEdges > 0 ) {
6212 prevNode = currentNode;
6213 aItE = currentNode->GetInverseElementIterator();
6214 currentElem = aItE->next();
6215 if( currentElem == prevElem)
6216 currentElem = aItE->next();
6217 nIt = currentElem->nodesIterator();
6218 prevElem = currentElem;
6224 if(nbEdges != totalNbEdges)
6225 return EXTR_PATH_NOT_EDGE;
6227 TopTools_SequenceOfShape Edges;
6228 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6229 int startNid = theN1->GetID();
6230 for ( size_t i = 1; i < aNodesList.size(); i++ )
6232 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6233 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6234 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6235 list<SMESH_MeshEditor_PathPoint> LPP;
6237 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6238 LLPPs.push_back(LPP);
6239 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6240 else startNid = aNodesList[i-1]->GetID();
6243 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6244 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6245 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6246 for(; itPP!=firstList.end(); itPP++) {
6247 fullList.push_back( *itPP );
6250 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6251 SMESH_MeshEditor_PathPoint PP2;
6252 fullList.pop_back();
6254 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6255 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6256 itPP = currList.begin();
6257 PP2 = currList.front();
6258 gp_Dir D1 = PP1.Tangent();
6259 gp_Dir D2 = PP2.Tangent();
6260 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6261 PP1.SetTangent(Dnew);
6262 fullList.push_back(PP1);
6264 for(; itPP!=currList.end(); itPP++) {
6265 fullList.push_back( *itPP );
6267 PP1 = fullList.back();
6268 fullList.pop_back();
6270 fullList.push_back(PP1);
6272 } // Sub-shape for the Pattern must be an Edge or Wire
6273 else if ( aS.ShapeType() == TopAbs_EDGE )
6275 aTrackEdge = TopoDS::Edge( aS );
6276 // the Edge must not be degenerated
6277 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6278 return EXTR_BAD_PATH_SHAPE;
6279 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6280 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6281 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6282 // starting node must be aN1 or aN2
6283 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6284 return EXTR_BAD_STARTING_NODE;
6285 aItN = pMeshDS->nodesIterator();
6286 while ( aItN->more() ) {
6287 const SMDS_MeshNode* pNode = aItN->next();
6288 if( pNode==aN1 || pNode==aN2 ) continue;
6289 const SMDS_EdgePosition* pEPos =
6290 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6291 double aT = pEPos->GetUParameter();
6292 aPrms.push_back( aT );
6294 //Extrusion_Error err =
6295 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6297 else if( aS.ShapeType() == TopAbs_WIRE ) {
6298 list< SMESH_subMesh* > LSM;
6299 TopTools_SequenceOfShape Edges;
6300 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6301 for(; eExp.More(); eExp.Next()) {
6302 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6303 if( SMESH_Algo::isDegenerated(E) ) continue;
6304 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6310 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6311 TopoDS_Vertex aVprev;
6312 TColStd_MapOfInteger UsedNums;
6313 int NbEdges = Edges.Length();
6315 for(; i<=NbEdges; i++) {
6317 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6318 for(; itLSM!=LSM.end(); itLSM++) {
6320 if(UsedNums.Contains(k)) continue;
6321 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6322 SMESH_subMesh* locTrack = *itLSM;
6323 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6324 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6325 bool aN1isOK = false, aN2isOK = false;
6326 if ( aVprev.IsNull() ) {
6327 // if previous vertex is not yet defined, it means that we in the beginning of wire
6328 // and we have to find initial vertex corresponding to starting node theN1
6329 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6330 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6331 // starting node must be aN1 or aN2
6332 aN1isOK = ( aN1 && aN1 == theN1 );
6333 aN2isOK = ( aN2 && aN2 == theN1 );
6336 // we have specified ending vertex of the previous edge on the previous iteration
6337 // and we have just to check that it corresponds to any vertex in current segment
6338 aN1isOK = aVprev.IsSame( aV1 );
6339 aN2isOK = aVprev.IsSame( aV2 );
6341 if ( !aN1isOK && !aN2isOK ) continue;
6342 // 2. Collect parameters on the track edge
6344 aItN = locMeshDS->GetNodes();
6345 while ( aItN->more() ) {
6346 const SMDS_MeshNode* pNode = aItN->next();
6347 const SMDS_EdgePosition* pEPos =
6348 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6349 double aT = pEPos->GetUParameter();
6350 aPrms.push_back( aT );
6352 list<SMESH_MeshEditor_PathPoint> LPP;
6353 //Extrusion_Error err =
6354 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6355 LLPPs.push_back(LPP);
6357 // update startN for search following egde
6358 if ( aN1isOK ) aVprev = aV2;
6363 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6364 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6365 fullList.splice( fullList.end(), firstList );
6367 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6368 fullList.pop_back();
6370 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6371 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6372 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6373 gp_Dir D1 = PP1.Tangent();
6374 gp_Dir D2 = PP2.Tangent();
6375 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6376 PP1.SetTangent(Dnew);
6377 fullList.push_back(PP1);
6378 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6379 PP1 = fullList.back();
6380 fullList.pop_back();
6382 // if wire not closed
6383 fullList.push_back(PP1);
6387 return EXTR_BAD_PATH_SHAPE;
6390 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6391 theHasRefPoint, theRefPoint, theMakeGroups);
6395 //=======================================================================
6396 //function : MakeEdgePathPoints
6397 //purpose : auxilary for ExtrusionAlongTrack
6398 //=======================================================================
6399 SMESH_MeshEditor::Extrusion_Error
6400 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6401 const TopoDS_Edge& aTrackEdge,
6403 list<SMESH_MeshEditor_PathPoint>& LPP)
6405 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6407 aTolVec2=aTolVec*aTolVec;
6409 TopoDS_Vertex aV1, aV2;
6410 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6411 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6412 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6413 // 2. Collect parameters on the track edge
6414 aPrms.push_front( aT1 );
6415 aPrms.push_back( aT2 );
6418 if( FirstIsStart ) {
6429 SMESH_MeshEditor_PathPoint aPP;
6430 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6431 std::list<double>::iterator aItD = aPrms.begin();
6432 for(; aItD != aPrms.end(); ++aItD) {
6436 aC3D->D1( aT, aP3D, aVec );
6437 aL2 = aVec.SquareMagnitude();
6438 if ( aL2 < aTolVec2 )
6439 return EXTR_CANT_GET_TANGENT;
6440 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6442 aPP.SetTangent( aTgt );
6443 aPP.SetParameter( aT );
6450 //=======================================================================
6451 //function : MakeExtrElements
6452 //purpose : auxilary for ExtrusionAlongTrack
6453 //=======================================================================
6454 SMESH_MeshEditor::Extrusion_Error
6455 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6456 list<SMESH_MeshEditor_PathPoint>& fullList,
6457 const bool theHasAngles,
6458 list<double>& theAngles,
6459 const bool theLinearVariation,
6460 const bool theHasRefPoint,
6461 const gp_Pnt& theRefPoint,
6462 const bool theMakeGroups)
6464 const int aNbTP = fullList.size();
6467 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6468 LinearAngleVariation(aNbTP-1, theAngles);
6470 // fill vector of path points with angles
6471 vector<SMESH_MeshEditor_PathPoint> aPPs;
6472 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6473 list<double>::iterator itAngles = theAngles.begin();
6474 aPPs.push_back( *itPP++ );
6475 for( ; itPP != fullList.end(); itPP++) {
6476 aPPs.push_back( *itPP );
6477 if ( theHasAngles && itAngles != theAngles.end() )
6478 aPPs.back().SetAngle( *itAngles++ );
6481 TNodeOfNodeListMap mapNewNodes;
6482 TElemOfVecOfNnlmiMap mapElemNewNodes;
6483 TTElemOfElemListMap newElemsMap;
6484 TIDSortedElemSet::iterator itElem;
6485 // source elements for each generated one
6486 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6488 // 3. Center of rotation aV0
6489 gp_Pnt aV0 = theRefPoint;
6490 if ( !theHasRefPoint )
6492 gp_XYZ aGC( 0.,0.,0. );
6493 TIDSortedElemSet newNodes;
6495 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6497 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6498 itElem = theElements.begin();
6499 for ( ; itElem != theElements.end(); itElem++ )
6501 const SMDS_MeshElement* elem = *itElem;
6502 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6503 while ( itN->more() ) {
6504 const SMDS_MeshElement* node = itN->next();
6505 if ( newNodes.insert( node ).second )
6506 aGC += SMESH_TNodeXYZ( node );
6510 aGC /= newNodes.size();
6512 } // if (!theHasRefPoint) {
6514 // 4. Processing the elements
6515 SMESHDS_Mesh* aMesh = GetMeshDS();
6516 list<const SMDS_MeshNode*> emptyList;
6518 setElemsFirst( theElemSets );
6519 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6521 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6522 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6524 const SMDS_MeshElement* elem = *itElem;
6526 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6527 newNodesItVec.reserve( elem->NbNodes() );
6529 // loop on elem nodes
6531 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6532 while ( itN->more() )
6535 // check if a node has been already processed
6536 const SMDS_MeshNode* node = cast2Node( itN->next() );
6537 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6538 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6539 if ( listNewNodes.empty() )
6542 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6543 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6544 gp_Ax1 anAx1, anAxT1T0;
6545 gp_Dir aDT1x, aDT0x, aDT1T0;
6550 aPN0 = SMESH_TNodeXYZ( node );
6552 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6554 aDT0x= aPP0.Tangent();
6556 for ( int j = 1; j < aNbTP; ++j ) {
6557 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6559 aDT1x = aPP1.Tangent();
6560 aAngle1x = aPP1.Angle();
6562 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6564 gp_Vec aV01x( aP0x, aP1x );
6565 aTrsf.SetTranslation( aV01x );
6568 aV1x = aV0x.Transformed( aTrsf );
6569 aPN1 = aPN0.Transformed( aTrsf );
6571 // rotation 1 [ T1,T0 ]
6572 aAngleT1T0=-aDT1x.Angle( aDT0x );
6573 if (fabs(aAngleT1T0) > aTolAng)
6576 anAxT1T0.SetLocation( aV1x );
6577 anAxT1T0.SetDirection( aDT1T0 );
6578 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6580 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6584 if ( theHasAngles ) {
6585 anAx1.SetLocation( aV1x );
6586 anAx1.SetDirection( aDT1x );
6587 aTrsfRot.SetRotation( anAx1, aAngle1x );
6589 aPN1 = aPN1.Transformed( aTrsfRot );
6593 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6595 // create additional node
6596 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6597 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6598 myLastCreatedNodes.Append(newNode);
6599 srcNodes.Append( node );
6600 listNewNodes.push_back( newNode );
6602 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6603 myLastCreatedNodes.Append(newNode);
6604 srcNodes.Append( node );
6605 listNewNodes.push_back( newNode );
6613 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6615 // if current elem is quadratic and current node is not medium
6616 // we have to check - may be it is needed to insert additional nodes
6617 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6618 if ((int) listNewNodes.size() == aNbTP-1 )
6620 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6621 gp_XYZ P(node->X(), node->Y(), node->Z());
6622 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6624 for(i=0; i<aNbTP-1; i++) {
6625 const SMDS_MeshNode* N = *it;
6626 double x = ( N->X() + P.X() )/2.;
6627 double y = ( N->Y() + P.Y() )/2.;
6628 double z = ( N->Z() + P.Z() )/2.;
6629 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6630 srcNodes.Append( node );
6631 myLastCreatedNodes.Append(newN);
6634 P = gp_XYZ(N->X(),N->Y(),N->Z());
6636 listNewNodes.clear();
6637 for(i=0; i<2*(aNbTP-1); i++) {
6638 listNewNodes.push_back(aNodes[i]);
6643 newNodesItVec.push_back( nIt );
6646 // make new elements
6647 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6651 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6653 if ( theMakeGroups )
6654 generateGroups( srcNodes, srcElems, "extruded");
6660 //=======================================================================
6661 //function : LinearAngleVariation
6662 //purpose : auxilary for ExtrusionAlongTrack
6663 //=======================================================================
6664 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6665 list<double>& Angles)
6667 int nbAngles = Angles.size();
6668 if( nbSteps > nbAngles ) {
6669 vector<double> theAngles(nbAngles);
6670 list<double>::iterator it = Angles.begin();
6672 for(; it!=Angles.end(); it++) {
6674 theAngles[i] = (*it);
6677 double rAn2St = double( nbAngles ) / double( nbSteps );
6678 double angPrev = 0, angle;
6679 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6680 double angCur = rAn2St * ( iSt+1 );
6681 double angCurFloor = floor( angCur );
6682 double angPrevFloor = floor( angPrev );
6683 if ( angPrevFloor == angCurFloor )
6684 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6686 int iP = int( angPrevFloor );
6687 double angPrevCeil = ceil(angPrev);
6688 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6690 int iC = int( angCurFloor );
6691 if ( iC < nbAngles )
6692 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6694 iP = int( angPrevCeil );
6696 angle += theAngles[ iC ];
6698 res.push_back(angle);
6703 for(; it!=res.end(); it++)
6704 Angles.push_back( *it );
6709 //================================================================================
6711 * \brief Move or copy theElements applying theTrsf to their nodes
6712 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6713 * \param theTrsf - transformation to apply
6714 * \param theCopy - if true, create translated copies of theElems
6715 * \param theMakeGroups - if true and theCopy, create translated groups
6716 * \param theTargetMesh - mesh to copy translated elements into
6717 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6719 //================================================================================
6721 SMESH_MeshEditor::PGroupIDs
6722 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6723 const gp_Trsf& theTrsf,
6725 const bool theMakeGroups,
6726 SMESH_Mesh* theTargetMesh)
6728 myLastCreatedElems.Clear();
6729 myLastCreatedNodes.Clear();
6731 bool needReverse = false;
6732 string groupPostfix;
6733 switch ( theTrsf.Form() ) {
6735 MESSAGE("gp_PntMirror");
6737 groupPostfix = "mirrored";
6740 MESSAGE("gp_Ax1Mirror");
6741 groupPostfix = "mirrored";
6744 MESSAGE("gp_Ax2Mirror");
6746 groupPostfix = "mirrored";
6749 MESSAGE("gp_Rotation");
6750 groupPostfix = "rotated";
6752 case gp_Translation:
6753 MESSAGE("gp_Translation");
6754 groupPostfix = "translated";
6757 MESSAGE("gp_Scale");
6758 groupPostfix = "scaled";
6760 case gp_CompoundTrsf: // different scale by axis
6761 MESSAGE("gp_CompoundTrsf");
6762 groupPostfix = "scaled";
6766 needReverse = false;
6767 groupPostfix = "transformed";
6770 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6771 SMESHDS_Mesh* aMesh = GetMeshDS();
6773 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6774 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6775 SMESH_MeshEditor::ElemFeatures elemType;
6777 // map old node to new one
6778 TNodeNodeMap nodeMap;
6780 // elements sharing moved nodes; those of them which have all
6781 // nodes mirrored but are not in theElems are to be reversed
6782 TIDSortedElemSet inverseElemSet;
6784 // source elements for each generated one
6785 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6787 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6788 TIDSortedElemSet orphanNode;
6790 if ( theElems.empty() ) // transform the whole mesh
6793 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6794 while ( eIt->more() ) theElems.insert( eIt->next() );
6796 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6797 while ( nIt->more() )
6799 const SMDS_MeshNode* node = nIt->next();
6800 if ( node->NbInverseElements() == 0)
6801 orphanNode.insert( node );
6805 // loop on elements to transform nodes : first orphan nodes then elems
6806 TIDSortedElemSet::iterator itElem;
6807 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6808 for (int i=0; i<2; i++)
6809 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6811 const SMDS_MeshElement* elem = *itElem;
6815 // loop on elem nodes
6817 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6818 while ( itN->more() )
6820 const SMDS_MeshNode* node = cast2Node( itN->next() );
6821 // check if a node has been already transformed
6822 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6823 nodeMap.insert( make_pair ( node, node ));
6824 if ( !n2n_isnew.second )
6827 node->GetXYZ( coord );
6828 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6829 if ( theTargetMesh ) {
6830 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6831 n2n_isnew.first->second = newNode;
6832 myLastCreatedNodes.Append(newNode);
6833 srcNodes.Append( node );
6835 else if ( theCopy ) {
6836 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6837 n2n_isnew.first->second = newNode;
6838 myLastCreatedNodes.Append(newNode);
6839 srcNodes.Append( node );
6842 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6843 // node position on shape becomes invalid
6844 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6845 ( SMDS_SpacePosition::originSpacePosition() );
6848 // keep inverse elements
6849 if ( !theCopy && !theTargetMesh && needReverse ) {
6850 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6851 while ( invElemIt->more() ) {
6852 const SMDS_MeshElement* iel = invElemIt->next();
6853 inverseElemSet.insert( iel );
6857 } // loop on elems in { &orphanNode, &theElems };
6859 // either create new elements or reverse mirrored ones
6860 if ( !theCopy && !needReverse && !theTargetMesh )
6863 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6865 // Replicate or reverse elements
6867 std::vector<int> iForw;
6868 vector<const SMDS_MeshNode*> nodes;
6869 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6871 const SMDS_MeshElement* elem = *itElem;
6872 if ( !elem ) continue;
6874 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6875 size_t nbNodes = elem->NbNodes();
6876 if ( geomType == SMDSGeom_NONE ) continue; // node
6878 nodes.resize( nbNodes );
6880 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6882 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6886 bool allTransformed = true;
6887 int nbFaces = aPolyedre->NbFaces();
6888 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6890 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6891 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6893 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6894 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6895 if ( nodeMapIt == nodeMap.end() )
6896 allTransformed = false; // not all nodes transformed
6898 nodes.push_back((*nodeMapIt).second);
6900 if ( needReverse && allTransformed )
6901 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6903 if ( !allTransformed )
6904 continue; // not all nodes transformed
6906 else // ----------------------- the rest element types
6908 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6909 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6910 const vector<int>& i = needReverse ? iRev : iForw;
6912 // find transformed nodes
6914 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6915 while ( itN->more() ) {
6916 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6917 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6918 if ( nodeMapIt == nodeMap.end() )
6919 break; // not all nodes transformed
6920 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6922 if ( iNode != nbNodes )
6923 continue; // not all nodes transformed
6927 // copy in this or a new mesh
6928 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6929 srcElems.Append( elem );
6932 // reverse element as it was reversed by transformation
6934 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6937 } // loop on elements
6939 if ( editor && editor != this )
6940 myLastCreatedElems = editor->myLastCreatedElems;
6942 PGroupIDs newGroupIDs;
6944 if ( ( theMakeGroups && theCopy ) ||
6945 ( theMakeGroups && theTargetMesh ) )
6946 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6951 //=======================================================================
6953 * \brief Create groups of elements made during transformation
6954 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6955 * \param elemGens - elements making corresponding myLastCreatedElems
6956 * \param postfix - to append to names of new groups
6957 * \param targetMesh - mesh to create groups in
6958 * \param topPresent - is there "top" elements that are created by sweeping
6960 //=======================================================================
6962 SMESH_MeshEditor::PGroupIDs
6963 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6964 const SMESH_SequenceOfElemPtr& elemGens,
6965 const std::string& postfix,
6966 SMESH_Mesh* targetMesh,
6967 const bool topPresent)
6969 PGroupIDs newGroupIDs( new list<int> );
6970 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6972 // Sort existing groups by types and collect their names
6974 // containers to store an old group and generated new ones;
6975 // 1st new group is for result elems of different type than a source one;
6976 // 2nd new group is for same type result elems ("top" group at extrusion)
6978 using boost::make_tuple;
6979 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6980 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6981 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6983 set< string > groupNames;
6985 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6986 if ( !groupIt->more() ) return newGroupIDs;
6988 int newGroupID = mesh->GetGroupIds().back()+1;
6989 while ( groupIt->more() )
6991 SMESH_Group * group = groupIt->next();
6992 if ( !group ) continue;
6993 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6994 if ( !groupDS || groupDS->IsEmpty() ) continue;
6995 groupNames.insert ( group->GetName() );
6996 groupDS->SetStoreName( group->GetName() );
6997 const SMDSAbs_ElementType type = groupDS->GetType();
6998 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6999 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7000 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7001 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7004 // Loop on nodes and elements to add them in new groups
7006 vector< const SMDS_MeshElement* > resultElems;
7007 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7009 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7010 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7011 if ( gens.Length() != elems.Length() )
7012 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7014 // loop on created elements
7015 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7017 const SMDS_MeshElement* sourceElem = gens( iElem );
7018 if ( !sourceElem ) {
7019 MESSAGE("generateGroups(): NULL source element");
7022 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7023 if ( groupsOldNew.empty() ) { // no groups of this type at all
7024 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7025 ++iElem; // skip all elements made by sourceElem
7028 // collect all elements made by the iElem-th sourceElem
7029 resultElems.clear();
7030 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7031 if ( resElem != sourceElem )
7032 resultElems.push_back( resElem );
7033 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7034 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7035 if ( resElem != sourceElem )
7036 resultElems.push_back( resElem );
7038 const SMDS_MeshElement* topElem = 0;
7039 if ( isNodes ) // there must be a top element
7041 topElem = resultElems.back();
7042 resultElems.pop_back();
7046 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7047 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7048 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7050 topElem = *resElemIt;
7051 *resElemIt = 0; // erase *resElemIt
7055 // add resultElems to groups originted from ones the sourceElem belongs to
7056 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7057 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7059 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7060 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7062 // fill in a new group
7063 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7064 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7065 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7067 newGroup.Add( *resElemIt );
7069 // fill a "top" group
7072 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7073 newTopGroup.Add( topElem );
7077 } // loop on created elements
7078 }// loop on nodes and elements
7080 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7082 list<int> topGrouIds;
7083 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7085 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7086 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7087 orderedOldNewGroups[i]->get<2>() };
7088 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7090 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7091 if ( newGroupDS->IsEmpty() )
7093 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7098 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7101 const bool isTop = ( topPresent &&
7102 newGroupDS->GetType() == oldGroupDS->GetType() &&
7105 string name = oldGroupDS->GetStoreName();
7106 { // remove trailing whitespaces (issue 22599)
7107 size_t size = name.size();
7108 while ( size > 1 && isspace( name[ size-1 ]))
7110 if ( size != name.size() )
7112 name.resize( size );
7113 oldGroupDS->SetStoreName( name.c_str() );
7116 if ( !targetMesh ) {
7117 string suffix = ( isTop ? "top": postfix.c_str() );
7121 while ( !groupNames.insert( name ).second ) // name exists
7122 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7127 newGroupDS->SetStoreName( name.c_str() );
7129 // make a SMESH_Groups
7130 mesh->AddGroup( newGroupDS );
7132 topGrouIds.push_back( newGroupDS->GetID() );
7134 newGroupIDs->push_back( newGroupDS->GetID() );
7138 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7143 //================================================================================
7145 * * \brief Return list of group of nodes close to each other within theTolerance
7146 * * Search among theNodes or in the whole mesh if theNodes is empty using
7147 * * an Octree algorithm
7148 * \param [in,out] theNodes - the nodes to treat
7149 * \param [in] theTolerance - the tolerance
7150 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7151 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7152 * corner and medium nodes in separate groups
7154 //================================================================================
7156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7157 const double theTolerance,
7158 TListOfListOfNodes & theGroupsOfNodes,
7159 bool theSeparateCornersAndMedium)
7161 myLastCreatedElems.Clear();
7162 myLastCreatedNodes.Clear();
7164 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7165 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7166 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7167 theSeparateCornersAndMedium = false;
7169 TIDSortedNodeSet& corners = theNodes;
7170 TIDSortedNodeSet medium;
7172 if ( theNodes.empty() ) // get all nodes in the mesh
7174 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7175 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7176 if ( theSeparateCornersAndMedium )
7177 while ( nIt->more() )
7179 const SMDS_MeshNode* n = nIt->next();
7180 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7181 nodeSet->insert( nodeSet->end(), n );
7184 while ( nIt->more() )
7185 theNodes.insert( theNodes.end(),nIt->next() );
7187 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7189 TIDSortedNodeSet::iterator nIt = corners.begin();
7190 while ( nIt != corners.end() )
7191 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7193 medium.insert( medium.end(), *nIt );
7194 corners.erase( nIt++ );
7202 if ( !corners.empty() )
7203 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7204 if ( !medium.empty() )
7205 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7208 //=======================================================================
7209 //function : SimplifyFace
7210 //purpose : split a chain of nodes into several closed chains
7211 //=======================================================================
7213 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7214 vector<const SMDS_MeshNode *>& poly_nodes,
7215 vector<int>& quantities) const
7217 int nbNodes = faceNodes.size();
7218 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7222 size_t prevNbQuant = quantities.size();
7224 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7225 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7226 map< const SMDS_MeshNode*, int >::iterator nInd;
7228 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7229 simpleNodes.push_back( faceNodes[0] );
7230 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7232 if ( faceNodes[ iCur ] != simpleNodes.back() )
7234 int index = simpleNodes.size();
7235 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7236 int prevIndex = nInd->second;
7237 if ( prevIndex < index )
7240 int loopLen = index - prevIndex;
7243 // store the sub-loop
7244 quantities.push_back( loopLen );
7245 for ( int i = prevIndex; i < index; i++ )
7246 poly_nodes.push_back( simpleNodes[ i ]);
7248 simpleNodes.resize( prevIndex+1 );
7252 simpleNodes.push_back( faceNodes[ iCur ]);
7257 if ( simpleNodes.size() > 2 )
7259 quantities.push_back( simpleNodes.size() );
7260 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7263 return quantities.size() - prevNbQuant;
7266 //=======================================================================
7267 //function : MergeNodes
7268 //purpose : In each group, the cdr of nodes are substituted by the first one
7270 //=======================================================================
7272 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7274 MESSAGE("MergeNodes");
7275 myLastCreatedElems.Clear();
7276 myLastCreatedNodes.Clear();
7278 SMESHDS_Mesh* aMesh = GetMeshDS();
7280 TNodeNodeMap nodeNodeMap; // node to replace - new node
7281 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7282 list< int > rmElemIds, rmNodeIds;
7284 // Fill nodeNodeMap and elems
7286 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7287 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7289 list<const SMDS_MeshNode*>& nodes = *grIt;
7290 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7291 const SMDS_MeshNode* nToKeep = *nIt;
7292 for ( ++nIt; nIt != nodes.end(); nIt++ )
7294 const SMDS_MeshNode* nToRemove = *nIt;
7295 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7296 if ( nToRemove != nToKeep )
7298 rmNodeIds.push_back( nToRemove->GetID() );
7299 AddToSameGroups( nToKeep, nToRemove, aMesh );
7300 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7301 // after MergeNodes() w/o creating node in place of merged ones.
7302 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7303 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7304 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7305 sm->SetIsAlwaysComputed( true );
7307 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7308 while ( invElemIt->more() ) {
7309 const SMDS_MeshElement* elem = invElemIt->next();
7314 // Change element nodes or remove an element
7316 set<const SMDS_MeshNode*> nodeSet;
7317 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7319 ElemFeatures elemType;
7321 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7322 for ( ; eIt != elems.end(); eIt++ )
7324 const SMDS_MeshElement* elem = *eIt;
7325 const int nbNodes = elem->NbNodes();
7326 const int aShapeId = FindShape( elem );
7327 SMDSAbs_EntityType entity = elem->GetEntityType();
7330 curNodes.resize( nbNodes );
7331 uniqueNodes.resize( nbNodes );
7332 iRepl.resize( nbNodes );
7333 int iUnique = 0, iCur = 0, nbRepl = 0;
7335 // get new seq of nodes
7336 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7337 while ( itN->more() )
7339 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7341 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7342 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7344 { ////////// BUG 0020185: begin
7345 bool stopRecur = false;
7346 set<const SMDS_MeshNode*> nodesRecur;
7347 nodesRecur.insert(n);
7348 while (!stopRecur) {
7349 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7350 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7351 n = (*nnIt_i).second;
7352 if (!nodesRecur.insert(n).second) {
7353 // error: recursive dependency
7360 } ////////// BUG 0020185: end
7362 curNodes[ iCur ] = n;
7363 bool isUnique = nodeSet.insert( n ).second;
7365 uniqueNodes[ iUnique++ ] = n;
7367 iRepl[ nbRepl++ ] = iCur;
7371 // Analyse element topology after replacement
7374 int nbUniqueNodes = nodeSet.size();
7375 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7377 if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7379 if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7381 elemType.Init( elem );
7382 const bool isQuad = elemType.myIsQuad;
7384 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7385 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7387 // a polygon can divide into several elements
7388 vector<const SMDS_MeshNode *> polygons_nodes;
7389 vector<int> quantities;
7390 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7393 vector<const SMDS_MeshNode *> face_nodes;
7395 for (int iface = 0; iface < nbNew; iface++)
7397 int nbNewNodes = quantities[iface];
7398 face_nodes.assign( polygons_nodes.begin() + inode,
7399 polygons_nodes.begin() + inode + nbNewNodes );
7400 inode += nbNewNodes;
7401 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7403 bool isValid = ( nbNewNodes % 2 == 0 );
7404 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7405 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7406 elemType.SetQuad( isValid );
7407 if ( isValid ) // put medium nodes after corners
7408 SMDS_MeshCell::applyInterlaceRev
7409 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7410 nbNewNodes ), face_nodes );
7412 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7414 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7416 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7419 rmElemIds.push_back(elem->GetID());
7423 else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7425 if ( nbUniqueNodes < 4 ) {
7426 rmElemIds.push_back(elem->GetID());
7429 // each face has to be analyzed in order to check volume validity
7430 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7433 int nbFaces = aPolyedre->NbFaces();
7435 vector<const SMDS_MeshNode *> poly_nodes;
7436 vector<int> quantities;
7437 vector<const SMDS_MeshNode *> faceNodes;
7439 for (int iface = 1; iface <= nbFaces; iface++)
7441 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7442 faceNodes.resize( nbFaceNodes );
7443 for (int inode = 1; inode <= nbFaceNodes; inode++)
7445 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7446 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7447 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7448 faceNode = (*nnIt).second;
7449 faceNodes[inode - 1] = faceNode;
7451 SimplifyFace(faceNodes, poly_nodes, quantities);
7454 if ( quantities.size() > 3 ) {
7455 // TODO: remove coincident faces
7458 if ( quantities.size() > 3 )
7460 const SMDS_MeshElement* newElem =
7461 aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7462 myLastCreatedElems.Append( newElem );
7463 if ( aShapeId && newElem )
7464 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7465 rmElemIds.push_back( elem->GetID() );
7469 rmElemIds.push_back( elem->GetID() );
7480 // TODO not all the possible cases are solved. Find something more generic?
7482 case SMDSEntity_Edge: //////// EDGE
7483 case SMDSEntity_Triangle: //// TRIANGLE
7484 case SMDSEntity_Quad_Triangle:
7485 case SMDSEntity_Tetra:
7486 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7491 case SMDSEntity_Quad_Edge:
7493 isOk = false; // to linear EDGE ???????
7496 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7498 if ( nbUniqueNodes < 3 )
7500 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7501 isOk = false; // opposite nodes stick
7504 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7513 if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7514 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7515 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7516 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7517 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7523 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7532 if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7533 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7534 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7535 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7536 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7542 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7545 if ( nbUniqueNodes == 4 ) {
7546 // ---------------------------------> tetrahedron
7547 if ( curNodes[3] == curNodes[4] &&
7548 curNodes[3] == curNodes[5] ) {
7552 else if ( curNodes[0] == curNodes[1] &&
7553 curNodes[0] == curNodes[2] ) {
7554 // bottom nodes stick: set a top before
7555 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7556 uniqueNodes[ 0 ] = curNodes [ 5 ];
7557 uniqueNodes[ 1 ] = curNodes [ 4 ];
7558 uniqueNodes[ 2 ] = curNodes [ 3 ];
7561 else if (( curNodes[0] == curNodes[3] ) +
7562 ( curNodes[1] == curNodes[4] ) +
7563 ( curNodes[2] == curNodes[5] ) == 2 ) {
7564 // a lateral face turns into a line
7568 else if ( nbUniqueNodes == 5 ) {
7569 // PENTAHEDRON --------------------> pyramid
7570 if ( curNodes[0] == curNodes[3] )
7572 uniqueNodes[ 0 ] = curNodes[ 1 ];
7573 uniqueNodes[ 1 ] = curNodes[ 4 ];
7574 uniqueNodes[ 2 ] = curNodes[ 5 ];
7575 uniqueNodes[ 3 ] = curNodes[ 2 ];
7576 uniqueNodes[ 4 ] = curNodes[ 0 ];
7579 if ( curNodes[1] == curNodes[4] )
7581 uniqueNodes[ 0 ] = curNodes[ 0 ];
7582 uniqueNodes[ 1 ] = curNodes[ 2 ];
7583 uniqueNodes[ 2 ] = curNodes[ 5 ];
7584 uniqueNodes[ 3 ] = curNodes[ 3 ];
7585 uniqueNodes[ 4 ] = curNodes[ 1 ];
7588 if ( curNodes[2] == curNodes[5] )
7590 uniqueNodes[ 0 ] = curNodes[ 0 ];
7591 uniqueNodes[ 1 ] = curNodes[ 3 ];
7592 uniqueNodes[ 2 ] = curNodes[ 4 ];
7593 uniqueNodes[ 3 ] = curNodes[ 1 ];
7594 uniqueNodes[ 4 ] = curNodes[ 2 ];
7600 case SMDSEntity_Hexa:
7602 //////////////////////////////////// HEXAHEDRON
7604 SMDS_VolumeTool hexa (elem);
7605 hexa.SetExternalNormal();
7606 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7607 //////////////////////// HEX ---> tetrahedron
7608 for ( int iFace = 0; iFace < 6; iFace++ ) {
7609 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7610 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7611 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7612 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7613 // one face turns into a point ...
7614 int pickInd = ind[ 0 ];
7615 int iOppFace = hexa.GetOppFaceIndex( iFace );
7616 ind = hexa.GetFaceNodesIndices( iOppFace );
7618 uniqueNodes.clear();
7619 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7620 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7623 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7625 if ( nbStick == 1 ) {
7626 // ... and the opposite one - into a triangle.
7628 uniqueNodes.push_back( curNodes[ pickInd ]);
7635 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7636 //////////////////////// HEX ---> prism
7637 int nbTria = 0, iTria[3];
7638 const int *ind; // indices of face nodes
7639 // look for triangular faces
7640 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7641 ind = hexa.GetFaceNodesIndices( iFace );
7642 TIDSortedNodeSet faceNodes;
7643 for ( iCur = 0; iCur < 4; iCur++ )
7644 faceNodes.insert( curNodes[ind[iCur]] );
7645 if ( faceNodes.size() == 3 )
7646 iTria[ nbTria++ ] = iFace;
7648 // check if triangles are opposite
7649 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7651 // set nodes of the bottom triangle
7652 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7654 for ( iCur = 0; iCur < 4; iCur++ )
7655 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7656 indB.push_back( ind[iCur] );
7657 if ( !hexa.IsForward() )
7658 std::swap( indB[0], indB[2] );
7659 for ( iCur = 0; iCur < 3; iCur++ )
7660 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7661 // set nodes of the top triangle
7662 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7663 for ( iCur = 0; iCur < 3; ++iCur )
7664 for ( int j = 0; j < 4; ++j )
7665 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7667 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7674 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7675 //////////////////// HEXAHEDRON ---> pyramid
7676 for ( int iFace = 0; iFace < 6; iFace++ ) {
7677 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7678 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7679 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7680 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7681 // one face turns into a point ...
7682 int iOppFace = hexa.GetOppFaceIndex( iFace );
7683 ind = hexa.GetFaceNodesIndices( iOppFace );
7684 uniqueNodes.clear();
7685 for ( iCur = 0; iCur < 4; iCur++ ) {
7686 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7689 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7691 if ( uniqueNodes.size() == 4 ) {
7692 // ... and the opposite one is a quadrangle
7694 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7695 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7703 if ( !isOk && nbUniqueNodes > 4 ) {
7704 ////////////////// HEXAHEDRON ---> polyhedron
7705 hexa.SetExternalNormal();
7706 vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7707 vector<int> quantities; quantities.reserve( 6 );
7708 for ( int iFace = 0; iFace < 6; iFace++ )
7710 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7711 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7712 curNodes[ind[1]] == curNodes[ind[3]] )
7715 break; // opposite nodes stick
7718 for ( iCur = 0; iCur < 4; iCur++ )
7720 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7721 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7723 if ( nodeSet.size() < 3 )
7724 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7726 quantities.push_back( nodeSet.size() );
7728 if ( quantities.size() >= 4 )
7730 const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7731 myLastCreatedElems.Append( newElem );
7732 if ( aShapeId && newElem )
7733 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7734 rmElemIds.push_back( elem->GetID() );
7738 } // case HEXAHEDRON
7742 } // switch ( nbNodes )
7744 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7746 if ( isOk ) // a non-poly elem remains valid after sticking nodes
7748 if ( nbNodes != nbUniqueNodes ||
7749 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7751 elemType.Init( elem ).SetID( elem->GetID() );
7753 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7754 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7756 uniqueNodes.resize(nbUniqueNodes);
7757 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7758 if ( sm && newElem )
7759 sm->AddElement( newElem );
7760 if ( elem != newElem )
7761 ReplaceElemInGroups( elem, newElem, aMesh );
7765 // Remove invalid regular element or invalid polygon
7766 rmElemIds.push_back( elem->GetID() );
7769 } // loop on elements
7771 // Remove bad elements, then equal nodes (order important)
7773 Remove( rmElemIds, false );
7774 Remove( rmNodeIds, true );
7780 // ========================================================
7781 // class : SortableElement
7782 // purpose : allow sorting elements basing on their nodes
7783 // ========================================================
7784 class SortableElement : public set <const SMDS_MeshElement*>
7788 SortableElement( const SMDS_MeshElement* theElem )
7791 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7792 while ( nodeIt->more() )
7793 this->insert( nodeIt->next() );
7796 const SMDS_MeshElement* Get() const
7800 mutable const SMDS_MeshElement* myElem;
7803 //=======================================================================
7804 //function : FindEqualElements
7805 //purpose : Return list of group of elements built on the same nodes.
7806 // Search among theElements or in the whole mesh if theElements is empty
7807 //=======================================================================
7809 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7810 TListOfListOfElementsID & theGroupsOfElementsID)
7812 myLastCreatedElems.Clear();
7813 myLastCreatedNodes.Clear();
7815 typedef map< SortableElement, int > TMapOfNodeSet;
7816 typedef list<int> TGroupOfElems;
7818 if ( theElements.empty() )
7819 { // get all elements in the mesh
7820 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7821 while ( eIt->more() )
7822 theElements.insert( theElements.end(), eIt->next() );
7825 vector< TGroupOfElems > arrayOfGroups;
7826 TGroupOfElems groupOfElems;
7827 TMapOfNodeSet mapOfNodeSet;
7829 TIDSortedElemSet::iterator elemIt = theElements.begin();
7830 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7832 const SMDS_MeshElement* curElem = *elemIt;
7833 SortableElement SE(curElem);
7835 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7836 if ( !pp.second ) { // one more coincident elem
7837 TMapOfNodeSet::iterator& itSE = pp.first;
7838 int ind = (*itSE).second;
7839 arrayOfGroups[ind].push_back( curElem->GetID() );
7842 arrayOfGroups.push_back( groupOfElems );
7843 arrayOfGroups.back().push_back( curElem->GetID() );
7848 groupOfElems.clear();
7849 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7850 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7852 if ( groupIt->size() > 1 ) {
7853 //groupOfElems.sort(); -- theElements is sorted already
7854 theGroupsOfElementsID.push_back( groupOfElems );
7855 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7860 //=======================================================================
7861 //function : MergeElements
7862 //purpose : In each given group, substitute all elements by the first one.
7863 //=======================================================================
7865 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7867 myLastCreatedElems.Clear();
7868 myLastCreatedNodes.Clear();
7870 typedef list<int> TListOfIDs;
7871 TListOfIDs rmElemIds; // IDs of elems to remove
7873 SMESHDS_Mesh* aMesh = GetMeshDS();
7875 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7876 while ( groupsIt != theGroupsOfElementsID.end() ) {
7877 TListOfIDs& aGroupOfElemID = *groupsIt;
7878 aGroupOfElemID.sort();
7879 int elemIDToKeep = aGroupOfElemID.front();
7880 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7881 aGroupOfElemID.pop_front();
7882 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7883 while ( idIt != aGroupOfElemID.end() ) {
7884 int elemIDToRemove = *idIt;
7885 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7886 // add the kept element in groups of removed one (PAL15188)
7887 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7888 rmElemIds.push_back( elemIDToRemove );
7894 Remove( rmElemIds, false );
7897 //=======================================================================
7898 //function : MergeEqualElements
7899 //purpose : Remove all but one of elements built on the same nodes.
7900 //=======================================================================
7902 void SMESH_MeshEditor::MergeEqualElements()
7904 TIDSortedElemSet aMeshElements; /* empty input ==
7905 to merge equal elements in the whole mesh */
7906 TListOfListOfElementsID aGroupsOfElementsID;
7907 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7908 MergeElements(aGroupsOfElementsID);
7911 //=======================================================================
7912 //function : findAdjacentFace
7914 //=======================================================================
7916 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7917 const SMDS_MeshNode* n2,
7918 const SMDS_MeshElement* elem)
7920 TIDSortedElemSet elemSet, avoidSet;
7922 avoidSet.insert ( elem );
7923 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7926 //=======================================================================
7927 //function : findSegment
7928 //purpose : Return a mesh segment by two nodes one of which can be medium
7929 //=======================================================================
7931 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7932 const SMDS_MeshNode* n2)
7934 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7935 while ( it->more() )
7937 const SMDS_MeshElement* seg = it->next();
7938 if ( seg->GetNodeIndex( n2 ) >= 0 )
7944 //=======================================================================
7945 //function : FindFreeBorder
7947 //=======================================================================
7949 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7951 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7952 const SMDS_MeshNode* theSecondNode,
7953 const SMDS_MeshNode* theLastNode,
7954 list< const SMDS_MeshNode* > & theNodes,
7955 list< const SMDS_MeshElement* >& theFaces)
7957 if ( !theFirstNode || !theSecondNode )
7959 // find border face between theFirstNode and theSecondNode
7960 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7964 theFaces.push_back( curElem );
7965 theNodes.push_back( theFirstNode );
7966 theNodes.push_back( theSecondNode );
7968 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7969 TIDSortedElemSet foundElems;
7970 bool needTheLast = ( theLastNode != 0 );
7972 while ( nStart != theLastNode ) {
7973 if ( nStart == theFirstNode )
7974 return !needTheLast;
7976 // find all free border faces sharing form nStart
7978 list< const SMDS_MeshElement* > curElemList;
7979 list< const SMDS_MeshNode* > nStartList;
7980 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7981 while ( invElemIt->more() ) {
7982 const SMDS_MeshElement* e = invElemIt->next();
7983 if ( e == curElem || foundElems.insert( e ).second ) {
7985 int iNode = 0, nbNodes = e->NbNodes();
7986 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7988 if ( e->IsQuadratic() ) {
7989 const SMDS_VtkFace* F =
7990 dynamic_cast<const SMDS_VtkFace*>(e);
7991 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7992 // use special nodes iterator
7993 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7994 while( anIter->more() ) {
7995 nodes[ iNode++ ] = cast2Node(anIter->next());
7999 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8000 while ( nIt->more() )
8001 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8003 nodes[ iNode ] = nodes[ 0 ];
8005 for ( iNode = 0; iNode < nbNodes; iNode++ )
8006 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8007 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8008 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8010 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8011 curElemList.push_back( e );
8015 // analyse the found
8017 int nbNewBorders = curElemList.size();
8018 if ( nbNewBorders == 0 ) {
8019 // no free border furthermore
8020 return !needTheLast;
8022 else if ( nbNewBorders == 1 ) {
8023 // one more element found
8025 nStart = nStartList.front();
8026 curElem = curElemList.front();
8027 theFaces.push_back( curElem );
8028 theNodes.push_back( nStart );
8031 // several continuations found
8032 list< const SMDS_MeshElement* >::iterator curElemIt;
8033 list< const SMDS_MeshNode* >::iterator nStartIt;
8034 // check if one of them reached the last node
8035 if ( needTheLast ) {
8036 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8037 curElemIt!= curElemList.end();
8038 curElemIt++, nStartIt++ )
8039 if ( *nStartIt == theLastNode ) {
8040 theFaces.push_back( *curElemIt );
8041 theNodes.push_back( *nStartIt );
8045 // find the best free border by the continuations
8046 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8047 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8048 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8049 curElemIt!= curElemList.end();
8050 curElemIt++, nStartIt++ )
8052 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8053 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8054 // find one more free border
8055 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8059 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8060 // choice: clear a worse one
8061 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8062 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8063 contNodes[ iWorse ].clear();
8064 contFaces[ iWorse ].clear();
8067 if ( contNodes[0].empty() && contNodes[1].empty() )
8070 // append the best free border
8071 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8072 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8073 theNodes.pop_back(); // remove nIgnore
8074 theNodes.pop_back(); // remove nStart
8075 theFaces.pop_back(); // remove curElem
8076 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8077 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8078 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8079 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8082 } // several continuations found
8083 } // while ( nStart != theLastNode )
8088 //=======================================================================
8089 //function : CheckFreeBorderNodes
8090 //purpose : Return true if the tree nodes are on a free border
8091 //=======================================================================
8093 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8094 const SMDS_MeshNode* theNode2,
8095 const SMDS_MeshNode* theNode3)
8097 list< const SMDS_MeshNode* > nodes;
8098 list< const SMDS_MeshElement* > faces;
8099 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8102 //=======================================================================
8103 //function : SewFreeBorder
8105 //warning : for border-to-side sewing theSideSecondNode is considered as
8106 // the last side node and theSideThirdNode is not used
8107 //=======================================================================
8109 SMESH_MeshEditor::Sew_Error
8110 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8111 const SMDS_MeshNode* theBordSecondNode,
8112 const SMDS_MeshNode* theBordLastNode,
8113 const SMDS_MeshNode* theSideFirstNode,
8114 const SMDS_MeshNode* theSideSecondNode,
8115 const SMDS_MeshNode* theSideThirdNode,
8116 const bool theSideIsFreeBorder,
8117 const bool toCreatePolygons,
8118 const bool toCreatePolyedrs)
8120 myLastCreatedElems.Clear();
8121 myLastCreatedNodes.Clear();
8123 MESSAGE("::SewFreeBorder()");
8124 Sew_Error aResult = SEW_OK;
8126 // ====================================
8127 // find side nodes and elements
8128 // ====================================
8130 list< const SMDS_MeshNode* > nSide[ 2 ];
8131 list< const SMDS_MeshElement* > eSide[ 2 ];
8132 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8133 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8137 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8138 nSide[0], eSide[0])) {
8139 MESSAGE(" Free Border 1 not found " );
8140 aResult = SEW_BORDER1_NOT_FOUND;
8142 if (theSideIsFreeBorder) {
8145 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8146 nSide[1], eSide[1])) {
8147 MESSAGE(" Free Border 2 not found " );
8148 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8151 if ( aResult != SEW_OK )
8154 if (!theSideIsFreeBorder) {
8158 // -------------------------------------------------------------------------
8160 // 1. If nodes to merge are not coincident, move nodes of the free border
8161 // from the coord sys defined by the direction from the first to last
8162 // nodes of the border to the correspondent sys of the side 2
8163 // 2. On the side 2, find the links most co-directed with the correspondent
8164 // links of the free border
8165 // -------------------------------------------------------------------------
8167 // 1. Since sewing may break if there are volumes to split on the side 2,
8168 // we wont move nodes but just compute new coordinates for them
8169 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8170 TNodeXYZMap nBordXYZ;
8171 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8172 list< const SMDS_MeshNode* >::iterator nBordIt;
8174 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8175 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8176 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8177 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8178 double tol2 = 1.e-8;
8179 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8180 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8181 // Need node movement.
8183 // find X and Z axes to create trsf
8184 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8186 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8188 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8191 gp_Ax3 toBordAx( Pb1, Zb, X );
8192 gp_Ax3 fromSideAx( Ps1, Zs, X );
8193 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8195 gp_Trsf toBordSys, fromSide2Sys;
8196 toBordSys.SetTransformation( toBordAx );
8197 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8198 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8201 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8202 const SMDS_MeshNode* n = *nBordIt;
8203 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8204 toBordSys.Transforms( xyz );
8205 fromSide2Sys.Transforms( xyz );
8206 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8210 // just insert nodes XYZ in the nBordXYZ map
8211 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8212 const SMDS_MeshNode* n = *nBordIt;
8213 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8217 // 2. On the side 2, find the links most co-directed with the correspondent
8218 // links of the free border
8220 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8221 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8222 sideNodes.push_back( theSideFirstNode );
8224 bool hasVolumes = false;
8225 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8226 set<long> foundSideLinkIDs, checkedLinkIDs;
8227 SMDS_VolumeTool volume;
8228 //const SMDS_MeshNode* faceNodes[ 4 ];
8230 const SMDS_MeshNode* sideNode;
8231 const SMDS_MeshElement* sideElem;
8232 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8233 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8234 nBordIt = bordNodes.begin();
8236 // border node position and border link direction to compare with
8237 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8238 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8239 // choose next side node by link direction or by closeness to
8240 // the current border node:
8241 bool searchByDir = ( *nBordIt != theBordLastNode );
8243 // find the next node on the Side 2
8245 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8247 checkedLinkIDs.clear();
8248 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8250 // loop on inverse elements of current node (prevSideNode) on the Side 2
8251 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8252 while ( invElemIt->more() )
8254 const SMDS_MeshElement* elem = invElemIt->next();
8255 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8256 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8257 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8258 bool isVolume = volume.Set( elem );
8259 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8260 if ( isVolume ) // --volume
8262 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8263 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8264 if(elem->IsQuadratic()) {
8265 const SMDS_VtkFace* F =
8266 dynamic_cast<const SMDS_VtkFace*>(elem);
8267 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8268 // use special nodes iterator
8269 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8270 while( anIter->more() ) {
8271 nodes[ iNode ] = cast2Node(anIter->next());
8272 if ( nodes[ iNode++ ] == prevSideNode )
8273 iPrevNode = iNode - 1;
8277 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8278 while ( nIt->more() ) {
8279 nodes[ iNode ] = cast2Node( nIt->next() );
8280 if ( nodes[ iNode++ ] == prevSideNode )
8281 iPrevNode = iNode - 1;
8284 // there are 2 links to check
8289 // loop on links, to be precise, on the second node of links
8290 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8291 const SMDS_MeshNode* n = nodes[ iNode ];
8293 if ( !volume.IsLinked( n, prevSideNode ))
8297 if ( iNode ) // a node before prevSideNode
8298 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8299 else // a node after prevSideNode
8300 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8302 // check if this link was already used
8303 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8304 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8305 if (!isJustChecked &&
8306 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8308 // test a link geometrically
8309 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8310 bool linkIsBetter = false;
8311 double dot = 0.0, dist = 0.0;
8312 if ( searchByDir ) { // choose most co-directed link
8313 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8314 linkIsBetter = ( dot > maxDot );
8316 else { // choose link with the node closest to bordPos
8317 dist = ( nextXYZ - bordPos ).SquareModulus();
8318 linkIsBetter = ( dist < minDist );
8320 if ( linkIsBetter ) {
8329 } // loop on inverse elements of prevSideNode
8332 MESSAGE(" Cant find path by links of the Side 2 ");
8333 return SEW_BAD_SIDE_NODES;
8335 sideNodes.push_back( sideNode );
8336 sideElems.push_back( sideElem );
8337 foundSideLinkIDs.insert ( linkID );
8338 prevSideNode = sideNode;
8340 if ( *nBordIt == theBordLastNode )
8341 searchByDir = false;
8343 // find the next border link to compare with
8344 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8345 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8346 // move to next border node if sideNode is before forward border node (bordPos)
8347 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8348 prevBordNode = *nBordIt;
8350 bordPos = nBordXYZ[ *nBordIt ];
8351 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8352 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8356 while ( sideNode != theSideSecondNode );
8358 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8359 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8360 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8362 } // end nodes search on the side 2
8364 // ============================
8365 // sew the border to the side 2
8366 // ============================
8368 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8369 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8371 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8372 if ( toMergeConformal && toCreatePolygons )
8374 // do not merge quadrangles if polygons are OK (IPAL0052824)
8375 eIt[0] = eSide[0].begin();
8376 eIt[1] = eSide[1].begin();
8377 bool allQuads[2] = { true, true };
8378 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8379 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8380 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8382 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8385 TListOfListOfNodes nodeGroupsToMerge;
8386 if (( toMergeConformal ) ||
8387 ( theSideIsFreeBorder && !theSideThirdNode )) {
8389 // all nodes are to be merged
8391 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8392 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8393 nIt[0]++, nIt[1]++ )
8395 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8396 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8397 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8402 // insert new nodes into the border and the side to get equal nb of segments
8404 // get normalized parameters of nodes on the borders
8405 vector< double > param[ 2 ];
8406 param[0].resize( maxNbNodes );
8407 param[1].resize( maxNbNodes );
8409 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8410 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8411 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8412 const SMDS_MeshNode* nPrev = *nIt;
8413 double bordLength = 0;
8414 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8415 const SMDS_MeshNode* nCur = *nIt;
8416 gp_XYZ segment (nCur->X() - nPrev->X(),
8417 nCur->Y() - nPrev->Y(),
8418 nCur->Z() - nPrev->Z());
8419 double segmentLen = segment.Modulus();
8420 bordLength += segmentLen;
8421 param[ iBord ][ iNode ] = bordLength;
8424 // normalize within [0,1]
8425 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8426 param[ iBord ][ iNode ] /= bordLength;
8430 // loop on border segments
8431 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8432 int i[ 2 ] = { 0, 0 };
8433 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8434 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8436 TElemOfNodeListMap insertMap;
8437 TElemOfNodeListMap::iterator insertMapIt;
8439 // key: elem to insert nodes into
8440 // value: 2 nodes to insert between + nodes to be inserted
8442 bool next[ 2 ] = { false, false };
8444 // find min adjacent segment length after sewing
8445 double nextParam = 10., prevParam = 0;
8446 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8447 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8448 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8449 if ( i[ iBord ] > 0 )
8450 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8452 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8453 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8454 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8456 // choose to insert or to merge nodes
8457 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8458 if ( Abs( du ) <= minSegLen * 0.2 ) {
8461 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8462 const SMDS_MeshNode* n0 = *nIt[0];
8463 const SMDS_MeshNode* n1 = *nIt[1];
8464 nodeGroupsToMerge.back().push_back( n1 );
8465 nodeGroupsToMerge.back().push_back( n0 );
8466 // position of node of the border changes due to merge
8467 param[ 0 ][ i[0] ] += du;
8468 // move n1 for the sake of elem shape evaluation during insertion.
8469 // n1 will be removed by MergeNodes() anyway
8470 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8471 next[0] = next[1] = true;
8476 int intoBord = ( du < 0 ) ? 0 : 1;
8477 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8478 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8479 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8480 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8481 if ( intoBord == 1 ) {
8482 // move node of the border to be on a link of elem of the side
8483 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8484 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8485 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8486 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8487 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8489 insertMapIt = insertMap.find( elem );
8490 bool notFound = ( insertMapIt == insertMap.end() );
8491 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8493 // insert into another link of the same element:
8494 // 1. perform insertion into the other link of the elem
8495 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8496 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8497 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8498 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8499 // 2. perform insertion into the link of adjacent faces
8500 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8501 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8503 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8504 InsertNodesIntoLink( seg, n12, n22, nodeList );
8506 if (toCreatePolyedrs) {
8507 // perform insertion into the links of adjacent volumes
8508 UpdateVolumes(n12, n22, nodeList);
8510 // 3. find an element appeared on n1 and n2 after the insertion
8511 insertMap.erase( elem );
8512 elem = findAdjacentFace( n1, n2, 0 );
8514 if ( notFound || otherLink ) {
8515 // add element and nodes of the side into the insertMap
8516 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8517 (*insertMapIt).second.push_back( n1 );
8518 (*insertMapIt).second.push_back( n2 );
8520 // add node to be inserted into elem
8521 (*insertMapIt).second.push_back( nIns );
8522 next[ 1 - intoBord ] = true;
8525 // go to the next segment
8526 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8527 if ( next[ iBord ] ) {
8528 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8530 nPrev[ iBord ] = *nIt[ iBord ];
8531 nIt[ iBord ]++; i[ iBord ]++;
8535 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8537 // perform insertion of nodes into elements
8539 for (insertMapIt = insertMap.begin();
8540 insertMapIt != insertMap.end();
8543 const SMDS_MeshElement* elem = (*insertMapIt).first;
8544 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8545 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8546 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8548 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8550 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8551 InsertNodesIntoLink( seg, n1, n2, nodeList );
8554 if ( !theSideIsFreeBorder ) {
8555 // look for and insert nodes into the faces adjacent to elem
8556 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8557 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8560 if (toCreatePolyedrs) {
8561 // perform insertion into the links of adjacent volumes
8562 UpdateVolumes(n1, n2, nodeList);
8565 } // end: insert new nodes
8567 MergeNodes ( nodeGroupsToMerge );
8570 // Remove coincident segments
8573 TIDSortedElemSet segments;
8574 SMESH_SequenceOfElemPtr newFaces;
8575 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8577 if ( !myLastCreatedElems(i) ) continue;
8578 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8579 segments.insert( segments.end(), myLastCreatedElems(i) );
8581 newFaces.Append( myLastCreatedElems(i) );
8583 // get segments adjacent to merged nodes
8584 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8585 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8587 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8588 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8589 while ( segIt->more() )
8590 segments.insert( segIt->next() );
8594 TListOfListOfElementsID equalGroups;
8595 if ( !segments.empty() )
8596 FindEqualElements( segments, equalGroups );
8597 if ( !equalGroups.empty() )
8599 // remove from segments those that will be removed
8600 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8601 for ( ; itGroups != equalGroups.end(); ++itGroups )
8603 list< int >& group = *itGroups;
8604 list< int >::iterator id = group.begin();
8605 for ( ++id; id != group.end(); ++id )
8606 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8607 segments.erase( seg );
8609 // remove equal segments
8610 MergeElements( equalGroups );
8612 // restore myLastCreatedElems
8613 myLastCreatedElems = newFaces;
8614 TIDSortedElemSet::iterator seg = segments.begin();
8615 for ( ; seg != segments.end(); ++seg )
8616 myLastCreatedElems.Append( *seg );
8622 //=======================================================================
8623 //function : InsertNodesIntoLink
8624 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8625 // and theBetweenNode2 and split theElement
8626 //=======================================================================
8628 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8629 const SMDS_MeshNode* theBetweenNode1,
8630 const SMDS_MeshNode* theBetweenNode2,
8631 list<const SMDS_MeshNode*>& theNodesToInsert,
8632 const bool toCreatePoly)
8634 if ( !theElement ) return;
8636 SMESHDS_Mesh *aMesh = GetMeshDS();
8637 vector<const SMDS_MeshElement*> newElems;
8639 if ( theElement->GetType() == SMDSAbs_Edge )
8641 theNodesToInsert.push_front( theBetweenNode1 );
8642 theNodesToInsert.push_back ( theBetweenNode2 );
8643 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8644 const SMDS_MeshNode* n1 = *n;
8645 for ( ++n; n != theNodesToInsert.end(); ++n )
8647 const SMDS_MeshNode* n2 = *n;
8648 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8649 AddToSameGroups( seg, theElement, aMesh );
8651 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8654 theNodesToInsert.pop_front();
8655 theNodesToInsert.pop_back();
8657 if ( theElement->IsQuadratic() ) // add a not split part
8659 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8660 theElement->end_nodes() );
8661 int iOther = 0, nbN = nodes.size();
8662 for ( ; iOther < nbN; ++iOther )
8663 if ( nodes[iOther] != theBetweenNode1 &&
8664 nodes[iOther] != theBetweenNode2 )
8668 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8669 AddToSameGroups( seg, theElement, aMesh );
8671 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8673 else if ( iOther == 2 )
8675 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8676 AddToSameGroups( seg, theElement, aMesh );
8678 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8681 // treat new elements
8682 for ( size_t i = 0; i < newElems.size(); ++i )
8685 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8686 myLastCreatedElems.Append( newElems[i] );
8688 ReplaceElemInGroups( theElement, newElems, aMesh );
8689 aMesh->RemoveElement( theElement );
8692 } // if ( theElement->GetType() == SMDSAbs_Edge )
8694 const SMDS_MeshElement* theFace = theElement;
8695 if ( theFace->GetType() != SMDSAbs_Face ) return;
8697 // find indices of 2 link nodes and of the rest nodes
8698 int iNode = 0, il1, il2, i3, i4;
8699 il1 = il2 = i3 = i4 = -1;
8700 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8702 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8703 while ( nodeIt->more() ) {
8704 const SMDS_MeshNode* n = nodeIt->next();
8705 if ( n == theBetweenNode1 )
8707 else if ( n == theBetweenNode2 )
8713 nodes[ iNode++ ] = n;
8715 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8718 // arrange link nodes to go one after another regarding the face orientation
8719 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8720 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8725 aNodesToInsert.reverse();
8727 // check that not link nodes of a quadrangles are in good order
8728 int nbFaceNodes = theFace->NbNodes();
8729 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8735 if (toCreatePoly || theFace->IsPoly()) {
8738 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8740 // add nodes of face up to first node of link
8743 if ( theFace->IsQuadratic() ) {
8744 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8745 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8746 // use special nodes iterator
8747 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8748 while( anIter->more() && !isFLN ) {
8749 const SMDS_MeshNode* n = cast2Node(anIter->next());
8750 poly_nodes[iNode++] = n;
8751 if (n == nodes[il1]) {
8755 // add nodes to insert
8756 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8757 for (; nIt != aNodesToInsert.end(); nIt++) {
8758 poly_nodes[iNode++] = *nIt;
8760 // add nodes of face starting from last node of link
8761 while ( anIter->more() ) {
8762 poly_nodes[iNode++] = cast2Node(anIter->next());
8766 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8767 while ( nodeIt->more() && !isFLN ) {
8768 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8769 poly_nodes[iNode++] = n;
8770 if (n == nodes[il1]) {
8774 // add nodes to insert
8775 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8776 for (; nIt != aNodesToInsert.end(); nIt++) {
8777 poly_nodes[iNode++] = *nIt;
8779 // add nodes of face starting from last node of link
8780 while ( nodeIt->more() ) {
8781 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8782 poly_nodes[iNode++] = n;
8787 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8790 else if ( !theFace->IsQuadratic() )
8792 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8793 int nbLinkNodes = 2 + aNodesToInsert.size();
8794 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8795 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8796 linkNodes[ 0 ] = nodes[ il1 ];
8797 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8798 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8799 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8800 linkNodes[ iNode++ ] = *nIt;
8802 // decide how to split a quadrangle: compare possible variants
8803 // and choose which of splits to be a quadrangle
8804 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8805 if ( nbFaceNodes == 3 ) {
8806 iBestQuad = nbSplits;
8809 else if ( nbFaceNodes == 4 ) {
8810 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8811 double aBestRate = DBL_MAX;
8812 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8814 double aBadRate = 0;
8815 // evaluate elements quality
8816 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8817 if ( iSplit == iQuad ) {
8818 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8822 aBadRate += getBadRate( &quad, aCrit );
8825 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8827 nodes[ iSplit < iQuad ? i4 : i3 ]);
8828 aBadRate += getBadRate( &tria, aCrit );
8832 if ( aBadRate < aBestRate ) {
8834 aBestRate = aBadRate;
8839 // create new elements
8841 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8843 if ( iSplit == iBestQuad )
8844 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8849 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8851 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8854 const SMDS_MeshNode* newNodes[ 4 ];
8855 newNodes[ 0 ] = linkNodes[ i1 ];
8856 newNodes[ 1 ] = linkNodes[ i2 ];
8857 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8858 newNodes[ 3 ] = nodes[ i4 ];
8859 if (iSplit == iBestQuad)
8860 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8862 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8864 } // end if(!theFace->IsQuadratic())
8866 else { // theFace is quadratic
8867 // we have to split theFace on simple triangles and one simple quadrangle
8869 int nbshift = tmp*2;
8870 // shift nodes in nodes[] by nbshift
8872 for(i=0; i<nbshift; i++) {
8873 const SMDS_MeshNode* n = nodes[0];
8874 for(j=0; j<nbFaceNodes-1; j++) {
8875 nodes[j] = nodes[j+1];
8877 nodes[nbFaceNodes-1] = n;
8879 il1 = il1 - nbshift;
8880 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8881 // n0 n1 n2 n0 n1 n2
8882 // +-----+-----+ +-----+-----+
8891 // create new elements
8893 if ( nbFaceNodes == 6 ) { // quadratic triangle
8894 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8895 if ( theFace->IsMediumNode(nodes[il1]) ) {
8896 // create quadrangle
8897 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8903 // create quadrangle
8904 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8910 else { // nbFaceNodes==8 - quadratic quadrangle
8911 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8912 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8913 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8914 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8915 // create quadrangle
8916 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8922 // create quadrangle
8923 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8929 // create needed triangles using n1,n2,n3 and inserted nodes
8930 int nbn = 2 + aNodesToInsert.size();
8931 vector<const SMDS_MeshNode*> aNodes(nbn);
8932 aNodes[0 ] = nodes[n1];
8933 aNodes[nbn-1] = nodes[n2];
8934 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8935 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8936 aNodes[iNode++] = *nIt;
8938 for ( i = 1; i < nbn; i++ )
8939 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8942 // remove the old face
8943 for ( size_t i = 0; i < newElems.size(); ++i )
8946 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8947 myLastCreatedElems.Append( newElems[i] );
8949 ReplaceElemInGroups( theFace, newElems, aMesh );
8950 aMesh->RemoveElement(theFace);
8952 } // InsertNodesIntoLink()
8954 //=======================================================================
8955 //function : UpdateVolumes
8957 //=======================================================================
8959 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8960 const SMDS_MeshNode* theBetweenNode2,
8961 list<const SMDS_MeshNode*>& theNodesToInsert)
8963 myLastCreatedElems.Clear();
8964 myLastCreatedNodes.Clear();
8966 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8967 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8968 const SMDS_MeshElement* elem = invElemIt->next();
8970 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8971 SMDS_VolumeTool aVolume (elem);
8972 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8975 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8976 int iface, nbFaces = aVolume.NbFaces();
8977 vector<const SMDS_MeshNode *> poly_nodes;
8978 vector<int> quantities (nbFaces);
8980 for (iface = 0; iface < nbFaces; iface++) {
8981 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8982 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8983 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8985 for (int inode = 0; inode < nbFaceNodes; inode++) {
8986 poly_nodes.push_back(faceNodes[inode]);
8988 if (nbInserted == 0) {
8989 if (faceNodes[inode] == theBetweenNode1) {
8990 if (faceNodes[inode + 1] == theBetweenNode2) {
8991 nbInserted = theNodesToInsert.size();
8993 // add nodes to insert
8994 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8995 for (; nIt != theNodesToInsert.end(); nIt++) {
8996 poly_nodes.push_back(*nIt);
9000 else if (faceNodes[inode] == theBetweenNode2) {
9001 if (faceNodes[inode + 1] == theBetweenNode1) {
9002 nbInserted = theNodesToInsert.size();
9004 // add nodes to insert in reversed order
9005 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9007 for (; nIt != theNodesToInsert.begin(); nIt--) {
9008 poly_nodes.push_back(*nIt);
9010 poly_nodes.push_back(*nIt);
9017 quantities[iface] = nbFaceNodes + nbInserted;
9020 // Replace the volume
9021 SMESHDS_Mesh *aMesh = GetMeshDS();
9023 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9025 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9026 myLastCreatedElems.Append( newElem );
9027 ReplaceElemInGroups( elem, newElem, aMesh );
9029 aMesh->RemoveElement( elem );
9035 //================================================================================
9037 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9039 //================================================================================
9041 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9042 vector<const SMDS_MeshNode *> & nodes,
9043 vector<int> & nbNodeInFaces )
9046 nbNodeInFaces.clear();
9047 SMDS_VolumeTool vTool ( elem );
9048 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9050 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9051 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9052 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9057 //=======================================================================
9059 * \brief Convert elements contained in a sub-mesh to quadratic
9060 * \return int - nb of checked elements
9062 //=======================================================================
9064 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9065 SMESH_MesherHelper& theHelper,
9066 const bool theForce3d)
9069 if( !theSm ) return nbElem;
9071 vector<int> nbNodeInFaces;
9072 vector<const SMDS_MeshNode *> nodes;
9073 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9074 while(ElemItr->more())
9077 const SMDS_MeshElement* elem = ElemItr->next();
9078 if( !elem ) continue;
9080 // analyse a necessity of conversion
9081 const SMDSAbs_ElementType aType = elem->GetType();
9082 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9084 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9085 bool hasCentralNodes = false;
9086 if ( elem->IsQuadratic() )
9089 switch ( aGeomType ) {
9090 case SMDSEntity_Quad_Triangle:
9091 case SMDSEntity_Quad_Quadrangle:
9092 case SMDSEntity_Quad_Hexa:
9093 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9095 case SMDSEntity_BiQuad_Triangle:
9096 case SMDSEntity_BiQuad_Quadrangle:
9097 case SMDSEntity_TriQuad_Hexa:
9098 alreadyOK = theHelper.GetIsBiQuadratic();
9099 hasCentralNodes = true;
9104 // take into account already present modium nodes
9106 case SMDSAbs_Volume:
9107 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9109 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9111 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9117 // get elem data needed to re-create it
9119 const int id = elem->GetID();
9120 const int nbNodes = elem->NbCornerNodes();
9121 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9122 if ( aGeomType == SMDSEntity_Polyhedra )
9123 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9124 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9125 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9127 // remove a linear element
9128 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9130 // remove central nodes of biquadratic elements (biquad->quad convertion)
9131 if ( hasCentralNodes )
9132 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9133 if ( nodes[i]->NbInverseElements() == 0 )
9134 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9136 const SMDS_MeshElement* NewElem = 0;
9142 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9150 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9153 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9156 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9160 case SMDSAbs_Volume :
9164 case SMDSEntity_Tetra:
9165 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9167 case SMDSEntity_Pyramid:
9168 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9170 case SMDSEntity_Penta:
9171 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9173 case SMDSEntity_Hexa:
9174 case SMDSEntity_Quad_Hexa:
9175 case SMDSEntity_TriQuad_Hexa:
9176 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9177 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9179 case SMDSEntity_Hexagonal_Prism:
9181 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9188 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9189 if( NewElem && NewElem->getshapeId() < 1 )
9190 theSm->AddElement( NewElem );
9194 //=======================================================================
9195 //function : ConvertToQuadratic
9197 //=======================================================================
9199 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9201 SMESHDS_Mesh* meshDS = GetMeshDS();
9203 SMESH_MesherHelper aHelper(*myMesh);
9205 aHelper.SetIsQuadratic( true );
9206 aHelper.SetIsBiQuadratic( theToBiQuad );
9207 aHelper.SetElementsOnShape(true);
9208 aHelper.ToFixNodeParameters( true );
9210 // convert elements assigned to sub-meshes
9211 int nbCheckedElems = 0;
9212 if ( myMesh->HasShapeToMesh() )
9214 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9216 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9217 while ( smIt->more() ) {
9218 SMESH_subMesh* sm = smIt->next();
9219 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9220 aHelper.SetSubShape( sm->GetSubShape() );
9221 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9227 // convert elements NOT assigned to sub-meshes
9228 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9229 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9231 aHelper.SetElementsOnShape(false);
9232 SMESHDS_SubMesh *smDS = 0;
9235 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9236 while( aEdgeItr->more() )
9238 const SMDS_MeshEdge* edge = aEdgeItr->next();
9239 if ( !edge->IsQuadratic() )
9241 int id = edge->GetID();
9242 const SMDS_MeshNode* n1 = edge->GetNode(0);
9243 const SMDS_MeshNode* n2 = edge->GetNode(1);
9245 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9247 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9248 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9252 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9257 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9258 while( aFaceItr->more() )
9260 const SMDS_MeshFace* face = aFaceItr->next();
9261 if ( !face ) continue;
9263 const SMDSAbs_EntityType type = face->GetEntityType();
9267 case SMDSEntity_Quad_Triangle:
9268 case SMDSEntity_Quad_Quadrangle:
9269 alreadyOK = !theToBiQuad;
9270 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9272 case SMDSEntity_BiQuad_Triangle:
9273 case SMDSEntity_BiQuad_Quadrangle:
9274 alreadyOK = theToBiQuad;
9275 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9277 default: alreadyOK = false;
9282 const int id = face->GetID();
9283 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9285 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9287 SMDS_MeshFace * NewFace = 0;
9290 case SMDSEntity_Triangle:
9291 case SMDSEntity_Quad_Triangle:
9292 case SMDSEntity_BiQuad_Triangle:
9293 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9294 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9295 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9298 case SMDSEntity_Quadrangle:
9299 case SMDSEntity_Quad_Quadrangle:
9300 case SMDSEntity_BiQuad_Quadrangle:
9301 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9302 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9303 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9307 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9309 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9313 vector<int> nbNodeInFaces;
9314 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9315 while(aVolumeItr->more())
9317 const SMDS_MeshVolume* volume = aVolumeItr->next();
9318 if ( !volume ) continue;
9320 const SMDSAbs_EntityType type = volume->GetEntityType();
9321 if ( volume->IsQuadratic() )
9326 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9327 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9328 default: alreadyOK = true;
9332 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9336 const int id = volume->GetID();
9337 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9338 if ( type == SMDSEntity_Polyhedra )
9339 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9340 else if ( type == SMDSEntity_Hexagonal_Prism )
9341 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9343 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9345 SMDS_MeshVolume * NewVolume = 0;
9348 case SMDSEntity_Tetra:
9349 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9351 case SMDSEntity_Hexa:
9352 case SMDSEntity_Quad_Hexa:
9353 case SMDSEntity_TriQuad_Hexa:
9354 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9355 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9356 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9357 if ( nodes[i]->NbInverseElements() == 0 )
9358 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9360 case SMDSEntity_Pyramid:
9361 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9362 nodes[3], nodes[4], id, theForce3d);
9364 case SMDSEntity_Penta:
9365 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9366 nodes[3], nodes[4], nodes[5], id, theForce3d);
9368 case SMDSEntity_Hexagonal_Prism:
9370 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9372 ReplaceElemInGroups(volume, NewVolume, meshDS);
9377 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9378 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9379 // aHelper.FixQuadraticElements(myError);
9380 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9384 //================================================================================
9386 * \brief Makes given elements quadratic
9387 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9388 * \param theElements - elements to make quadratic
9390 //================================================================================
9392 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9393 TIDSortedElemSet& theElements,
9394 const bool theToBiQuad)
9396 if ( theElements.empty() ) return;
9398 // we believe that all theElements are of the same type
9399 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9401 // get all nodes shared by theElements
9402 TIDSortedNodeSet allNodes;
9403 TIDSortedElemSet::iterator eIt = theElements.begin();
9404 for ( ; eIt != theElements.end(); ++eIt )
9405 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9407 // complete theElements with elements of lower dim whose all nodes are in allNodes
9409 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9410 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9411 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9412 for ( ; nIt != allNodes.end(); ++nIt )
9414 const SMDS_MeshNode* n = *nIt;
9415 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9416 while ( invIt->more() )
9418 const SMDS_MeshElement* e = invIt->next();
9419 const SMDSAbs_ElementType type = e->GetType();
9420 if ( e->IsQuadratic() )
9422 quadAdjacentElems[ type ].insert( e );
9425 switch ( e->GetEntityType() ) {
9426 case SMDSEntity_Quad_Triangle:
9427 case SMDSEntity_Quad_Quadrangle:
9428 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9429 case SMDSEntity_BiQuad_Triangle:
9430 case SMDSEntity_BiQuad_Quadrangle:
9431 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9432 default: alreadyOK = true;
9437 if ( type >= elemType )
9438 continue; // same type or more complex linear element
9440 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9441 continue; // e is already checked
9445 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9446 while ( nodeIt->more() && allIn )
9447 allIn = allNodes.count( nodeIt->next() );
9449 theElements.insert(e );
9453 SMESH_MesherHelper helper(*myMesh);
9454 helper.SetIsQuadratic( true );
9455 helper.SetIsBiQuadratic( theToBiQuad );
9457 // add links of quadratic adjacent elements to the helper
9459 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9460 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9461 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9463 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9465 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9466 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9467 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9469 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9471 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9472 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9473 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9475 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9478 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9480 SMESHDS_Mesh* meshDS = GetMeshDS();
9481 SMESHDS_SubMesh* smDS = 0;
9482 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9484 const SMDS_MeshElement* elem = *eIt;
9487 int nbCentralNodes = 0;
9488 switch ( elem->GetEntityType() ) {
9489 // linear convertible
9490 case SMDSEntity_Edge:
9491 case SMDSEntity_Triangle:
9492 case SMDSEntity_Quadrangle:
9493 case SMDSEntity_Tetra:
9494 case SMDSEntity_Pyramid:
9495 case SMDSEntity_Hexa:
9496 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9497 // quadratic that can become bi-quadratic
9498 case SMDSEntity_Quad_Triangle:
9499 case SMDSEntity_Quad_Quadrangle:
9500 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9502 case SMDSEntity_BiQuad_Triangle:
9503 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9504 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9506 default: alreadyOK = true;
9508 if ( alreadyOK ) continue;
9510 const SMDSAbs_ElementType type = elem->GetType();
9511 const int id = elem->GetID();
9512 const int nbNodes = elem->NbCornerNodes();
9513 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9515 helper.SetSubShape( elem->getshapeId() );
9517 if ( !smDS || !smDS->Contains( elem ))
9518 smDS = meshDS->MeshElements( elem->getshapeId() );
9519 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9521 SMDS_MeshElement * newElem = 0;
9524 case 4: // cases for most frequently used element types go first (for optimization)
9525 if ( type == SMDSAbs_Volume )
9526 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9528 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9531 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9532 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9535 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9538 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9541 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9542 nodes[4], id, theForce3d);
9545 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9546 nodes[4], nodes[5], id, theForce3d);
9550 ReplaceElemInGroups( elem, newElem, meshDS);
9551 if( newElem && smDS )
9552 smDS->AddElement( newElem );
9554 // remove central nodes
9555 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9556 if ( nodes[i]->NbInverseElements() == 0 )
9557 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9559 } // loop on theElements
9562 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9563 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9564 // helper.FixQuadraticElements( myError );
9565 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9569 //=======================================================================
9571 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9572 * \return int - nb of checked elements
9574 //=======================================================================
9576 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9577 SMDS_ElemIteratorPtr theItr,
9578 const int theShapeID)
9581 SMESHDS_Mesh* meshDS = GetMeshDS();
9582 ElemFeatures elemType;
9583 vector<const SMDS_MeshNode *> nodes;
9585 while( theItr->more() )
9587 const SMDS_MeshElement* elem = theItr->next();
9589 if( elem && elem->IsQuadratic())
9592 int nbCornerNodes = elem->NbCornerNodes();
9593 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9595 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9597 //remove a quadratic element
9598 if ( !theSm || !theSm->Contains( elem ))
9599 theSm = meshDS->MeshElements( elem->getshapeId() );
9600 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9602 // remove medium nodes
9603 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9604 if ( nodes[i]->NbInverseElements() == 0 )
9605 meshDS->RemoveFreeNode( nodes[i], theSm );
9607 // add a linear element
9608 nodes.resize( nbCornerNodes );
9609 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9610 ReplaceElemInGroups(elem, newElem, meshDS);
9611 if( theSm && newElem )
9612 theSm->AddElement( newElem );
9618 //=======================================================================
9619 //function : ConvertFromQuadratic
9621 //=======================================================================
9623 bool SMESH_MeshEditor::ConvertFromQuadratic()
9625 int nbCheckedElems = 0;
9626 if ( myMesh->HasShapeToMesh() )
9628 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9630 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9631 while ( smIt->more() ) {
9632 SMESH_subMesh* sm = smIt->next();
9633 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9634 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9640 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9641 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9643 SMESHDS_SubMesh *aSM = 0;
9644 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9652 //================================================================================
9654 * \brief Return true if all medium nodes of the element are in the node set
9656 //================================================================================
9658 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9660 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9661 if ( !nodeSet.count( elem->GetNode(i) ))
9667 //================================================================================
9669 * \brief Makes given elements linear
9671 //================================================================================
9673 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9675 if ( theElements.empty() ) return;
9677 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9678 set<int> mediumNodeIDs;
9679 TIDSortedElemSet::iterator eIt = theElements.begin();
9680 for ( ; eIt != theElements.end(); ++eIt )
9682 const SMDS_MeshElement* e = *eIt;
9683 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9684 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9687 // replace given elements by linear ones
9688 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9689 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9691 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9692 // except those elements sharing medium nodes of quadratic element whose medium nodes
9693 // are not all in mediumNodeIDs
9695 // get remaining medium nodes
9696 TIDSortedNodeSet mediumNodes;
9697 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9698 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9699 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9700 mediumNodes.insert( mediumNodes.end(), n );
9702 // find more quadratic elements to convert
9703 TIDSortedElemSet moreElemsToConvert;
9704 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9705 for ( ; nIt != mediumNodes.end(); ++nIt )
9707 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9708 while ( invIt->more() )
9710 const SMDS_MeshElement* e = invIt->next();
9711 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9713 // find a more complex element including e and
9714 // whose medium nodes are not in mediumNodes
9715 bool complexFound = false;
9716 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9718 SMDS_ElemIteratorPtr invIt2 =
9719 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9720 while ( invIt2->more() )
9722 const SMDS_MeshElement* eComplex = invIt2->next();
9723 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9725 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9726 if ( nbCommonNodes == e->NbNodes())
9728 complexFound = true;
9729 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9735 if ( !complexFound )
9736 moreElemsToConvert.insert( e );
9740 elemIt = elemSetIterator( moreElemsToConvert );
9741 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9744 //=======================================================================
9745 //function : SewSideElements
9747 //=======================================================================
9749 SMESH_MeshEditor::Sew_Error
9750 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9751 TIDSortedElemSet& theSide2,
9752 const SMDS_MeshNode* theFirstNode1,
9753 const SMDS_MeshNode* theFirstNode2,
9754 const SMDS_MeshNode* theSecondNode1,
9755 const SMDS_MeshNode* theSecondNode2)
9757 myLastCreatedElems.Clear();
9758 myLastCreatedNodes.Clear();
9760 MESSAGE ("::::SewSideElements()");
9761 if ( theSide1.size() != theSide2.size() )
9762 return SEW_DIFF_NB_OF_ELEMENTS;
9764 Sew_Error aResult = SEW_OK;
9766 // 1. Build set of faces representing each side
9767 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9768 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9770 // =======================================================================
9771 // 1. Build set of faces representing each side:
9772 // =======================================================================
9773 // a. build set of nodes belonging to faces
9774 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9775 // c. create temporary faces representing side of volumes if correspondent
9776 // face does not exist
9778 SMESHDS_Mesh* aMesh = GetMeshDS();
9779 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9780 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9781 TIDSortedElemSet faceSet1, faceSet2;
9782 set<const SMDS_MeshElement*> volSet1, volSet2;
9783 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9784 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9785 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9786 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9787 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9788 int iSide, iFace, iNode;
9790 list<const SMDS_MeshElement* > tempFaceList;
9791 for ( iSide = 0; iSide < 2; iSide++ ) {
9792 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9793 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9794 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9795 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9796 set<const SMDS_MeshElement*>::iterator vIt;
9797 TIDSortedElemSet::iterator eIt;
9798 set<const SMDS_MeshNode*>::iterator nIt;
9800 // check that given nodes belong to given elements
9801 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9802 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9803 int firstIndex = -1, secondIndex = -1;
9804 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9805 const SMDS_MeshElement* elem = *eIt;
9806 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9807 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9808 if ( firstIndex > -1 && secondIndex > -1 ) break;
9810 if ( firstIndex < 0 || secondIndex < 0 ) {
9811 // we can simply return until temporary faces created
9812 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9815 // -----------------------------------------------------------
9816 // 1a. Collect nodes of existing faces
9817 // and build set of face nodes in order to detect missing
9818 // faces corresponding to sides of volumes
9819 // -----------------------------------------------------------
9821 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9823 // loop on the given element of a side
9824 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9825 //const SMDS_MeshElement* elem = *eIt;
9826 const SMDS_MeshElement* elem = *eIt;
9827 if ( elem->GetType() == SMDSAbs_Face ) {
9828 faceSet->insert( elem );
9829 set <const SMDS_MeshNode*> faceNodeSet;
9830 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9831 while ( nodeIt->more() ) {
9832 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9833 nodeSet->insert( n );
9834 faceNodeSet.insert( n );
9836 setOfFaceNodeSet.insert( faceNodeSet );
9838 else if ( elem->GetType() == SMDSAbs_Volume )
9839 volSet->insert( elem );
9841 // ------------------------------------------------------------------------------
9842 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9843 // ------------------------------------------------------------------------------
9845 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9846 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9847 while ( fIt->more() ) { // loop on faces sharing a node
9848 const SMDS_MeshElement* f = fIt->next();
9849 if ( faceSet->find( f ) == faceSet->end() ) {
9850 // check if all nodes are in nodeSet and
9851 // complete setOfFaceNodeSet if they are
9852 set <const SMDS_MeshNode*> faceNodeSet;
9853 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9854 bool allInSet = true;
9855 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9856 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9857 if ( nodeSet->find( n ) == nodeSet->end() )
9860 faceNodeSet.insert( n );
9863 faceSet->insert( f );
9864 setOfFaceNodeSet.insert( faceNodeSet );
9870 // -------------------------------------------------------------------------
9871 // 1c. Create temporary faces representing sides of volumes if correspondent
9872 // face does not exist
9873 // -------------------------------------------------------------------------
9875 if ( !volSet->empty() ) {
9876 //int nodeSetSize = nodeSet->size();
9878 // loop on given volumes
9879 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9880 SMDS_VolumeTool vol (*vIt);
9881 // loop on volume faces: find free faces
9882 // --------------------------------------
9883 list<const SMDS_MeshElement* > freeFaceList;
9884 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9885 if ( !vol.IsFreeFace( iFace ))
9887 // check if there is already a face with same nodes in a face set
9888 const SMDS_MeshElement* aFreeFace = 0;
9889 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9890 int nbNodes = vol.NbFaceNodes( iFace );
9891 set <const SMDS_MeshNode*> faceNodeSet;
9892 vol.GetFaceNodes( iFace, faceNodeSet );
9893 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9895 // no such a face is given but it still can exist, check it
9896 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9897 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9900 // create a temporary face
9901 if ( nbNodes == 3 ) {
9902 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9903 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9905 else if ( nbNodes == 4 ) {
9906 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9907 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9910 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9911 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9912 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9915 tempFaceList.push_back( aFreeFace );
9919 freeFaceList.push_back( aFreeFace );
9921 } // loop on faces of a volume
9923 // choose one of several free faces of a volume
9924 // --------------------------------------------
9925 if ( freeFaceList.size() > 1 ) {
9926 // choose a face having max nb of nodes shared by other elems of a side
9927 int maxNbNodes = -1;
9928 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9929 while ( fIt != freeFaceList.end() ) { // loop on free faces
9930 int nbSharedNodes = 0;
9931 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9932 while ( nodeIt->more() ) { // loop on free face nodes
9933 const SMDS_MeshNode* n =
9934 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9935 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9936 while ( invElemIt->more() ) {
9937 const SMDS_MeshElement* e = invElemIt->next();
9938 nbSharedNodes += faceSet->count( e );
9939 nbSharedNodes += elemSet->count( e );
9942 if ( nbSharedNodes > maxNbNodes ) {
9943 maxNbNodes = nbSharedNodes;
9944 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9946 else if ( nbSharedNodes == maxNbNodes ) {
9950 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9953 if ( freeFaceList.size() > 1 )
9955 // could not choose one face, use another way
9956 // choose a face most close to the bary center of the opposite side
9957 gp_XYZ aBC( 0., 0., 0. );
9958 set <const SMDS_MeshNode*> addedNodes;
9959 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9960 eIt = elemSet2->begin();
9961 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9962 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9963 while ( nodeIt->more() ) { // loop on free face nodes
9964 const SMDS_MeshNode* n =
9965 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9966 if ( addedNodes.insert( n ).second )
9967 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9970 aBC /= addedNodes.size();
9971 double minDist = DBL_MAX;
9972 fIt = freeFaceList.begin();
9973 while ( fIt != freeFaceList.end() ) { // loop on free faces
9975 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9976 while ( nodeIt->more() ) { // loop on free face nodes
9977 const SMDS_MeshNode* n =
9978 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9979 gp_XYZ p( n->X(),n->Y(),n->Z() );
9980 dist += ( aBC - p ).SquareModulus();
9982 if ( dist < minDist ) {
9984 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9987 fIt = freeFaceList.erase( fIt++ );
9990 } // choose one of several free faces of a volume
9992 if ( freeFaceList.size() == 1 ) {
9993 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9994 faceSet->insert( aFreeFace );
9995 // complete a node set with nodes of a found free face
9996 // for ( iNode = 0; iNode < ; iNode++ )
9997 // nodeSet->insert( fNodes[ iNode ] );
10000 } // loop on volumes of a side
10002 // // complete a set of faces if new nodes in a nodeSet appeared
10003 // // ----------------------------------------------------------
10004 // if ( nodeSetSize != nodeSet->size() ) {
10005 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10006 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10007 // while ( fIt->more() ) { // loop on faces sharing a node
10008 // const SMDS_MeshElement* f = fIt->next();
10009 // if ( faceSet->find( f ) == faceSet->end() ) {
10010 // // check if all nodes are in nodeSet and
10011 // // complete setOfFaceNodeSet if they are
10012 // set <const SMDS_MeshNode*> faceNodeSet;
10013 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10014 // bool allInSet = true;
10015 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10016 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10017 // if ( nodeSet->find( n ) == nodeSet->end() )
10018 // allInSet = false;
10020 // faceNodeSet.insert( n );
10022 // if ( allInSet ) {
10023 // faceSet->insert( f );
10024 // setOfFaceNodeSet.insert( faceNodeSet );
10030 } // Create temporary faces, if there are volumes given
10033 if ( faceSet1.size() != faceSet2.size() ) {
10034 // delete temporary faces: they are in reverseElements of actual nodes
10035 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10036 // while ( tmpFaceIt->more() )
10037 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10038 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10039 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10040 // aMesh->RemoveElement(*tmpFaceIt);
10041 MESSAGE("Diff nb of faces");
10042 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10045 // ============================================================
10046 // 2. Find nodes to merge:
10047 // bind a node to remove to a node to put instead
10048 // ============================================================
10050 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10051 if ( theFirstNode1 != theFirstNode2 )
10052 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10053 if ( theSecondNode1 != theSecondNode2 )
10054 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10056 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10057 set< long > linkIdSet; // links to process
10058 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10060 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10061 list< NLink > linkList[2];
10062 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10063 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10064 // loop on links in linkList; find faces by links and append links
10065 // of the found faces to linkList
10066 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10067 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10069 NLink link[] = { *linkIt[0], *linkIt[1] };
10070 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10071 if ( !linkIdSet.count( linkID ) )
10074 // by links, find faces in the face sets,
10075 // and find indices of link nodes in the found faces;
10076 // in a face set, there is only one or no face sharing a link
10077 // ---------------------------------------------------------------
10079 const SMDS_MeshElement* face[] = { 0, 0 };
10080 vector<const SMDS_MeshNode*> fnodes[2];
10081 int iLinkNode[2][2];
10082 TIDSortedElemSet avoidSet;
10083 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10084 const SMDS_MeshNode* n1 = link[iSide].first;
10085 const SMDS_MeshNode* n2 = link[iSide].second;
10086 //cout << "Side " << iSide << " ";
10087 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10088 // find a face by two link nodes
10089 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10090 *faceSetPtr[ iSide ], avoidSet,
10091 &iLinkNode[iSide][0],
10092 &iLinkNode[iSide][1] );
10093 if ( face[ iSide ])
10095 //cout << " F " << face[ iSide]->GetID() <<endl;
10096 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10097 // put face nodes to fnodes
10098 if ( face[ iSide ]->IsQuadratic() )
10100 // use interlaced nodes iterator
10101 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10102 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10103 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10104 while ( nIter->more() )
10105 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10109 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10110 face[ iSide ]->end_nodes() );
10112 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10116 // check similarity of elements of the sides
10117 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10118 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10119 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10120 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10123 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10125 break; // do not return because it's necessary to remove tmp faces
10128 // set nodes to merge
10129 // -------------------
10131 if ( face[0] && face[1] ) {
10132 const int nbNodes = face[0]->NbNodes();
10133 if ( nbNodes != face[1]->NbNodes() ) {
10134 MESSAGE("Diff nb of face nodes");
10135 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10136 break; // do not return because it s necessary to remove tmp faces
10138 bool reverse[] = { false, false }; // order of nodes in the link
10139 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10140 // analyse link orientation in faces
10141 int i1 = iLinkNode[ iSide ][ 0 ];
10142 int i2 = iLinkNode[ iSide ][ 1 ];
10143 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10145 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10146 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10147 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10149 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10150 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10153 // add other links of the faces to linkList
10154 // -----------------------------------------
10156 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10157 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10158 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10159 if ( !iter_isnew.second ) { // already in a set: no need to process
10160 linkIdSet.erase( iter_isnew.first );
10162 else // new in set == encountered for the first time: add
10164 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10165 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10166 linkList[0].push_back ( NLink( n1, n2 ));
10167 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10172 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10175 } // loop on link lists
10177 if ( aResult == SEW_OK &&
10178 ( //linkIt[0] != linkList[0].end() ||
10179 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10180 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10181 " " << (faceSetPtr[1]->empty()));
10182 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10185 // ====================================================================
10186 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10187 // ====================================================================
10189 // delete temporary faces
10190 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10191 // while ( tmpFaceIt->more() )
10192 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10193 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10194 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10195 aMesh->RemoveElement(*tmpFaceIt);
10197 if ( aResult != SEW_OK)
10200 list< int > nodeIDsToRemove;
10201 vector< const SMDS_MeshNode*> nodes;
10202 ElemFeatures elemType;
10204 // loop on nodes replacement map
10205 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10206 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10207 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10209 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10210 nodeIDsToRemove.push_back( nToRemove->GetID() );
10211 // loop on elements sharing nToRemove
10212 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10213 while ( invElemIt->more() ) {
10214 const SMDS_MeshElement* e = invElemIt->next();
10215 // get a new suite of nodes: make replacement
10216 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10217 nodes.resize( nbNodes );
10218 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10219 while ( nIt->more() ) {
10220 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10221 nnIt = nReplaceMap.find( n );
10222 if ( nnIt != nReplaceMap.end() ) {
10224 n = (*nnIt).second;
10228 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10229 // elemIDsToRemove.push_back( e->GetID() );
10233 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10234 aMesh->RemoveElement( e );
10236 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10238 AddToSameGroups( newElem, e, aMesh );
10239 if ( int aShapeId = e->getshapeId() )
10240 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10246 Remove( nodeIDsToRemove, true );
10251 //================================================================================
10253 * \brief Find corresponding nodes in two sets of faces
10254 * \param theSide1 - first face set
10255 * \param theSide2 - second first face
10256 * \param theFirstNode1 - a boundary node of set 1
10257 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10258 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10259 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10260 * \param nReplaceMap - output map of corresponding nodes
10261 * \return bool - is a success or not
10263 //================================================================================
10266 //#define DEBUG_MATCHING_NODES
10269 SMESH_MeshEditor::Sew_Error
10270 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10271 set<const SMDS_MeshElement*>& theSide2,
10272 const SMDS_MeshNode* theFirstNode1,
10273 const SMDS_MeshNode* theFirstNode2,
10274 const SMDS_MeshNode* theSecondNode1,
10275 const SMDS_MeshNode* theSecondNode2,
10276 TNodeNodeMap & nReplaceMap)
10278 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10280 nReplaceMap.clear();
10281 if ( theFirstNode1 != theFirstNode2 )
10282 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10283 if ( theSecondNode1 != theSecondNode2 )
10284 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10286 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10287 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10289 list< NLink > linkList[2];
10290 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10291 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10293 // loop on links in linkList; find faces by links and append links
10294 // of the found faces to linkList
10295 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10296 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10297 NLink link[] = { *linkIt[0], *linkIt[1] };
10298 if ( linkSet.find( link[0] ) == linkSet.end() )
10301 // by links, find faces in the face sets,
10302 // and find indices of link nodes in the found faces;
10303 // in a face set, there is only one or no face sharing a link
10304 // ---------------------------------------------------------------
10306 const SMDS_MeshElement* face[] = { 0, 0 };
10307 list<const SMDS_MeshNode*> notLinkNodes[2];
10308 //bool reverse[] = { false, false }; // order of notLinkNodes
10310 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10312 const SMDS_MeshNode* n1 = link[iSide].first;
10313 const SMDS_MeshNode* n2 = link[iSide].second;
10314 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10315 set< const SMDS_MeshElement* > facesOfNode1;
10316 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10318 // during a loop of the first node, we find all faces around n1,
10319 // during a loop of the second node, we find one face sharing both n1 and n2
10320 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10321 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10322 while ( fIt->more() ) { // loop on faces sharing a node
10323 const SMDS_MeshElement* f = fIt->next();
10324 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10325 ! facesOfNode1.insert( f ).second ) // f encounters twice
10327 if ( face[ iSide ] ) {
10328 MESSAGE( "2 faces per link " );
10329 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10332 faceSet->erase( f );
10334 // get not link nodes
10335 int nbN = f->NbNodes();
10336 if ( f->IsQuadratic() )
10338 nbNodes[ iSide ] = nbN;
10339 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10340 int i1 = f->GetNodeIndex( n1 );
10341 int i2 = f->GetNodeIndex( n2 );
10342 int iEnd = nbN, iBeg = -1, iDelta = 1;
10343 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10345 std::swap( iEnd, iBeg ); iDelta = -1;
10350 if ( i == iEnd ) i = iBeg + iDelta;
10351 if ( i == i1 ) break;
10352 nodes.push_back ( f->GetNode( i ) );
10358 // check similarity of elements of the sides
10359 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10360 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10361 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10362 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10365 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10369 // set nodes to merge
10370 // -------------------
10372 if ( face[0] && face[1] ) {
10373 if ( nbNodes[0] != nbNodes[1] ) {
10374 MESSAGE("Diff nb of face nodes");
10375 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10377 #ifdef DEBUG_MATCHING_NODES
10378 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10379 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10380 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10382 int nbN = nbNodes[0];
10384 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10385 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10386 for ( int i = 0 ; i < nbN - 2; ++i ) {
10387 #ifdef DEBUG_MATCHING_NODES
10388 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10390 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10394 // add other links of the face 1 to linkList
10395 // -----------------------------------------
10397 const SMDS_MeshElement* f0 = face[0];
10398 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10399 for ( int i = 0; i < nbN; i++ )
10401 const SMDS_MeshNode* n2 = f0->GetNode( i );
10402 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10403 linkSet.insert( SMESH_TLink( n1, n2 ));
10404 if ( !iter_isnew.second ) { // already in a set: no need to process
10405 linkSet.erase( iter_isnew.first );
10407 else // new in set == encountered for the first time: add
10409 #ifdef DEBUG_MATCHING_NODES
10410 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10411 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10413 linkList[0].push_back ( NLink( n1, n2 ));
10414 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10419 } // loop on link lists
10424 //================================================================================
10426 * \brief Create elements equal (on same nodes) to given ones
10427 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10428 * elements of the uppest dimension are duplicated.
10430 //================================================================================
10432 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10434 ClearLastCreated();
10435 SMESHDS_Mesh* mesh = GetMeshDS();
10437 // get an element type and an iterator over elements
10439 SMDSAbs_ElementType type;
10440 SMDS_ElemIteratorPtr elemIt;
10441 vector< const SMDS_MeshElement* > allElems;
10442 if ( theElements.empty() )
10444 if ( mesh->NbNodes() == 0 )
10446 // get most complex type
10447 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10448 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10449 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10451 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10452 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10457 // put all elements in the vector <allElems>
10458 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10459 elemIt = mesh->elementsIterator( type );
10460 while ( elemIt->more() )
10461 allElems.push_back( elemIt->next());
10462 elemIt = elemSetIterator( allElems );
10466 type = (*theElements.begin())->GetType();
10467 elemIt = elemSetIterator( theElements );
10470 // duplicate elements
10472 ElemFeatures elemType;
10474 vector< const SMDS_MeshNode* > nodes;
10475 while ( elemIt->more() )
10477 const SMDS_MeshElement* elem = elemIt->next();
10478 if ( elem->GetType() != type )
10481 elemType.Init( elem, /*basicOnly=*/false );
10482 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10484 AddElement( nodes, elemType );
10488 //================================================================================
10490 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10491 \param theElems - the list of elements (edges or faces) to be replicated
10492 The nodes for duplication could be found from these elements
10493 \param theNodesNot - list of nodes to NOT replicate
10494 \param theAffectedElems - the list of elements (cells and edges) to which the
10495 replicated nodes should be associated to.
10496 \return TRUE if operation has been completed successfully, FALSE otherwise
10498 //================================================================================
10500 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10501 const TIDSortedElemSet& theNodesNot,
10502 const TIDSortedElemSet& theAffectedElems )
10504 myLastCreatedElems.Clear();
10505 myLastCreatedNodes.Clear();
10507 if ( theElems.size() == 0 )
10510 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10515 TNodeNodeMap anOldNodeToNewNode;
10516 // duplicate elements and nodes
10517 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10518 // replce nodes by duplications
10519 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10523 //================================================================================
10525 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10526 \param theMeshDS - mesh instance
10527 \param theElems - the elements replicated or modified (nodes should be changed)
10528 \param theNodesNot - nodes to NOT replicate
10529 \param theNodeNodeMap - relation of old node to new created node
10530 \param theIsDoubleElem - flag os to replicate element or modify
10531 \return TRUE if operation has been completed successfully, FALSE otherwise
10533 //================================================================================
10535 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10536 const TIDSortedElemSet& theElems,
10537 const TIDSortedElemSet& theNodesNot,
10538 TNodeNodeMap& theNodeNodeMap,
10539 const bool theIsDoubleElem )
10541 MESSAGE("doubleNodes");
10542 // iterate through element and duplicate them (by nodes duplication)
10544 std::vector<const SMDS_MeshNode*> newNodes;
10545 ElemFeatures elemType;
10547 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10548 for ( ; elemItr != theElems.end(); ++elemItr )
10550 const SMDS_MeshElement* anElem = *elemItr;
10554 // duplicate nodes to duplicate element
10555 bool isDuplicate = false;
10556 newNodes.resize( anElem->NbNodes() );
10557 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10559 while ( anIter->more() )
10561 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10562 const SMDS_MeshNode* aNewNode = aCurrNode;
10563 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10564 if ( n2n != theNodeNodeMap.end() )
10566 aNewNode = n2n->second;
10568 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10571 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10572 copyPosition( aCurrNode, aNewNode );
10573 theNodeNodeMap[ aCurrNode ] = aNewNode;
10574 myLastCreatedNodes.Append( aNewNode );
10576 isDuplicate |= (aCurrNode != aNewNode);
10577 newNodes[ ind++ ] = aNewNode;
10579 if ( !isDuplicate )
10582 if ( theIsDoubleElem )
10583 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10585 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10592 //================================================================================
10594 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10595 \param theNodes - identifiers of nodes to be doubled
10596 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10597 nodes. If list of element identifiers is empty then nodes are doubled but
10598 they not assigned to elements
10599 \return TRUE if operation has been completed successfully, FALSE otherwise
10601 //================================================================================
10603 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10604 const std::list< int >& theListOfModifiedElems )
10606 MESSAGE("DoubleNodes");
10607 myLastCreatedElems.Clear();
10608 myLastCreatedNodes.Clear();
10610 if ( theListOfNodes.size() == 0 )
10613 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10617 // iterate through nodes and duplicate them
10619 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10621 std::list< int >::const_iterator aNodeIter;
10622 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10624 int aCurr = *aNodeIter;
10625 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10631 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10634 copyPosition( aNode, aNewNode );
10635 anOldNodeToNewNode[ aNode ] = aNewNode;
10636 myLastCreatedNodes.Append( aNewNode );
10640 // Create map of new nodes for modified elements
10642 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10644 std::list< int >::const_iterator anElemIter;
10645 for ( anElemIter = theListOfModifiedElems.begin();
10646 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10648 int aCurr = *anElemIter;
10649 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10653 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10655 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10657 while ( anIter->more() )
10659 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10660 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10662 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10663 aNodeArr[ ind++ ] = aNewNode;
10666 aNodeArr[ ind++ ] = aCurrNode;
10668 anElemToNodes[ anElem ] = aNodeArr;
10671 // Change nodes of elements
10673 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10674 anElemToNodesIter = anElemToNodes.begin();
10675 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10677 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10678 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10681 MESSAGE("ChangeElementNodes");
10682 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10691 //================================================================================
10693 \brief Check if element located inside shape
10694 \return TRUE if IN or ON shape, FALSE otherwise
10696 //================================================================================
10698 template<class Classifier>
10699 bool isInside(const SMDS_MeshElement* theElem,
10700 Classifier& theClassifier,
10701 const double theTol)
10703 gp_XYZ centerXYZ (0, 0, 0);
10704 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10705 while (aNodeItr->more())
10706 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10708 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10709 theClassifier.Perform(aPnt, theTol);
10710 TopAbs_State aState = theClassifier.State();
10711 return (aState == TopAbs_IN || aState == TopAbs_ON );
10714 //================================================================================
10716 * \brief Classifier of the 3D point on the TopoDS_Face
10717 * with interaface suitable for isInside()
10719 //================================================================================
10721 struct _FaceClassifier
10723 Extrema_ExtPS _extremum;
10724 BRepAdaptor_Surface _surface;
10725 TopAbs_State _state;
10727 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10729 _extremum.Initialize( _surface,
10730 _surface.FirstUParameter(), _surface.LastUParameter(),
10731 _surface.FirstVParameter(), _surface.LastVParameter(),
10732 _surface.Tolerance(), _surface.Tolerance() );
10734 void Perform(const gp_Pnt& aPnt, double theTol)
10737 _state = TopAbs_OUT;
10738 _extremum.Perform(aPnt);
10739 if ( _extremum.IsDone() )
10740 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10741 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10743 TopAbs_State State() const
10750 //================================================================================
10752 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10753 This method is the first step of DoubleNodeElemGroupsInRegion.
10754 \param theElems - list of groups of elements (edges or faces) to be replicated
10755 \param theNodesNot - list of groups of nodes not to replicated
10756 \param theShape - shape to detect affected elements (element which geometric center
10757 located on or inside shape). If the shape is null, detection is done on faces orientations
10758 (select elements with a gravity center on the side given by faces normals).
10759 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10760 The replicated nodes should be associated to affected elements.
10761 \return groups of affected elements
10762 \sa DoubleNodeElemGroupsInRegion()
10764 //================================================================================
10766 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10767 const TIDSortedElemSet& theNodesNot,
10768 const TopoDS_Shape& theShape,
10769 TIDSortedElemSet& theAffectedElems)
10771 if ( theShape.IsNull() )
10773 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10774 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10775 std::set<const SMDS_MeshElement*> edgesToCheck;
10776 alreadyCheckedNodes.clear();
10777 alreadyCheckedElems.clear();
10778 edgesToCheck.clear();
10780 // --- iterates on elements to be replicated and get elements by back references from their nodes
10782 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10784 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10786 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10787 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10790 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10791 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10792 std::set<const SMDS_MeshNode*> nodesElem;
10794 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10795 while ( nodeItr->more() )
10797 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10798 nodesElem.insert(aNode);
10800 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10801 for (; nodit != nodesElem.end(); nodit++)
10803 MESSAGE(" noeud ");
10804 const SMDS_MeshNode* aNode = *nodit;
10805 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10807 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10809 alreadyCheckedNodes.insert(aNode);
10810 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10811 while ( backElemItr->more() )
10813 MESSAGE(" backelem ");
10814 const SMDS_MeshElement* curElem = backElemItr->next();
10815 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10817 if (theElems.find(curElem) != theElems.end())
10819 alreadyCheckedElems.insert(curElem);
10820 double x=0, y=0, z=0;
10822 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10823 while ( nodeItr2->more() )
10825 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10826 x += anotherNode->X();
10827 y += anotherNode->Y();
10828 z += anotherNode->Z();
10832 p.SetCoord( x/nb -aNode->X(),
10834 z/nb -aNode->Z() );
10835 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10838 MESSAGE(" --- inserted")
10839 theAffectedElems.insert( curElem );
10841 else if (curElem->GetType() == SMDSAbs_Edge)
10842 edgesToCheck.insert(curElem);
10846 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10847 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10848 for( ; eit != edgesToCheck.end(); eit++)
10850 bool onside = true;
10851 const SMDS_MeshElement* anEdge = *eit;
10852 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10853 while ( nodeItr->more() )
10855 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10856 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10864 MESSAGE(" --- edge onside inserted")
10865 theAffectedElems.insert(anEdge);
10871 const double aTol = Precision::Confusion();
10872 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10873 auto_ptr<_FaceClassifier> aFaceClassifier;
10874 if ( theShape.ShapeType() == TopAbs_SOLID )
10876 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10877 bsc3d->PerformInfinitePoint(aTol);
10879 else if (theShape.ShapeType() == TopAbs_FACE )
10881 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10884 // iterates on indicated elements and get elements by back references from their nodes
10885 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10887 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10889 MESSAGE("element " << ielem++);
10890 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10893 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10894 while ( nodeItr->more() )
10896 MESSAGE(" noeud ");
10897 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10898 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10900 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10901 while ( backElemItr->more() )
10903 MESSAGE(" backelem ");
10904 const SMDS_MeshElement* curElem = backElemItr->next();
10905 if ( curElem && theElems.find(curElem) == theElems.end() &&
10907 isInside( curElem, *bsc3d, aTol ) :
10908 isInside( curElem, *aFaceClassifier, aTol )))
10909 theAffectedElems.insert( curElem );
10917 //================================================================================
10919 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10920 \param theElems - group of of elements (edges or faces) to be replicated
10921 \param theNodesNot - group of nodes not to replicate
10922 \param theShape - shape to detect affected elements (element which geometric center
10923 located on or inside shape).
10924 The replicated nodes should be associated to affected elements.
10925 \return TRUE if operation has been completed successfully, FALSE otherwise
10927 //================================================================================
10929 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10930 const TIDSortedElemSet& theNodesNot,
10931 const TopoDS_Shape& theShape )
10933 if ( theShape.IsNull() )
10936 const double aTol = Precision::Confusion();
10937 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10938 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10939 if ( theShape.ShapeType() == TopAbs_SOLID )
10941 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10942 bsc3d->PerformInfinitePoint(aTol);
10944 else if (theShape.ShapeType() == TopAbs_FACE )
10946 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10949 // iterates on indicated elements and get elements by back references from their nodes
10950 TIDSortedElemSet anAffected;
10951 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10952 for ( ; elemItr != theElems.end(); ++elemItr )
10954 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10958 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10959 while ( nodeItr->more() )
10961 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10962 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10964 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10965 while ( backElemItr->more() )
10967 const SMDS_MeshElement* curElem = backElemItr->next();
10968 if ( curElem && theElems.find(curElem) == theElems.end() &&
10970 isInside( curElem, *bsc3d, aTol ) :
10971 isInside( curElem, *aFaceClassifier, aTol )))
10972 anAffected.insert( curElem );
10976 return DoubleNodes( theElems, theNodesNot, anAffected );
10980 * \brief compute an oriented angle between two planes defined by four points.
10981 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10982 * @param p0 base of the rotation axe
10983 * @param p1 extremity of the rotation axe
10984 * @param g1 belongs to the first plane
10985 * @param g2 belongs to the second plane
10987 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10989 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10990 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10991 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10992 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10993 gp_Vec vref(p0, p1);
10996 gp_Vec n1 = vref.Crossed(v1);
10997 gp_Vec n2 = vref.Crossed(v2);
10999 return n2.AngleWithRef(n1, vref);
11001 catch ( Standard_Failure ) {
11003 return Max( v1.Magnitude(), v2.Magnitude() );
11007 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11008 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11009 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11010 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11011 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11012 * 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.
11013 * 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.
11014 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11015 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11016 * \param theElems - list of groups of volumes, where a group of volume is a set of
11017 * SMDS_MeshElements sorted by Id.
11018 * \param createJointElems - if TRUE, create the elements
11019 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11020 * the boundary between \a theDomains and the rest mesh
11021 * \return TRUE if operation has been completed successfully, FALSE otherwise
11023 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11024 bool createJointElems,
11025 bool onAllBoundaries)
11027 MESSAGE("----------------------------------------------");
11028 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11029 MESSAGE("----------------------------------------------");
11031 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11032 meshDS->BuildDownWardConnectivity(true);
11034 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11036 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11037 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11038 // build the list of nodes shared by 2 or more domains, with their domain indexes
11040 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11041 std::map<int,int>celldom; // cell vtkId --> domain
11042 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11043 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11044 faceDomains.clear();
11046 cellDomains.clear();
11047 nodeDomains.clear();
11048 std::map<int,int> emptyMap;
11049 std::set<int> emptySet;
11052 MESSAGE(".. Number of domains :"<<theElems.size());
11054 TIDSortedElemSet theRestDomElems;
11055 const int iRestDom = -1;
11056 const int idom0 = onAllBoundaries ? iRestDom : 0;
11057 const int nbDomains = theElems.size();
11059 // Check if the domains do not share an element
11060 for (int idom = 0; idom < nbDomains-1; idom++)
11062 // MESSAGE("... Check of domain #" << idom);
11063 const TIDSortedElemSet& domain = theElems[idom];
11064 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11065 for (; elemItr != domain.end(); ++elemItr)
11067 const SMDS_MeshElement* anElem = *elemItr;
11068 int idombisdeb = idom + 1 ;
11069 // check if the element belongs to a domain further in the list
11070 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11072 const TIDSortedElemSet& domainbis = theElems[idombis];
11073 if ( domainbis.count( anElem ))
11075 MESSAGE(".... Domain #" << idom);
11076 MESSAGE(".... Domain #" << idombis);
11077 throw SALOME_Exception("The domains are not disjoint.");
11084 for (int idom = 0; idom < nbDomains; idom++)
11087 // --- build a map (face to duplicate --> volume to modify)
11088 // with all the faces shared by 2 domains (group of elements)
11089 // and corresponding volume of this domain, for each shared face.
11090 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11092 MESSAGE("... Neighbors of domain #" << idom);
11093 const TIDSortedElemSet& domain = theElems[idom];
11094 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11095 for (; elemItr != domain.end(); ++elemItr)
11097 const SMDS_MeshElement* anElem = *elemItr;
11100 int vtkId = anElem->getVtkId();
11101 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11102 int neighborsVtkIds[NBMAXNEIGHBORS];
11103 int downIds[NBMAXNEIGHBORS];
11104 unsigned char downTypes[NBMAXNEIGHBORS];
11105 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11106 for (int n = 0; n < nbNeighbors; n++)
11108 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11109 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11110 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11113 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11115 // MESSAGE("Domain " << idombis);
11116 const TIDSortedElemSet& domainbis = theElems[idombis];
11117 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11119 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11121 DownIdType face(downIds[n], downTypes[n]);
11122 if (!faceDomains[face].count(idom))
11124 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11125 celldom[vtkId] = idom;
11126 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11130 theRestDomElems.insert( elem );
11131 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11132 celldom[neighborsVtkIds[n]] = iRestDom;
11140 //MESSAGE("Number of shared faces " << faceDomains.size());
11141 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11143 // --- explore the shared faces domain by domain,
11144 // explore the nodes of the face and see if they belong to a cell in the domain,
11145 // which has only a node or an edge on the border (not a shared face)
11147 for (int idomain = idom0; idomain < nbDomains; idomain++)
11149 //MESSAGE("Domain " << idomain);
11150 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11151 itface = faceDomains.begin();
11152 for (; itface != faceDomains.end(); ++itface)
11154 const std::map<int, int>& domvol = itface->second;
11155 if (!domvol.count(idomain))
11157 DownIdType face = itface->first;
11158 //MESSAGE(" --- face " << face.cellId);
11159 std::set<int> oldNodes;
11161 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11162 std::set<int>::iterator itn = oldNodes.begin();
11163 for (; itn != oldNodes.end(); ++itn)
11166 //MESSAGE(" node " << oldId);
11167 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11168 for (int i=0; i<l.ncells; i++)
11170 int vtkId = l.cells[i];
11171 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11172 if (!domain.count(anElem))
11174 int vtkType = grid->GetCellType(vtkId);
11175 int downId = grid->CellIdToDownId(vtkId);
11178 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11179 continue; // not OK at this stage of the algorithm:
11180 //no cells created after BuildDownWardConnectivity
11182 DownIdType aCell(downId, vtkType);
11183 cellDomains[aCell][idomain] = vtkId;
11184 celldom[vtkId] = idomain;
11185 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11191 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11192 // for each shared face, get the nodes
11193 // for each node, for each domain of the face, create a clone of the node
11195 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11196 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11197 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11199 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11200 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11201 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11203 MESSAGE(".. Duplication of the nodes");
11204 for (int idomain = idom0; idomain < nbDomains; idomain++)
11206 itface = faceDomains.begin();
11207 for (; itface != faceDomains.end(); ++itface)
11209 const std::map<int, int>& domvol = itface->second;
11210 if (!domvol.count(idomain))
11212 DownIdType face = itface->first;
11213 //MESSAGE(" --- face " << face.cellId);
11214 std::set<int> oldNodes;
11216 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11217 std::set<int>::iterator itn = oldNodes.begin();
11218 for (; itn != oldNodes.end(); ++itn)
11221 if (nodeDomains[oldId].empty())
11223 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11224 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11226 std::map<int, int>::const_iterator itdom = domvol.begin();
11227 for (; itdom != domvol.end(); ++itdom)
11229 int idom = itdom->first;
11230 //MESSAGE(" domain " << idom);
11231 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11233 if (nodeDomains[oldId].size() >= 2) // a multiple node
11235 vector<int> orderedDoms;
11236 //MESSAGE("multiple node " << oldId);
11237 if (mutipleNodes.count(oldId))
11238 orderedDoms = mutipleNodes[oldId];
11241 map<int,int>::iterator it = nodeDomains[oldId].begin();
11242 for (; it != nodeDomains[oldId].end(); ++it)
11243 orderedDoms.push_back(it->first);
11245 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11246 //stringstream txt;
11247 //for (int i=0; i<orderedDoms.size(); i++)
11248 // txt << orderedDoms[i] << " ";
11249 //MESSAGE("orderedDoms " << txt.str());
11250 mutipleNodes[oldId] = orderedDoms;
11252 double *coords = grid->GetPoint(oldId);
11253 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11254 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11255 int newId = newNode->getVtkId();
11256 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11257 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11264 MESSAGE(".. Creation of elements");
11265 for (int idomain = idom0; idomain < nbDomains; idomain++)
11267 itface = faceDomains.begin();
11268 for (; itface != faceDomains.end(); ++itface)
11270 std::map<int, int> domvol = itface->second;
11271 if (!domvol.count(idomain))
11273 DownIdType face = itface->first;
11274 //MESSAGE(" --- face " << face.cellId);
11275 std::set<int> oldNodes;
11277 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11278 int nbMultipleNodes = 0;
11279 std::set<int>::iterator itn = oldNodes.begin();
11280 for (; itn != oldNodes.end(); ++itn)
11283 if (mutipleNodes.count(oldId))
11286 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11288 //MESSAGE("multiple Nodes detected on a shared face");
11289 int downId = itface->first.cellId;
11290 unsigned char cellType = itface->first.cellType;
11291 // --- shared edge or shared face ?
11292 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11295 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11296 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11297 if (mutipleNodes.count(nodes[i]))
11298 if (!mutipleNodesToFace.count(nodes[i]))
11299 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11301 else // shared face (between two volumes)
11303 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11304 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11305 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11306 for (int ie =0; ie < nbEdges; ie++)
11309 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11310 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11312 vector<int> vn0 = mutipleNodes[nodes[0]];
11313 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11315 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11316 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11317 if ( vn0[i0] == vn1[i1] )
11318 doms.push_back( vn0[ i0 ]);
11319 if ( doms.size() > 2 )
11321 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11322 double *coords = grid->GetPoint(nodes[0]);
11323 gp_Pnt p0(coords[0], coords[1], coords[2]);
11324 coords = grid->GetPoint(nodes[nbNodes - 1]);
11325 gp_Pnt p1(coords[0], coords[1], coords[2]);
11327 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11328 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11329 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11330 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11331 for ( size_t id = 0; id < doms.size(); id++ )
11333 int idom = doms[id];
11334 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11335 for ( int ivol = 0; ivol < nbvol; ivol++ )
11337 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11338 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11339 if (domain.count(elem))
11341 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11342 domvol[idom] = svol;
11343 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11345 vtkIdType npts = 0;
11346 vtkIdType* pts = 0;
11347 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11348 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11351 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11352 angleDom[idom] = 0;
11356 gp_Pnt g(values[0], values[1], values[2]);
11357 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11358 //MESSAGE(" angle=" << angleDom[idom]);
11364 map<double, int> sortedDom; // sort domains by angle
11365 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11366 sortedDom[ia->second] = ia->first;
11367 vector<int> vnodes;
11369 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11371 vdom.push_back(ib->second);
11372 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11374 for (int ino = 0; ino < nbNodes; ino++)
11375 vnodes.push_back(nodes[ino]);
11376 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11385 // --- iterate on shared faces (volumes to modify, face to extrude)
11386 // get node id's of the face (id SMDS = id VTK)
11387 // create flat element with old and new nodes if requested
11389 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11390 // (domain1 X domain2) = domain1 + MAXINT*domain2
11392 std::map<int, std::map<long,int> > nodeQuadDomains;
11393 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11395 MESSAGE(".. Creation of elements: simple junction");
11396 if (createJointElems)
11399 string joints2DName = "joints2D";
11400 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11401 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11402 string joints3DName = "joints3D";
11403 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11404 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11406 itface = faceDomains.begin();
11407 for (; itface != faceDomains.end(); ++itface)
11409 DownIdType face = itface->first;
11410 std::set<int> oldNodes;
11411 std::set<int>::iterator itn;
11413 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11415 std::map<int, int> domvol = itface->second;
11416 std::map<int, int>::iterator itdom = domvol.begin();
11417 int dom1 = itdom->first;
11418 int vtkVolId = itdom->second;
11420 int dom2 = itdom->first;
11421 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11423 stringstream grpname;
11426 grpname << dom1 << "_" << dom2;
11428 grpname << dom2 << "_" << dom1;
11429 string namegrp = grpname.str();
11430 if (!mapOfJunctionGroups.count(namegrp))
11431 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11432 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11434 sgrp->Add(vol->GetID());
11435 if (vol->GetType() == SMDSAbs_Volume)
11436 joints3DGrp->Add(vol->GetID());
11437 else if (vol->GetType() == SMDSAbs_Face)
11438 joints2DGrp->Add(vol->GetID());
11442 // --- create volumes on multiple domain intersection if requested
11443 // iterate on mutipleNodesToFace
11444 // iterate on edgesMultiDomains
11446 MESSAGE(".. Creation of elements: multiple junction");
11447 if (createJointElems)
11449 // --- iterate on mutipleNodesToFace
11451 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11452 for (; itn != mutipleNodesToFace.end(); ++itn)
11454 int node = itn->first;
11455 vector<int> orderDom = itn->second;
11456 vector<vtkIdType> orderedNodes;
11457 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11458 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11459 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11461 stringstream grpname;
11463 grpname << 0 << "_" << 0;
11465 string namegrp = grpname.str();
11466 if (!mapOfJunctionGroups.count(namegrp))
11467 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11468 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11470 sgrp->Add(face->GetID());
11473 // --- iterate on edgesMultiDomains
11475 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11476 for (; ite != edgesMultiDomains.end(); ++ite)
11478 vector<int> nodes = ite->first;
11479 vector<int> orderDom = ite->second;
11480 vector<vtkIdType> orderedNodes;
11481 if (nodes.size() == 2)
11483 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11484 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11485 if ( orderDom.size() == 3 )
11486 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11487 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11489 for (int idom = orderDom.size()-1; idom >=0; idom--)
11490 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11491 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11494 string namegrp = "jointsMultiples";
11495 if (!mapOfJunctionGroups.count(namegrp))
11496 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11497 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11499 sgrp->Add(vol->GetID());
11503 //INFOS("Quadratic multiple joints not implemented");
11504 // TODO quadratic nodes
11509 // --- list the explicit faces and edges of the mesh that need to be modified,
11510 // i.e. faces and edges built with one or more duplicated nodes.
11511 // associate these faces or edges to their corresponding domain.
11512 // only the first domain found is kept when a face or edge is shared
11514 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11515 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11516 faceOrEdgeDom.clear();
11519 MESSAGE(".. Modification of elements");
11520 for (int idomain = idom0; idomain < nbDomains; idomain++)
11522 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11523 for (; itnod != nodeDomains.end(); ++itnod)
11525 int oldId = itnod->first;
11526 //MESSAGE(" node " << oldId);
11527 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11528 for (int i = 0; i < l.ncells; i++)
11530 int vtkId = l.cells[i];
11531 int vtkType = grid->GetCellType(vtkId);
11532 int downId = grid->CellIdToDownId(vtkId);
11534 continue; // new cells: not to be modified
11535 DownIdType aCell(downId, vtkType);
11536 int volParents[1000];
11537 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11538 for (int j = 0; j < nbvol; j++)
11539 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11540 if (!feDom.count(vtkId))
11542 feDom[vtkId] = idomain;
11543 faceOrEdgeDom[aCell] = emptyMap;
11544 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11545 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11546 // << " type " << vtkType << " downId " << downId);
11552 // --- iterate on shared faces (volumes to modify, face to extrude)
11553 // get node id's of the face
11554 // replace old nodes by new nodes in volumes, and update inverse connectivity
11556 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11557 for (int m=0; m<3; m++)
11559 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11560 itface = (*amap).begin();
11561 for (; itface != (*amap).end(); ++itface)
11563 DownIdType face = itface->first;
11564 std::set<int> oldNodes;
11565 std::set<int>::iterator itn;
11567 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11568 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11569 std::map<int, int> localClonedNodeIds;
11571 std::map<int, int> domvol = itface->second;
11572 std::map<int, int>::iterator itdom = domvol.begin();
11573 for (; itdom != domvol.end(); ++itdom)
11575 int idom = itdom->first;
11576 int vtkVolId = itdom->second;
11577 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11578 localClonedNodeIds.clear();
11579 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11582 if (nodeDomains[oldId].count(idom))
11584 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11585 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11588 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11593 // Remove empty groups (issue 0022812)
11594 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11595 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11597 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11598 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11601 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11602 grid->BuildLinks();
11610 * \brief Double nodes on some external faces and create flat elements.
11611 * Flat elements are mainly used by some types of mechanic calculations.
11613 * Each group of the list must be constituted of faces.
11614 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11615 * @param theElems - list of groups of faces, where a group of faces is a set of
11616 * SMDS_MeshElements sorted by Id.
11617 * @return TRUE if operation has been completed successfully, FALSE otherwise
11619 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11621 MESSAGE("-------------------------------------------------");
11622 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11623 MESSAGE("-------------------------------------------------");
11625 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11627 // --- For each group of faces
11628 // duplicate the nodes, create a flat element based on the face
11629 // replace the nodes of the faces by their clones
11631 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11632 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11633 clonedNodes.clear();
11634 intermediateNodes.clear();
11635 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11636 mapOfJunctionGroups.clear();
11638 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11640 const TIDSortedElemSet& domain = theElems[idom];
11641 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11642 for ( ; elemItr != domain.end(); ++elemItr )
11644 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11645 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11648 // MESSAGE("aFace=" << aFace->GetID());
11649 bool isQuad = aFace->IsQuadratic();
11650 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11652 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11654 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11655 while (nodeIt->more())
11657 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11658 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11660 ln2.push_back(node);
11662 ln0.push_back(node);
11664 const SMDS_MeshNode* clone = 0;
11665 if (!clonedNodes.count(node))
11667 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11668 copyPosition( node, clone );
11669 clonedNodes[node] = clone;
11672 clone = clonedNodes[node];
11675 ln3.push_back(clone);
11677 ln1.push_back(clone);
11679 const SMDS_MeshNode* inter = 0;
11680 if (isQuad && (!isMedium))
11682 if (!intermediateNodes.count(node))
11684 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11685 copyPosition( node, inter );
11686 intermediateNodes[node] = inter;
11689 inter = intermediateNodes[node];
11690 ln4.push_back(inter);
11694 // --- extrude the face
11696 vector<const SMDS_MeshNode*> ln;
11697 SMDS_MeshVolume* vol = 0;
11698 vtkIdType aType = aFace->GetVtkType();
11702 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11703 // MESSAGE("vol prism " << vol->GetID());
11704 ln.push_back(ln1[0]);
11705 ln.push_back(ln1[1]);
11706 ln.push_back(ln1[2]);
11709 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11710 // MESSAGE("vol hexa " << vol->GetID());
11711 ln.push_back(ln1[0]);
11712 ln.push_back(ln1[1]);
11713 ln.push_back(ln1[2]);
11714 ln.push_back(ln1[3]);
11716 case VTK_QUADRATIC_TRIANGLE:
11717 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11718 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11719 // MESSAGE("vol quad prism " << vol->GetID());
11720 ln.push_back(ln1[0]);
11721 ln.push_back(ln1[1]);
11722 ln.push_back(ln1[2]);
11723 ln.push_back(ln3[0]);
11724 ln.push_back(ln3[1]);
11725 ln.push_back(ln3[2]);
11727 case VTK_QUADRATIC_QUAD:
11728 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11729 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11730 // ln4[0], ln4[1], ln4[2], ln4[3]);
11731 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11732 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11733 ln4[0], ln4[1], ln4[2], ln4[3]);
11734 // MESSAGE("vol quad hexa " << vol->GetID());
11735 ln.push_back(ln1[0]);
11736 ln.push_back(ln1[1]);
11737 ln.push_back(ln1[2]);
11738 ln.push_back(ln1[3]);
11739 ln.push_back(ln3[0]);
11740 ln.push_back(ln3[1]);
11741 ln.push_back(ln3[2]);
11742 ln.push_back(ln3[3]);
11752 stringstream grpname;
11756 string namegrp = grpname.str();
11757 if (!mapOfJunctionGroups.count(namegrp))
11758 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11759 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11761 sgrp->Add(vol->GetID());
11764 // --- modify the face
11766 aFace->ChangeNodes(&ln[0], ln.size());
11773 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11774 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11775 * groups of faces to remove inside the object, (idem edges).
11776 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11778 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11779 const TopoDS_Shape& theShape,
11780 SMESH_NodeSearcher* theNodeSearcher,
11781 const char* groupName,
11782 std::vector<double>& nodesCoords,
11783 std::vector<std::vector<int> >& listOfListOfNodes)
11785 MESSAGE("--------------------------------");
11786 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11787 MESSAGE("--------------------------------");
11789 // --- zone of volumes to remove is given :
11790 // 1 either by a geom shape (one or more vertices) and a radius,
11791 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11792 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11793 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11794 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11795 // defined by it's name.
11797 SMESHDS_GroupBase* groupDS = 0;
11798 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11799 while ( groupIt->more() )
11802 SMESH_Group * group = groupIt->next();
11803 if ( !group ) continue;
11804 groupDS = group->GetGroupDS();
11805 if ( !groupDS || groupDS->IsEmpty() ) continue;
11806 std::string grpName = group->GetName();
11807 //MESSAGE("grpName=" << grpName);
11808 if (grpName == groupName)
11814 bool isNodeGroup = false;
11815 bool isNodeCoords = false;
11818 if (groupDS->GetType() != SMDSAbs_Node)
11820 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11823 if (nodesCoords.size() > 0)
11824 isNodeCoords = true; // a list o nodes given by their coordinates
11825 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11827 // --- define groups to build
11829 int idg; // --- group of SMDS volumes
11830 string grpvName = groupName;
11831 grpvName += "_vol";
11832 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11835 MESSAGE("group not created " << grpvName);
11838 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11840 int idgs; // --- group of SMDS faces on the skin
11841 string grpsName = groupName;
11842 grpsName += "_skin";
11843 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11846 MESSAGE("group not created " << grpsName);
11849 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11851 int idgi; // --- group of SMDS faces internal (several shapes)
11852 string grpiName = groupName;
11853 grpiName += "_internalFaces";
11854 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11857 MESSAGE("group not created " << grpiName);
11860 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11862 int idgei; // --- group of SMDS faces internal (several shapes)
11863 string grpeiName = groupName;
11864 grpeiName += "_internalEdges";
11865 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11868 MESSAGE("group not created " << grpeiName);
11871 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11873 // --- build downward connectivity
11875 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11876 meshDS->BuildDownWardConnectivity(true);
11877 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11879 // --- set of volumes detected inside
11881 std::set<int> setOfInsideVol;
11882 std::set<int> setOfVolToCheck;
11884 std::vector<gp_Pnt> gpnts;
11887 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11889 MESSAGE("group of nodes provided");
11890 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11891 while ( elemIt->more() )
11893 const SMDS_MeshElement* elem = elemIt->next();
11896 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11899 SMDS_MeshElement* vol = 0;
11900 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11901 while (volItr->more())
11903 vol = (SMDS_MeshElement*)volItr->next();
11904 setOfInsideVol.insert(vol->getVtkId());
11905 sgrp->Add(vol->GetID());
11909 else if (isNodeCoords)
11911 MESSAGE("list of nodes coordinates provided");
11914 while ( i < nodesCoords.size()-2 )
11916 double x = nodesCoords[i++];
11917 double y = nodesCoords[i++];
11918 double z = nodesCoords[i++];
11919 gp_Pnt p = gp_Pnt(x, y ,z);
11920 gpnts.push_back(p);
11921 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11925 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11927 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11928 TopTools_IndexedMapOfShape vertexMap;
11929 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11930 gp_Pnt p = gp_Pnt(0,0,0);
11931 if (vertexMap.Extent() < 1)
11934 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11936 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11937 p = BRep_Tool::Pnt(vertex);
11938 gpnts.push_back(p);
11939 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11943 if (gpnts.size() > 0)
11946 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11948 nodeId = startNode->GetID();
11949 MESSAGE("nodeId " << nodeId);
11951 double radius2 = radius*radius;
11952 MESSAGE("radius2 " << radius2);
11954 // --- volumes on start node
11956 setOfVolToCheck.clear();
11957 SMDS_MeshElement* startVol = 0;
11958 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11959 while (volItr->more())
11961 startVol = (SMDS_MeshElement*)volItr->next();
11962 setOfVolToCheck.insert(startVol->getVtkId());
11964 if (setOfVolToCheck.empty())
11966 MESSAGE("No volumes found");
11970 // --- starting with central volumes then their neighbors, check if they are inside
11971 // or outside the domain, until no more new neighbor volume is inside.
11972 // Fill the group of inside volumes
11974 std::map<int, double> mapOfNodeDistance2;
11975 mapOfNodeDistance2.clear();
11976 std::set<int> setOfOutsideVol;
11977 while (!setOfVolToCheck.empty())
11979 std::set<int>::iterator it = setOfVolToCheck.begin();
11981 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11982 bool volInside = false;
11983 vtkIdType npts = 0;
11984 vtkIdType* pts = 0;
11985 grid->GetCellPoints(vtkId, npts, pts);
11986 for (int i=0; i<npts; i++)
11988 double distance2 = 0;
11989 if (mapOfNodeDistance2.count(pts[i]))
11991 distance2 = mapOfNodeDistance2[pts[i]];
11992 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11996 double *coords = grid->GetPoint(pts[i]);
11997 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11999 for ( size_t j = 0; j < gpnts.size(); j++ )
12001 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12002 if (d2 < distance2)
12005 if (distance2 < radius2)
12009 mapOfNodeDistance2[pts[i]] = distance2;
12010 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12012 if (distance2 < radius2)
12014 volInside = true; // one or more nodes inside the domain
12015 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12021 setOfInsideVol.insert(vtkId);
12022 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12023 int neighborsVtkIds[NBMAXNEIGHBORS];
12024 int downIds[NBMAXNEIGHBORS];
12025 unsigned char downTypes[NBMAXNEIGHBORS];
12026 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12027 for (int n = 0; n < nbNeighbors; n++)
12028 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12029 setOfVolToCheck.insert(neighborsVtkIds[n]);
12033 setOfOutsideVol.insert(vtkId);
12034 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12036 setOfVolToCheck.erase(vtkId);
12040 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12041 // If yes, add the volume to the inside set
12043 bool addedInside = true;
12044 std::set<int> setOfVolToReCheck;
12045 while (addedInside)
12047 MESSAGE(" --------------------------- re check");
12048 addedInside = false;
12049 std::set<int>::iterator itv = setOfInsideVol.begin();
12050 for (; itv != setOfInsideVol.end(); ++itv)
12053 int neighborsVtkIds[NBMAXNEIGHBORS];
12054 int downIds[NBMAXNEIGHBORS];
12055 unsigned char downTypes[NBMAXNEIGHBORS];
12056 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12057 for (int n = 0; n < nbNeighbors; n++)
12058 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12059 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12061 setOfVolToCheck = setOfVolToReCheck;
12062 setOfVolToReCheck.clear();
12063 while (!setOfVolToCheck.empty())
12065 std::set<int>::iterator it = setOfVolToCheck.begin();
12067 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12069 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12070 int countInside = 0;
12071 int neighborsVtkIds[NBMAXNEIGHBORS];
12072 int downIds[NBMAXNEIGHBORS];
12073 unsigned char downTypes[NBMAXNEIGHBORS];
12074 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12075 for (int n = 0; n < nbNeighbors; n++)
12076 if (setOfInsideVol.count(neighborsVtkIds[n]))
12078 MESSAGE("countInside " << countInside);
12079 if (countInside > 1)
12081 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12082 setOfInsideVol.insert(vtkId);
12083 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12084 addedInside = true;
12087 setOfVolToReCheck.insert(vtkId);
12089 setOfVolToCheck.erase(vtkId);
12093 // --- map of Downward faces at the boundary, inside the global volume
12094 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12095 // fill group of SMDS faces inside the volume (when several volume shapes)
12096 // fill group of SMDS faces on the skin of the global volume (if skin)
12098 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12099 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12100 std::set<int>::iterator it = setOfInsideVol.begin();
12101 for (; it != setOfInsideVol.end(); ++it)
12104 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12105 int neighborsVtkIds[NBMAXNEIGHBORS];
12106 int downIds[NBMAXNEIGHBORS];
12107 unsigned char downTypes[NBMAXNEIGHBORS];
12108 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12109 for (int n = 0; n < nbNeighbors; n++)
12111 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12112 if (neighborDim == 3)
12114 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12116 DownIdType face(downIds[n], downTypes[n]);
12117 boundaryFaces[face] = vtkId;
12119 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12120 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12121 if (vtkFaceId >= 0)
12123 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12124 // find also the smds edges on this face
12125 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12126 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12127 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12128 for (int i = 0; i < nbEdges; i++)
12130 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12131 if (vtkEdgeId >= 0)
12132 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12136 else if (neighborDim == 2) // skin of the volume
12138 DownIdType face(downIds[n], downTypes[n]);
12139 skinFaces[face] = vtkId;
12140 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12141 if (vtkFaceId >= 0)
12142 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12147 // --- identify the edges constituting the wire of each subshape on the skin
12148 // define polylines with the nodes of edges, equivalent to wires
12149 // project polylines on subshapes, and partition, to get geom faces
12151 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12152 std::set<int> emptySet;
12154 std::set<int> shapeIds;
12156 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12157 while (itelem->more())
12159 const SMDS_MeshElement *elem = itelem->next();
12160 int shapeId = elem->getshapeId();
12161 int vtkId = elem->getVtkId();
12162 if (!shapeIdToVtkIdSet.count(shapeId))
12164 shapeIdToVtkIdSet[shapeId] = emptySet;
12165 shapeIds.insert(shapeId);
12167 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12170 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12171 std::set<DownIdType, DownIdCompare> emptyEdges;
12172 emptyEdges.clear();
12174 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12175 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12177 int shapeId = itShape->first;
12178 MESSAGE(" --- Shape ID --- "<< shapeId);
12179 shapeIdToEdges[shapeId] = emptyEdges;
12181 std::vector<int> nodesEdges;
12183 std::set<int>::iterator its = itShape->second.begin();
12184 for (; its != itShape->second.end(); ++its)
12187 MESSAGE(" " << vtkId);
12188 int neighborsVtkIds[NBMAXNEIGHBORS];
12189 int downIds[NBMAXNEIGHBORS];
12190 unsigned char downTypes[NBMAXNEIGHBORS];
12191 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12192 for (int n = 0; n < nbNeighbors; n++)
12194 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12196 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12197 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12198 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12200 DownIdType edge(downIds[n], downTypes[n]);
12201 if (!shapeIdToEdges[shapeId].count(edge))
12203 shapeIdToEdges[shapeId].insert(edge);
12205 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12206 nodesEdges.push_back(vtkNodeId[0]);
12207 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12208 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12214 std::list<int> order;
12216 if (nodesEdges.size() > 0)
12218 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12219 nodesEdges[0] = -1;
12220 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12221 nodesEdges[1] = -1; // do not reuse this edge
12225 int nodeTofind = order.back(); // try first to push back
12227 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12228 if (nodesEdges[i] == nodeTofind)
12230 if ( i == (int) nodesEdges.size() )
12231 found = false; // no follower found on back
12234 if (i%2) // odd ==> use the previous one
12235 if (nodesEdges[i-1] < 0)
12239 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12240 nodesEdges[i-1] = -1;
12242 else // even ==> use the next one
12243 if (nodesEdges[i+1] < 0)
12247 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12248 nodesEdges[i+1] = -1;
12253 // try to push front
12255 nodeTofind = order.front(); // try to push front
12256 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12257 if ( nodesEdges[i] == nodeTofind )
12259 if ( i == (int)nodesEdges.size() )
12261 found = false; // no predecessor found on front
12264 if (i%2) // odd ==> use the previous one
12265 if (nodesEdges[i-1] < 0)
12269 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12270 nodesEdges[i-1] = -1;
12272 else // even ==> use the next one
12273 if (nodesEdges[i+1] < 0)
12277 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12278 nodesEdges[i+1] = -1;
12284 std::vector<int> nodes;
12285 nodes.push_back(shapeId);
12286 std::list<int>::iterator itl = order.begin();
12287 for (; itl != order.end(); itl++)
12289 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12290 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12292 listOfListOfNodes.push_back(nodes);
12295 // partition geom faces with blocFissure
12296 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12297 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12303 //================================================================================
12305 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12306 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12307 * \return TRUE if operation has been completed successfully, FALSE otherwise
12309 //================================================================================
12311 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12313 // iterates on volume elements and detect all free faces on them
12314 SMESHDS_Mesh* aMesh = GetMeshDS();
12318 ElemFeatures faceType( SMDSAbs_Face );
12319 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12320 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12323 const SMDS_MeshVolume* volume = vIt->next();
12324 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12325 vTool.SetExternalNormal();
12326 const int iQuad = volume->IsQuadratic();
12327 faceType.SetQuad( iQuad );
12328 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12330 if (!vTool.IsFreeFace(iface))
12333 vector<const SMDS_MeshNode *> nodes;
12334 int nbFaceNodes = vTool.NbFaceNodes(iface);
12335 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12337 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12338 nodes.push_back(faceNodes[inode]);
12340 if (iQuad) // add medium nodes
12342 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12343 nodes.push_back(faceNodes[inode]);
12344 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12345 nodes.push_back(faceNodes[8]);
12347 // add new face based on volume nodes
12348 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12350 nbExisted++; // face already exsist
12354 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12359 return ( nbFree == ( nbExisted + nbCreated ));
12364 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12366 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12368 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12371 //================================================================================
12373 * \brief Creates missing boundary elements
12374 * \param elements - elements whose boundary is to be checked
12375 * \param dimension - defines type of boundary elements to create
12376 * \param group - a group to store created boundary elements in
12377 * \param targetMesh - a mesh to store created boundary elements in
12378 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12379 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12380 * boundary elements will be copied into the targetMesh
12381 * \param toAddExistingBondary - if true, not only new but also pre-existing
12382 * boundary elements will be added into the new group
12383 * \param aroundElements - if true, elements will be created on boundary of given
12384 * elements else, on boundary of the whole mesh.
12385 * \return nb of added boundary elements
12387 //================================================================================
12389 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12390 Bnd_Dimension dimension,
12391 SMESH_Group* group/*=0*/,
12392 SMESH_Mesh* targetMesh/*=0*/,
12393 bool toCopyElements/*=false*/,
12394 bool toCopyExistingBoundary/*=false*/,
12395 bool toAddExistingBondary/*= false*/,
12396 bool aroundElements/*= false*/)
12398 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12399 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12400 // hope that all elements are of the same type, do not check them all
12401 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12402 throw SALOME_Exception(LOCALIZED("wrong element type"));
12405 toCopyElements = toCopyExistingBoundary = false;
12407 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12408 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12409 int nbAddedBnd = 0;
12411 // editor adding present bnd elements and optionally holding elements to add to the group
12412 SMESH_MeshEditor* presentEditor;
12413 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12414 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12416 SMESH_MesherHelper helper( *myMesh );
12417 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12418 SMDS_VolumeTool vTool;
12419 TIDSortedElemSet avoidSet;
12420 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12423 typedef vector<const SMDS_MeshNode*> TConnectivity;
12424 TConnectivity tgtNodes;
12425 ElemFeatures elemKind( missType ), elemToCopy;
12427 vector<const SMDS_MeshElement*> presentBndElems;
12428 vector<TConnectivity> missingBndElems;
12429 vector<int> freeFacets;
12430 TConnectivity nodes, elemNodes;
12432 SMDS_ElemIteratorPtr eIt;
12433 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12434 else eIt = elemSetIterator( elements );
12436 while (eIt->more())
12438 const SMDS_MeshElement* elem = eIt->next();
12439 const int iQuad = elem->IsQuadratic();
12440 elemKind.SetQuad( iQuad );
12442 // ------------------------------------------------------------------------------------
12443 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12444 // ------------------------------------------------------------------------------------
12445 presentBndElems.clear();
12446 missingBndElems.clear();
12447 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12448 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12450 const SMDS_MeshElement* otherVol = 0;
12451 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12453 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12454 ( !aroundElements || elements.count( otherVol )))
12456 freeFacets.push_back( iface );
12458 if ( missType == SMDSAbs_Face )
12459 vTool.SetExternalNormal();
12460 for ( size_t i = 0; i < freeFacets.size(); ++i )
12462 int iface = freeFacets[i];
12463 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12464 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12465 if ( missType == SMDSAbs_Edge ) // boundary edges
12467 nodes.resize( 2+iQuad );
12468 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12470 for ( size_t j = 0; j < nodes.size(); ++j )
12471 nodes[ j ] = nn[ i+j ];
12472 if ( const SMDS_MeshElement* edge =
12473 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12474 presentBndElems.push_back( edge );
12476 missingBndElems.push_back( nodes );
12479 else // boundary face
12482 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12483 nodes.push_back( nn[inode] ); // add corner nodes
12485 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12486 nodes.push_back( nn[inode] ); // add medium nodes
12487 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12489 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12491 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12492 SMDSAbs_Face, /*noMedium=*/false ))
12493 presentBndElems.push_back( f );
12495 missingBndElems.push_back( nodes );
12497 if ( targetMesh != myMesh )
12499 // add 1D elements on face boundary to be added to a new mesh
12500 const SMDS_MeshElement* edge;
12501 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12504 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12506 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12507 if ( edge && avoidSet.insert( edge ).second )
12508 presentBndElems.push_back( edge );
12514 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12516 avoidSet.clear(), avoidSet.insert( elem );
12517 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12518 SMDS_MeshElement::iterator() );
12519 elemNodes.push_back( elemNodes[0] );
12520 nodes.resize( 2 + iQuad );
12521 const int nbLinks = elem->NbCornerNodes();
12522 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12524 nodes[0] = elemNodes[iN];
12525 nodes[1] = elemNodes[iN+1+iQuad];
12526 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12527 continue; // not free link
12529 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12530 if ( const SMDS_MeshElement* edge =
12531 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12532 presentBndElems.push_back( edge );
12534 missingBndElems.push_back( nodes );
12538 // ---------------------------------
12539 // 2. Add missing boundary elements
12540 // ---------------------------------
12541 if ( targetMesh != myMesh )
12542 // instead of making a map of nodes in this mesh and targetMesh,
12543 // we create nodes with same IDs.
12544 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12546 TConnectivity& srcNodes = missingBndElems[i];
12547 tgtNodes.resize( srcNodes.size() );
12548 for ( inode = 0; inode < srcNodes.size(); ++inode )
12549 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12550 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12552 /*noMedium=*/false))
12554 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12558 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12560 TConnectivity& nodes = missingBndElems[ i ];
12561 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12563 /*noMedium=*/false))
12565 SMDS_MeshElement* newElem =
12566 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12567 nbAddedBnd += bool( newElem );
12569 // try to set a new element to a shape
12570 if ( myMesh->HasShapeToMesh() )
12573 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12574 const size_t nbN = nodes.size() / (iQuad+1 );
12575 for ( inode = 0; inode < nbN && ok; ++inode )
12577 pair<int, TopAbs_ShapeEnum> i_stype =
12578 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12579 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12580 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12582 if ( ok && mediumShapes.size() > 1 )
12584 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12585 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12586 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12588 if (( ok = ( stype_i->first != stype_i_0.first )))
12589 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12590 aMesh->IndexToShape( stype_i_0.second ));
12593 if ( ok && mediumShapes.begin()->first == missShapeType )
12594 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12598 // ----------------------------------
12599 // 3. Copy present boundary elements
12600 // ----------------------------------
12601 if ( toCopyExistingBoundary )
12602 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12604 const SMDS_MeshElement* e = presentBndElems[i];
12605 tgtNodes.resize( e->NbNodes() );
12606 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12607 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12608 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12610 else // store present elements to add them to a group
12611 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12613 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12616 } // loop on given elements
12618 // ---------------------------------------------
12619 // 4. Fill group with boundary elements
12620 // ---------------------------------------------
12623 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12624 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12625 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12627 tgtEditor.myLastCreatedElems.Clear();
12628 tgtEditor2.myLastCreatedElems.Clear();
12630 // -----------------------
12631 // 5. Copy given elements
12632 // -----------------------
12633 if ( toCopyElements && targetMesh != myMesh )
12635 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12636 else eIt = elemSetIterator( elements );
12637 while (eIt->more())
12639 const SMDS_MeshElement* elem = eIt->next();
12640 tgtNodes.resize( elem->NbNodes() );
12641 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12642 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12643 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12645 tgtEditor.myLastCreatedElems.Clear();
12651 //================================================================================
12653 * \brief Copy node position and set \a to node on the same geometry
12655 //================================================================================
12657 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12658 const SMDS_MeshNode* to )
12660 if ( !from || !to ) return;
12662 SMDS_PositionPtr pos = from->GetPosition();
12663 if ( !pos || from->getshapeId() < 1 ) return;
12665 switch ( pos->GetTypeOfPosition() )
12667 case SMDS_TOP_3DSPACE: break;
12669 case SMDS_TOP_FACE:
12671 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12672 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12673 fPos->GetUParameter(), fPos->GetVParameter() );
12676 case SMDS_TOP_EDGE:
12678 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12679 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12680 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12683 case SMDS_TOP_VERTEX:
12685 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12688 case SMDS_TOP_UNSPEC: