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_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Edge.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 Return mesh DS
132 //================================================================================
134 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
136 return myMesh->GetMeshDS();
140 //================================================================================
142 * \brief Clears myLastCreatedNodes and myLastCreatedElems
144 //================================================================================
146 void SMESH_MeshEditor::ClearLastCreated()
148 myLastCreatedNodes.Clear();
149 myLastCreatedElems.Clear();
152 //================================================================================
154 * \brief Initializes members by an existing element
155 * \param [in] elem - the source element
156 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
158 //================================================================================
160 SMESH_MeshEditor::ElemFeatures&
161 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
165 myType = elem->GetType();
166 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
168 myIsPoly = elem->IsPoly();
171 myIsQuad = elem->IsQuadratic();
172 if ( myType == SMDSAbs_Volume && !basicOnly )
174 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
175 myPolyhedQuantities.swap( quant );
179 else if ( myType == SMDSAbs_Ball && !basicOnly )
181 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
187 //=======================================================================
191 //=======================================================================
194 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
195 const ElemFeatures& features)
197 SMDS_MeshElement* e = 0;
198 int nbnode = node.size();
199 SMESHDS_Mesh* mesh = GetMeshDS();
200 const int ID = features.myID;
202 switch ( features.myType ) {
204 if ( !features.myIsPoly ) {
206 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2] );
209 else if (nbnode == 4) {
210 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
211 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
213 else if (nbnode == 6) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
219 else if (nbnode == 7) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6] );
225 else if (nbnode == 8) {
226 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7], ID);
228 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
229 node[4], node[5], node[6], node[7] );
231 else if (nbnode == 9) {
232 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7], node[8], ID);
234 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
235 node[4], node[5], node[6], node[7], node[8] );
238 else if ( !features.myIsQuad )
240 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
241 else e = mesh->AddPolygonalFace (node );
243 else if ( nbnode % 2 == 0 ) // just a protection
245 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
246 else e = mesh->AddQuadPolygonalFace (node );
251 if ( !features.myIsPoly ) {
253 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
256 else if (nbnode == 5) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 else if (nbnode == 6) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], ID);
265 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 else if (nbnode == 8) {
269 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7], ID);
271 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7] );
274 else if (nbnode == 10) {
275 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
276 node[4], node[5], node[6], node[7],
277 node[8], node[9], ID);
278 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
279 node[4], node[5], node[6], node[7],
282 else if (nbnode == 12) {
283 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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], ID);
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10], node[11] );
290 else if (nbnode == 13) {
291 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292 node[4], node[5], node[6], node[7],
293 node[8], node[9], node[10],node[11],
295 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
300 else if (nbnode == 15) {
301 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302 node[4], node[5], node[6], node[7],
303 node[8], node[9], node[10],node[11],
304 node[12],node[13],node[14],ID);
305 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14] );
310 else if (nbnode == 20) {
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],ID);
316 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
317 node[4], node[5], node[6], node[7],
318 node[8], node[9], node[10],node[11],
319 node[12],node[13],node[14],node[15],
320 node[16],node[17],node[18],node[19] );
322 else if (nbnode == 27) {
323 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
324 node[4], node[5], node[6], node[7],
325 node[8], node[9], node[10],node[11],
326 node[12],node[13],node[14],node[15],
327 node[16],node[17],node[18],node[19],
328 node[20],node[21],node[22],node[23],
329 node[24],node[25],node[26], ID);
330 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
331 node[4], node[5], node[6], node[7],
332 node[8], node[9], node[10],node[11],
333 node[12],node[13],node[14],node[15],
334 node[16],node[17],node[18],node[19],
335 node[20],node[21],node[22],node[23],
336 node[24],node[25],node[26] );
339 else if ( !features.myIsQuad )
341 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
342 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
346 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
347 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
353 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
354 else e = mesh->AddEdge (node[0], node[1] );
356 else if ( nbnode == 3 ) {
357 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
358 else e = mesh->AddEdge (node[0], node[1], node[2] );
362 case SMDSAbs_0DElement:
364 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
365 else e = mesh->Add0DElement (node[0] );
370 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
371 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
375 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
376 else e = mesh->AddBall (node[0], features.myBallDiameter );
381 if ( e ) myLastCreatedElems.Append( e );
385 //=======================================================================
389 //=======================================================================
391 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
392 const ElemFeatures& features)
394 vector<const SMDS_MeshNode*> nodes;
395 nodes.reserve( nodeIDs.size() );
396 vector<int>::const_iterator id = nodeIDs.begin();
397 while ( id != nodeIDs.end() ) {
398 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
399 nodes.push_back( node );
403 return AddElement( nodes, features );
406 //=======================================================================
408 //purpose : Remove a node or an element.
409 // Modify a compute state of sub-meshes which become empty
410 //=======================================================================
412 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
415 myLastCreatedElems.Clear();
416 myLastCreatedNodes.Clear();
418 SMESHDS_Mesh* aMesh = GetMeshDS();
419 set< SMESH_subMesh *> smmap;
422 list<int>::const_iterator it = theIDs.begin();
423 for ( ; it != theIDs.end(); it++ ) {
424 const SMDS_MeshElement * elem;
426 elem = aMesh->FindNode( *it );
428 elem = aMesh->FindElement( *it );
432 // Notify VERTEX sub-meshes about modification
434 const SMDS_MeshNode* node = cast2Node( elem );
435 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436 if ( int aShapeID = node->getshapeId() )
437 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440 // Find sub-meshes to notify about modification
441 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442 // while ( nodeIt->more() ) {
443 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444 // const SMDS_PositionPtr& aPosition = node->GetPosition();
445 // if ( aPosition.get() ) {
446 // if ( int aShapeID = aPosition->GetShapeId() ) {
447 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448 // smmap.insert( sm );
455 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
457 aMesh->RemoveElement( elem );
461 // Notify sub-meshes about modification
462 if ( !smmap.empty() ) {
463 set< SMESH_subMesh *>::iterator smIt;
464 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468 // // Check if the whole mesh becomes empty
469 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
475 //================================================================================
477 * \brief Create 0D elements on all nodes of the given object except those
478 * nodes on which a 0D element already exists.
479 * \param elements - Elements on whose nodes to create 0D elements; if empty,
480 * the all mesh is treated
481 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483 //================================================================================
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486 TIDSortedElemSet& all0DElems )
488 SMDS_ElemIteratorPtr elemIt;
489 vector< const SMDS_MeshElement* > allNodes;
490 if ( elements.empty() )
492 allNodes.reserve( GetMeshDS()->NbNodes() );
493 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
494 while ( elemIt->more() )
495 allNodes.push_back( elemIt->next() );
497 elemIt = elemSetIterator( allNodes );
501 elemIt = elemSetIterator( elements );
504 while ( elemIt->more() )
506 const SMDS_MeshElement* e = elemIt->next();
507 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
508 while ( nodeIt->more() )
510 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
511 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
513 all0DElems.insert( it0D->next() );
515 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
516 all0DElems.insert( myLastCreatedElems.Last() );
522 //=======================================================================
523 //function : FindShape
524 //purpose : Return an index of the shape theElem is on
525 // or zero if a shape not found
526 //=======================================================================
528 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
530 myLastCreatedElems.Clear();
531 myLastCreatedNodes.Clear();
533 SMESHDS_Mesh * aMesh = GetMeshDS();
534 if ( aMesh->ShapeToMesh().IsNull() )
537 int aShapeID = theElem->getshapeId();
541 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
542 if ( sm->Contains( theElem ))
545 if ( theElem->GetType() == SMDSAbs_Node ) {
546 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
549 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
552 TopoDS_Shape aShape; // the shape a node of theElem is on
553 if ( theElem->GetType() != SMDSAbs_Node )
555 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
556 while ( nodeIt->more() ) {
557 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
558 if ((aShapeID = node->getshapeId()) > 0) {
559 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
560 if ( sm->Contains( theElem ))
562 if ( aShape.IsNull() )
563 aShape = aMesh->IndexToShape( aShapeID );
569 // None of nodes is on a proper shape,
570 // find the shape among ancestors of aShape on which a node is
571 if ( !aShape.IsNull() ) {
572 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
573 for ( ; ancIt.More(); ancIt.Next() ) {
574 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
575 if ( sm && sm->Contains( theElem ))
576 return aMesh->ShapeToIndex( ancIt.Value() );
581 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
582 while ( const SMESHDS_SubMesh* sm = smIt->next() )
583 if ( sm->Contains( theElem ))
590 //=======================================================================
591 //function : IsMedium
593 //=======================================================================
595 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
596 const SMDSAbs_ElementType typeToCheck)
598 bool isMedium = false;
599 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
600 while (it->more() && !isMedium ) {
601 const SMDS_MeshElement* elem = it->next();
602 isMedium = elem->IsMediumNode(node);
607 //=======================================================================
608 //function : shiftNodesQuadTria
609 //purpose : Shift nodes in the array corresponded to quadratic triangle
610 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
611 //=======================================================================
613 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
615 const SMDS_MeshNode* nd1 = aNodes[0];
616 aNodes[0] = aNodes[1];
617 aNodes[1] = aNodes[2];
619 const SMDS_MeshNode* nd2 = aNodes[3];
620 aNodes[3] = aNodes[4];
621 aNodes[4] = aNodes[5];
625 //=======================================================================
626 //function : nbEdgeConnectivity
627 //purpose : return number of the edges connected with the theNode.
628 // if theEdges has connections with the other type of the
629 // elements, return -1
630 //=======================================================================
632 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
634 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
636 // while(elemIt->more()) {
641 return theNode->NbInverseElements();
644 //=======================================================================
645 //function : getNodesFromTwoTria
647 //=======================================================================
649 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
650 const SMDS_MeshElement * theTria2,
651 vector< const SMDS_MeshNode*>& N1,
652 vector< const SMDS_MeshNode*>& N2)
654 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
655 if ( N1.size() < 6 ) return false;
656 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
657 if ( N2.size() < 6 ) return false;
659 int sames[3] = {-1,-1,-1};
671 if(nbsames!=2) return false;
673 shiftNodesQuadTria(N1);
675 shiftNodesQuadTria(N1);
678 i = sames[0] + sames[1] + sames[2];
680 shiftNodesQuadTria(N2);
682 // now we receive following N1 and N2 (using numeration as in the image below)
683 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
684 // i.e. first nodes from both arrays form a new diagonal
688 //=======================================================================
689 //function : InverseDiag
690 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
691 // but having other common link.
692 // Return False if args are improper
693 //=======================================================================
695 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
696 const SMDS_MeshElement * theTria2 )
698 myLastCreatedElems.Clear();
699 myLastCreatedNodes.Clear();
701 if (!theTria1 || !theTria2)
704 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
705 if (!F1) return false;
706 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
707 if (!F2) return false;
708 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
709 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
711 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
712 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
716 // put nodes in array and find out indices of the same ones
717 const SMDS_MeshNode* aNodes [6];
718 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
720 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
721 while ( it->more() ) {
722 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
724 if ( i > 2 ) // theTria2
725 // find same node of theTria1
726 for ( int j = 0; j < 3; j++ )
727 if ( aNodes[ i ] == aNodes[ j ]) {
736 return false; // theTria1 is not a triangle
737 it = theTria2->nodesIterator();
739 if ( i == 6 && it->more() )
740 return false; // theTria2 is not a triangle
743 // find indices of 1,2 and of A,B in theTria1
744 int iA = -1, iB = 0, i1 = 0, i2 = 0;
745 for ( i = 0; i < 6; i++ ) {
746 if ( sameInd [ i ] == -1 ) {
751 if ( iA >= 0) iB = i;
755 // nodes 1 and 2 should not be the same
756 if ( aNodes[ i1 ] == aNodes[ i2 ] )
760 aNodes[ iA ] = aNodes[ i2 ];
762 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
764 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
765 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
769 } // end if(F1 && F2)
771 // check case of quadratic faces
772 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
773 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
775 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
776 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
780 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
781 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
789 vector< const SMDS_MeshNode* > N1;
790 vector< const SMDS_MeshNode* > N2;
791 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
793 // now we receive following N1 and N2 (using numeration as above image)
794 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
795 // i.e. first nodes from both arrays determ new diagonal
797 vector< const SMDS_MeshNode*> N1new( N1.size() );
798 vector< const SMDS_MeshNode*> N2new( N2.size() );
799 N1new.back() = N1.back(); // central node of biquadratic
800 N2new.back() = N2.back();
801 N1new[0] = N1[0]; N2new[0] = N1[0];
802 N1new[1] = N2[0]; N2new[1] = N1[1];
803 N1new[2] = N2[1]; N2new[2] = N2[0];
804 N1new[3] = N1[4]; N2new[3] = N1[3];
805 N1new[4] = N2[3]; N2new[4] = N2[5];
806 N1new[5] = N1[5]; N2new[5] = N1[4];
807 // change nodes in faces
808 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
809 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
811 // move the central node of biquadratic triangle
812 SMESH_MesherHelper helper( *GetMesh() );
813 for ( int is2nd = 0; is2nd < 2; ++is2nd )
815 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
816 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
817 if ( nodes.size() < 7 )
819 helper.SetSubShape( tria->getshapeId() );
820 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
824 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
825 SMESH_TNodeXYZ( nodes[4] ) +
826 SMESH_TNodeXYZ( nodes[5] )) / 3.;
831 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
832 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
833 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
835 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
836 xyz = S->Value( uv.X(), uv.Y() );
837 xyz.Transform( loc );
838 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
839 nodes[6]->getshapeId() > 0 )
840 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
842 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
847 //=======================================================================
848 //function : findTriangles
849 //purpose : find triangles sharing theNode1-theNode2 link
850 //=======================================================================
852 static bool findTriangles(const SMDS_MeshNode * theNode1,
853 const SMDS_MeshNode * theNode2,
854 const SMDS_MeshElement*& theTria1,
855 const SMDS_MeshElement*& theTria2)
857 if ( !theNode1 || !theNode2 ) return false;
859 theTria1 = theTria2 = 0;
861 set< const SMDS_MeshElement* > emap;
862 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
864 const SMDS_MeshElement* elem = it->next();
865 if ( elem->NbCornerNodes() == 3 )
868 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
870 const SMDS_MeshElement* elem = it->next();
871 if ( emap.count( elem )) {
879 // theTria1 must be element with minimum ID
880 if ( theTria2->GetID() < theTria1->GetID() )
881 std::swap( theTria2, theTria1 );
889 //=======================================================================
890 //function : InverseDiag
891 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
892 // with ones built on the same 4 nodes but having other common link.
893 // Return false if proper faces not found
894 //=======================================================================
896 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
897 const SMDS_MeshNode * theNode2)
899 myLastCreatedElems.Clear();
900 myLastCreatedNodes.Clear();
902 const SMDS_MeshElement *tr1, *tr2;
903 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
906 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
907 if (!F1) return false;
908 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
909 if (!F2) return false;
910 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
911 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
913 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
914 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
918 // put nodes in array
919 // and find indices of 1,2 and of A in tr1 and of B in tr2
920 int i, iA1 = 0, i1 = 0;
921 const SMDS_MeshNode* aNodes1 [3];
922 SMDS_ElemIteratorPtr it;
923 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
924 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes1[ i ] == theNode1 )
926 iA1 = i; // node A in tr1
927 else if ( aNodes1[ i ] != theNode2 )
931 const SMDS_MeshNode* aNodes2 [3];
932 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
933 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
934 if ( aNodes2[ i ] == theNode2 )
935 iB2 = i; // node B in tr2
936 else if ( aNodes2[ i ] != theNode1 )
940 // nodes 1 and 2 should not be the same
941 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
945 aNodes1[ iA1 ] = aNodes2[ i2 ];
947 aNodes2[ iB2 ] = aNodes1[ i1 ];
949 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
950 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
955 // check case of quadratic faces
956 return InverseDiag(tr1,tr2);
959 //=======================================================================
960 //function : getQuadrangleNodes
961 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
962 // fusion of triangles tr1 and tr2 having shared link on
963 // theNode1 and theNode2
964 //=======================================================================
966 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
967 const SMDS_MeshNode * theNode1,
968 const SMDS_MeshNode * theNode2,
969 const SMDS_MeshElement * tr1,
970 const SMDS_MeshElement * tr2 )
972 if( tr1->NbNodes() != tr2->NbNodes() )
974 // find the 4-th node to insert into tr1
975 const SMDS_MeshNode* n4 = 0;
976 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
978 while ( !n4 && i<3 ) {
979 const SMDS_MeshNode * n = cast2Node( it->next() );
981 bool isDiag = ( n == theNode1 || n == theNode2 );
985 // Make an array of nodes to be in a quadrangle
986 int iNode = 0, iFirstDiag = -1;
987 it = tr1->nodesIterator();
990 const SMDS_MeshNode * n = cast2Node( it->next() );
992 bool isDiag = ( n == theNode1 || n == theNode2 );
994 if ( iFirstDiag < 0 )
996 else if ( iNode - iFirstDiag == 1 )
997 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
999 else if ( n == n4 ) {
1000 return false; // tr1 and tr2 should not have all the same nodes
1002 theQuadNodes[ iNode++ ] = n;
1004 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1005 theQuadNodes[ iNode ] = n4;
1010 //=======================================================================
1011 //function : DeleteDiag
1012 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1013 // with a quadrangle built on the same 4 nodes.
1014 // Return false if proper faces not found
1015 //=======================================================================
1017 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1018 const SMDS_MeshNode * theNode2)
1020 myLastCreatedElems.Clear();
1021 myLastCreatedNodes.Clear();
1023 const SMDS_MeshElement *tr1, *tr2;
1024 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1027 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1028 if (!F1) return false;
1029 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1030 if (!F2) return false;
1031 SMESHDS_Mesh * aMesh = GetMeshDS();
1033 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1034 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1036 const SMDS_MeshNode* aNodes [ 4 ];
1037 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1040 const SMDS_MeshElement* newElem = 0;
1041 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1042 myLastCreatedElems.Append(newElem);
1043 AddToSameGroups( newElem, tr1, aMesh );
1044 int aShapeId = tr1->getshapeId();
1047 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1049 aMesh->RemoveElement( tr1 );
1050 aMesh->RemoveElement( tr2 );
1055 // check case of quadratic faces
1056 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1058 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1062 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1063 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1071 vector< const SMDS_MeshNode* > N1;
1072 vector< const SMDS_MeshNode* > N2;
1073 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1075 // now we receive following N1 and N2 (using numeration as above image)
1076 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1077 // i.e. first nodes from both arrays determ new diagonal
1079 const SMDS_MeshNode* aNodes[8];
1089 const SMDS_MeshElement* newElem = 0;
1090 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1091 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1092 myLastCreatedElems.Append(newElem);
1093 AddToSameGroups( newElem, tr1, aMesh );
1094 int aShapeId = tr1->getshapeId();
1097 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1099 aMesh->RemoveElement( tr1 );
1100 aMesh->RemoveElement( tr2 );
1102 // remove middle node (9)
1103 GetMeshDS()->RemoveNode( N1[4] );
1108 //=======================================================================
1109 //function : Reorient
1110 //purpose : Reverse theElement orientation
1111 //=======================================================================
1113 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1115 myLastCreatedElems.Clear();
1116 myLastCreatedNodes.Clear();
1120 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1121 if ( !it || !it->more() )
1124 const SMDSAbs_ElementType type = theElem->GetType();
1125 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1128 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1129 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1131 const SMDS_VtkVolume* aPolyedre =
1132 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1134 MESSAGE("Warning: bad volumic element");
1137 const int nbFaces = aPolyedre->NbFaces();
1138 vector<const SMDS_MeshNode *> poly_nodes;
1139 vector<int> quantities (nbFaces);
1141 // reverse each face of the polyedre
1142 for (int iface = 1; iface <= nbFaces; iface++) {
1143 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1144 quantities[iface - 1] = nbFaceNodes;
1146 for (inode = nbFaceNodes; inode >= 1; inode--) {
1147 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1148 poly_nodes.push_back(curNode);
1151 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1153 else // other elements
1155 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1156 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1157 if ( interlace.empty() )
1159 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1163 SMDS_MeshCell::applyInterlace( interlace, nodes );
1165 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1170 //================================================================================
1172 * \brief Reorient faces.
1173 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1174 * \param theDirection - desired direction of normal of \a theFace
1175 * \param theFace - one of \a theFaces that sould be oriented according to
1176 * \a theDirection and whose orientation defines orientation of other faces
1177 * \return number of reoriented faces.
1179 //================================================================================
1181 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1182 const gp_Dir& theDirection,
1183 const SMDS_MeshElement * theFace)
1186 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1188 if ( theFaces.empty() )
1190 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1191 while ( fIt->more() )
1192 theFaces.insert( theFaces.end(), fIt->next() );
1195 // orient theFace according to theDirection
1197 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1198 if ( normal * theDirection.XYZ() < 0 )
1199 nbReori += Reorient( theFace );
1201 // Orient other faces
1203 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1204 TIDSortedElemSet avoidSet;
1205 set< SMESH_TLink > checkedLinks;
1206 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1208 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1209 theFaces.erase( theFace );
1210 startFaces.insert( theFace );
1212 int nodeInd1, nodeInd2;
1213 const SMDS_MeshElement* otherFace;
1214 vector< const SMDS_MeshElement* > facesNearLink;
1215 vector< std::pair< int, int > > nodeIndsOfFace;
1217 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1218 while ( !startFaces.empty() )
1220 startFace = startFaces.begin();
1221 theFace = *startFace;
1222 startFaces.erase( startFace );
1223 if ( !visitedFaces.insert( theFace ).second )
1227 avoidSet.insert(theFace);
1229 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1231 const int nbNodes = theFace->NbCornerNodes();
1232 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1234 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1235 linkIt_isNew = checkedLinks.insert( link );
1236 if ( !linkIt_isNew.second )
1238 // link has already been checked and won't be encountered more
1239 // if the group (theFaces) is manifold
1240 //checkedLinks.erase( linkIt_isNew.first );
1244 facesNearLink.clear();
1245 nodeIndsOfFace.clear();
1246 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1248 &nodeInd1, &nodeInd2 )))
1249 if ( otherFace != theFace)
1251 facesNearLink.push_back( otherFace );
1252 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1253 avoidSet.insert( otherFace );
1255 if ( facesNearLink.size() > 1 )
1257 // NON-MANIFOLD mesh shell !
1258 // select a face most co-directed with theFace,
1259 // other faces won't be visited this time
1261 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1262 double proj, maxProj = -1;
1263 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1264 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1265 if (( proj = Abs( NF * NOF )) > maxProj ) {
1267 otherFace = facesNearLink[i];
1268 nodeInd1 = nodeIndsOfFace[i].first;
1269 nodeInd2 = nodeIndsOfFace[i].second;
1272 // not to visit rejected faces
1273 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1274 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1275 visitedFaces.insert( facesNearLink[i] );
1277 else if ( facesNearLink.size() == 1 )
1279 otherFace = facesNearLink[0];
1280 nodeInd1 = nodeIndsOfFace.back().first;
1281 nodeInd2 = nodeIndsOfFace.back().second;
1283 if ( otherFace && otherFace != theFace)
1285 // link must be reverse in otherFace if orientation ot otherFace
1286 // is same as that of theFace
1287 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1289 nbReori += Reorient( otherFace );
1291 startFaces.insert( otherFace );
1294 std::swap( link.first, link.second ); // reverse the link
1300 //================================================================================
1302 * \brief Reorient faces basing on orientation of adjacent volumes.
1303 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1304 * \param theVolumes - reference volumes.
1305 * \param theOutsideNormal - to orient faces to have their normal
1306 * pointing either \a outside or \a inside the adjacent volumes.
1307 * \return number of reoriented faces.
1309 //================================================================================
1311 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1312 TIDSortedElemSet & theVolumes,
1313 const bool theOutsideNormal)
1317 SMDS_ElemIteratorPtr faceIt;
1318 if ( theFaces.empty() )
1319 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1321 faceIt = elemSetIterator( theFaces );
1323 vector< const SMDS_MeshNode* > faceNodes;
1324 TIDSortedElemSet checkedVolumes;
1325 set< const SMDS_MeshNode* > faceNodesSet;
1326 SMDS_VolumeTool volumeTool;
1328 while ( faceIt->more() ) // loop on given faces
1330 const SMDS_MeshElement* face = faceIt->next();
1331 if ( face->GetType() != SMDSAbs_Face )
1334 const size_t nbCornersNodes = face->NbCornerNodes();
1335 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1337 checkedVolumes.clear();
1338 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1339 while ( vIt->more() )
1341 const SMDS_MeshElement* volume = vIt->next();
1343 if ( !checkedVolumes.insert( volume ).second )
1345 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1348 // is volume adjacent?
1349 bool allNodesCommon = true;
1350 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1351 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1352 if ( !allNodesCommon )
1355 // get nodes of a corresponding volume facet
1356 faceNodesSet.clear();
1357 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1358 volumeTool.Set( volume );
1359 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1360 if ( facetID < 0 ) continue;
1361 volumeTool.SetExternalNormal();
1362 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1364 // compare order of faceNodes and facetNodes
1365 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1367 for ( int i = 0; i < 2; ++i )
1369 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1370 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1371 if ( faceNodes[ iN ] == n )
1377 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1378 if ( isOutside != theOutsideNormal )
1379 nbReori += Reorient( face );
1381 } // loop on given faces
1386 //=======================================================================
1387 //function : getBadRate
1389 //=======================================================================
1391 static double getBadRate (const SMDS_MeshElement* theElem,
1392 SMESH::Controls::NumericalFunctorPtr& theCrit)
1394 SMESH::Controls::TSequenceOfXYZ P;
1395 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1397 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1398 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1401 //=======================================================================
1402 //function : QuadToTri
1403 //purpose : Cut quadrangles into triangles.
1404 // theCrit is used to select a diagonal to cut
1405 //=======================================================================
1407 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1408 SMESH::Controls::NumericalFunctorPtr theCrit)
1410 myLastCreatedElems.Clear();
1411 myLastCreatedNodes.Clear();
1413 if ( !theCrit.get() )
1416 SMESHDS_Mesh * aMesh = GetMeshDS();
1418 Handle(Geom_Surface) surface;
1419 SMESH_MesherHelper helper( *GetMesh() );
1421 TIDSortedElemSet::iterator itElem;
1422 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1424 const SMDS_MeshElement* elem = *itElem;
1425 if ( !elem || elem->GetType() != SMDSAbs_Face )
1427 if ( elem->NbCornerNodes() != 4 )
1430 // retrieve element nodes
1431 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1433 // compare two sets of possible triangles
1434 double aBadRate1, aBadRate2; // to what extent a set is bad
1435 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1436 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1437 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1439 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1440 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1441 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1443 const int aShapeId = FindShape( elem );
1444 const SMDS_MeshElement* newElem1 = 0;
1445 const SMDS_MeshElement* newElem2 = 0;
1447 if ( !elem->IsQuadratic() ) // split liner quadrangle
1449 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1450 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1451 if ( aBadRate1 <= aBadRate2 ) {
1452 // tr1 + tr2 is better
1453 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1454 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1457 // tr3 + tr4 is better
1458 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1459 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1462 else // split quadratic quadrangle
1464 helper.SetIsQuadratic( true );
1465 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1467 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1468 if ( aNodes.size() == 9 )
1470 helper.SetIsBiQuadratic( true );
1471 if ( aBadRate1 <= aBadRate2 )
1472 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1474 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1476 // create a new element
1477 if ( aBadRate1 <= aBadRate2 ) {
1478 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1479 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1482 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1483 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1487 // care of a new element
1489 myLastCreatedElems.Append(newElem1);
1490 myLastCreatedElems.Append(newElem2);
1491 AddToSameGroups( newElem1, elem, aMesh );
1492 AddToSameGroups( newElem2, elem, aMesh );
1494 // put a new triangle on the same shape
1496 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1497 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1499 aMesh->RemoveElement( elem );
1504 //=======================================================================
1506 * \brief Split each of given quadrangles into 4 triangles.
1507 * \param theElems - The faces to be splitted. If empty all faces are split.
1509 //=======================================================================
1511 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1513 myLastCreatedElems.Clear();
1514 myLastCreatedNodes.Clear();
1516 SMESH_MesherHelper helper( *GetMesh() );
1517 helper.SetElementsOnShape( true );
1519 SMDS_ElemIteratorPtr faceIt;
1520 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1521 else faceIt = elemSetIterator( theElems );
1524 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1526 vector< const SMDS_MeshNode* > nodes;
1527 SMESHDS_SubMesh* subMeshDS = 0;
1529 Handle(Geom_Surface) surface;
1530 TopLoc_Location loc;
1532 while ( faceIt->more() )
1534 const SMDS_MeshElement* quad = faceIt->next();
1535 if ( !quad || quad->NbCornerNodes() != 4 )
1538 // get a surface the quad is on
1540 if ( quad->getshapeId() < 1 )
1543 helper.SetSubShape( 0 );
1546 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1548 helper.SetSubShape( quad->getshapeId() );
1549 if ( !helper.GetSubShape().IsNull() &&
1550 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1552 F = TopoDS::Face( helper.GetSubShape() );
1553 surface = BRep_Tool::Surface( F, loc );
1554 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1558 helper.SetSubShape( 0 );
1563 // create a central node
1565 const SMDS_MeshNode* nCentral;
1566 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1568 if ( nodes.size() == 9 )
1570 nCentral = nodes.back();
1577 for ( ; iN < nodes.size(); ++iN )
1578 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1580 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1581 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1583 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1584 xyz[0], xyz[1], xyz[2], xyz[3],
1585 xyz[4], xyz[5], xyz[6], xyz[7] );
1589 for ( ; iN < nodes.size(); ++iN )
1590 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1592 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1593 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1595 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1596 uv[0], uv[1], uv[2], uv[3],
1597 uv[4], uv[5], uv[6], uv[7] );
1599 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1603 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1604 uv[8].X(), uv[8].Y() );
1605 myLastCreatedNodes.Append( nCentral );
1608 // create 4 triangles
1610 helper.SetIsQuadratic ( nodes.size() > 4 );
1611 helper.SetIsBiQuadratic( nodes.size() == 9 );
1612 if ( helper.GetIsQuadratic() )
1613 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1615 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1617 for ( int i = 0; i < 4; ++i )
1619 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1622 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1623 myLastCreatedElems.Append( tria );
1628 //=======================================================================
1629 //function : BestSplit
1630 //purpose : Find better diagonal for cutting.
1631 //=======================================================================
1633 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1634 SMESH::Controls::NumericalFunctorPtr theCrit)
1636 myLastCreatedElems.Clear();
1637 myLastCreatedNodes.Clear();
1642 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1645 if( theQuad->NbNodes()==4 ||
1646 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1648 // retrieve element nodes
1649 const SMDS_MeshNode* aNodes [4];
1650 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1652 //while (itN->more())
1654 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1656 // compare two sets of possible triangles
1657 double aBadRate1, aBadRate2; // to what extent a set is bad
1658 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1659 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1660 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1662 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1663 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1664 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1665 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1666 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1667 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1668 return 1; // diagonal 1-3
1670 return 2; // diagonal 2-4
1677 // Methods of splitting volumes into tetra
1679 const int theHexTo5_1[5*4+1] =
1681 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1683 const int theHexTo5_2[5*4+1] =
1685 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1687 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1689 const int theHexTo6_1[6*4+1] =
1691 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
1693 const int theHexTo6_2[6*4+1] =
1695 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
1697 const int theHexTo6_3[6*4+1] =
1699 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
1701 const int theHexTo6_4[6*4+1] =
1703 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
1705 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1707 const int thePyraTo2_1[2*4+1] =
1709 0, 1, 2, 4, 0, 2, 3, 4, -1
1711 const int thePyraTo2_2[2*4+1] =
1713 1, 2, 3, 4, 1, 3, 0, 4, -1
1715 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1717 const int thePentaTo3_1[3*4+1] =
1719 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1721 const int thePentaTo3_2[3*4+1] =
1723 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1725 const int thePentaTo3_3[3*4+1] =
1727 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1729 const int thePentaTo3_4[3*4+1] =
1731 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1733 const int thePentaTo3_5[3*4+1] =
1735 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1737 const int thePentaTo3_6[3*4+1] =
1739 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1741 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1742 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1744 // Methods of splitting hexahedron into prisms
1746 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1748 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
1750 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1752 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
1754 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1756 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
1759 const int theHexTo2Prisms_BT_1[6*2+1] =
1761 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1763 const int theHexTo2Prisms_BT_2[6*2+1] =
1765 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1767 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1769 const int theHexTo2Prisms_LR_1[6*2+1] =
1771 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1773 const int theHexTo2Prisms_LR_2[6*2+1] =
1775 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1777 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1779 const int theHexTo2Prisms_FB_1[6*2+1] =
1781 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1783 const int theHexTo2Prisms_FB_2[6*2+1] =
1785 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1787 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1790 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1793 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1794 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1795 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1796 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1802 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1803 bool _baryNode; //!< additional node is to be created at cell barycenter
1804 bool _ownConn; //!< to delete _connectivity in destructor
1805 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1807 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1808 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1809 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1810 bool hasFacet( const TTriangleFacet& facet ) const
1812 if ( _nbCorners == 4 )
1814 const int* tetConn = _connectivity;
1815 for ( ; tetConn[0] >= 0; tetConn += 4 )
1816 if (( facet.contains( tetConn[0] ) +
1817 facet.contains( tetConn[1] ) +
1818 facet.contains( tetConn[2] ) +
1819 facet.contains( tetConn[3] )) == 3 )
1822 else // prism, _nbCorners == 6
1824 const int* prismConn = _connectivity;
1825 for ( ; prismConn[0] >= 0; prismConn += 6 )
1827 if (( facet.contains( prismConn[0] ) &&
1828 facet.contains( prismConn[1] ) &&
1829 facet.contains( prismConn[2] ))
1831 ( facet.contains( prismConn[3] ) &&
1832 facet.contains( prismConn[4] ) &&
1833 facet.contains( prismConn[5] )))
1841 //=======================================================================
1843 * \brief return TSplitMethod for the given element to split into tetrahedra
1845 //=======================================================================
1847 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1849 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1851 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1852 // an edge and a face barycenter; tertaherdons are based on triangles and
1853 // a volume barycenter
1854 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1856 // Find out how adjacent volumes are split
1858 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1859 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1860 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1862 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1863 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1864 if ( nbNodes < 4 ) continue;
1866 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1867 const int* nInd = vol.GetFaceNodesIndices( iF );
1870 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1871 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1872 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1873 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1877 int iCom = 0; // common node of triangle faces to split into
1878 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1880 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1881 nInd[ iQ * ( (iCom+1)%nbNodes )],
1882 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1883 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1884 nInd[ iQ * ( (iCom+2)%nbNodes )],
1885 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1886 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1888 triaSplits.push_back( t012 );
1889 triaSplits.push_back( t023 );
1894 if ( !triaSplits.empty() )
1895 hasAdjacentSplits = true;
1898 // Among variants of split method select one compliant with adjacent volumes
1900 TSplitMethod method;
1901 if ( !vol.Element()->IsPoly() && !is24TetMode )
1903 int nbVariants = 2, nbTet = 0;
1904 const int** connVariants = 0;
1905 switch ( vol.Element()->GetEntityType() )
1907 case SMDSEntity_Hexa:
1908 case SMDSEntity_Quad_Hexa:
1909 case SMDSEntity_TriQuad_Hexa:
1910 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1911 connVariants = theHexTo5, nbTet = 5;
1913 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1915 case SMDSEntity_Pyramid:
1916 case SMDSEntity_Quad_Pyramid:
1917 connVariants = thePyraTo2; nbTet = 2;
1919 case SMDSEntity_Penta:
1920 case SMDSEntity_Quad_Penta:
1921 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1926 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1928 // check method compliancy with adjacent tetras,
1929 // all found splits must be among facets of tetras described by this method
1930 method = TSplitMethod( nbTet, connVariants[variant] );
1931 if ( hasAdjacentSplits && method._nbSplits > 0 )
1933 bool facetCreated = true;
1934 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1936 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1937 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1938 facetCreated = method.hasFacet( *facet );
1940 if ( !facetCreated )
1941 method = TSplitMethod(0); // incompatible method
1945 if ( method._nbSplits < 1 )
1947 // No standard method is applicable, use a generic solution:
1948 // each facet of a volume is split into triangles and
1949 // each of triangles and a volume barycenter form a tetrahedron.
1951 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1953 int* connectivity = new int[ maxTetConnSize + 1 ];
1954 method._connectivity = connectivity;
1955 method._ownConn = true;
1956 method._baryNode = !isHex27; // to create central node or not
1959 int baryCenInd = vol.NbNodes() - int( isHex27 );
1960 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1962 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1963 const int* nInd = vol.GetFaceNodesIndices( iF );
1964 // find common node of triangle facets of tetra to create
1965 int iCommon = 0; // index in linear numeration
1966 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1967 if ( !triaSplits.empty() )
1970 const TTriangleFacet* facet = &triaSplits.front();
1971 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1972 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1973 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1976 else if ( nbNodes > 3 && !is24TetMode )
1978 // find the best method of splitting into triangles by aspect ratio
1979 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1980 map< double, int > badness2iCommon;
1981 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1982 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1983 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1986 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1988 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1989 nodes[ iQ*((iLast-1)%nbNodes)],
1990 nodes[ iQ*((iLast )%nbNodes)]);
1991 badness += getBadRate( &tria, aspectRatio );
1993 badness2iCommon.insert( make_pair( badness, iCommon ));
1995 // use iCommon with lowest badness
1996 iCommon = badness2iCommon.begin()->second;
1998 if ( iCommon >= nbNodes )
1999 iCommon = 0; // something wrong
2001 // fill connectivity of tetrahedra based on a current face
2002 int nbTet = nbNodes - 2;
2003 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2008 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2009 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2013 method._faceBaryNode[ iF ] = 0;
2014 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2017 for ( int i = 0; i < nbTet; ++i )
2019 int i1 = i, i2 = (i+1) % nbNodes;
2020 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2021 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2022 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2023 connectivity[ connSize++ ] = faceBaryCenInd;
2024 connectivity[ connSize++ ] = baryCenInd;
2029 for ( int i = 0; i < nbTet; ++i )
2031 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2032 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2033 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2034 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2035 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2036 connectivity[ connSize++ ] = baryCenInd;
2039 method._nbSplits += nbTet;
2041 } // loop on volume faces
2043 connectivity[ connSize++ ] = -1;
2045 } // end of generic solution
2049 //=======================================================================
2051 * \brief return TSplitMethod to split haxhedron into prisms
2053 //=======================================================================
2055 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2056 const int methodFlags,
2057 const int facetToSplit)
2059 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2061 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2063 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2065 static TSplitMethod to4methods[4]; // order BT, LR, FB
2066 if ( to4methods[iF]._nbSplits == 0 )
2070 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2071 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2072 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2075 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2076 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2077 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2080 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2081 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2082 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2084 default: return to4methods[3];
2086 to4methods[iF]._nbSplits = 4;
2087 to4methods[iF]._nbCorners = 6;
2089 return to4methods[iF];
2091 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2093 TSplitMethod method;
2095 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2097 const int nbVariants = 2, nbSplits = 2;
2098 const int** connVariants = 0;
2100 case 0: connVariants = theHexTo2Prisms_BT; break;
2101 case 1: connVariants = theHexTo2Prisms_LR; break;
2102 case 2: connVariants = theHexTo2Prisms_FB; break;
2103 default: return method;
2106 // look for prisms adjacent via facetToSplit and an opposite one
2107 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2109 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2110 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2111 if ( nbNodes != 4 ) return method;
2113 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2114 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2115 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2117 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2119 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2124 // there are adjacent prism
2125 for ( int variant = 0; variant < nbVariants; ++variant )
2127 // check method compliancy with adjacent prisms,
2128 // the found prism facets must be among facets of prisms described by current method
2129 method._nbSplits = nbSplits;
2130 method._nbCorners = 6;
2131 method._connectivity = connVariants[ variant ];
2132 if ( method.hasFacet( *t ))
2137 // No adjacent prisms. Select a variant with a best aspect ratio.
2139 double badness[2] = { 0., 0. };
2140 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2141 const SMDS_MeshNode** nodes = vol.GetNodes();
2142 for ( int variant = 0; variant < nbVariants; ++variant )
2143 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2145 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2146 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2148 method._connectivity = connVariants[ variant ];
2149 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2150 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2151 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2153 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2156 badness[ variant ] += getBadRate( &tria, aspectRatio );
2158 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2160 method._nbSplits = nbSplits;
2161 method._nbCorners = 6;
2162 method._connectivity = connVariants[ iBetter ];
2167 //================================================================================
2169 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2171 //================================================================================
2173 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2174 const SMDSAbs_GeometryType geom ) const
2176 // find the tetrahedron including the three nodes of facet
2177 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2178 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2179 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2180 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2181 while ( volIt1->more() )
2183 const SMDS_MeshElement* v = volIt1->next();
2184 if ( v->GetGeomType() != geom )
2186 const int lastCornerInd = v->NbCornerNodes() - 1;
2187 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2188 continue; // medium node not allowed
2189 const int ind2 = v->GetNodeIndex( n2 );
2190 if ( ind2 < 0 || lastCornerInd < ind2 )
2192 const int ind3 = v->GetNodeIndex( n3 );
2193 if ( ind3 < 0 || lastCornerInd < ind3 )
2200 //=======================================================================
2202 * \brief A key of a face of volume
2204 //=======================================================================
2206 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2208 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2210 TIDSortedNodeSet sortedNodes;
2211 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2212 int nbNodes = vol.NbFaceNodes( iF );
2213 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2214 for ( int i = 0; i < nbNodes; i += iQ )
2215 sortedNodes.insert( fNodes[i] );
2216 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2217 first.first = (*(n++))->GetID();
2218 first.second = (*(n++))->GetID();
2219 second.first = (*(n++))->GetID();
2220 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2225 //=======================================================================
2226 //function : SplitVolumes
2227 //purpose : Split volume elements into tetrahedra or prisms.
2228 // If facet ID < 0, element is split into tetrahedra,
2229 // else a hexahedron is split into prisms so that the given facet is
2230 // split into triangles
2231 //=======================================================================
2233 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2234 const int theMethodFlags)
2236 SMDS_VolumeTool volTool;
2237 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2238 fHelper.ToFixNodeParameters( true );
2240 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2241 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2243 SMESH_SequenceOfElemPtr newNodes, newElems;
2245 // map face of volume to it's baricenrtic node
2246 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2248 vector<const SMDS_MeshElement* > splitVols;
2250 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2251 for ( ; elem2facet != theElems.end(); ++elem2facet )
2253 const SMDS_MeshElement* elem = elem2facet->first;
2254 const int facetToSplit = elem2facet->second;
2255 if ( elem->GetType() != SMDSAbs_Volume )
2257 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2258 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2261 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2263 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2264 getTetraSplitMethod( volTool, theMethodFlags ) :
2265 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2266 if ( splitMethod._nbSplits < 1 ) continue;
2268 // find submesh to add new tetras to
2269 if ( !subMesh || !subMesh->Contains( elem ))
2271 int shapeID = FindShape( elem );
2272 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2273 subMesh = GetMeshDS()->MeshElements( shapeID );
2276 if ( elem->IsQuadratic() )
2279 // add quadratic links to the helper
2280 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2282 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2283 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2284 for ( int iN = 0; iN < nbN; iN += iQ )
2285 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2287 helper.SetIsQuadratic( true );
2292 helper.SetIsQuadratic( false );
2294 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2295 volTool.GetNodes() + elem->NbNodes() );
2296 helper.SetElementsOnShape( true );
2297 if ( splitMethod._baryNode )
2299 // make a node at barycenter
2300 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2301 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2302 nodes.push_back( gcNode );
2303 newNodes.Append( gcNode );
2305 if ( !splitMethod._faceBaryNode.empty() )
2307 // make or find baricentric nodes of faces
2308 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2309 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2311 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2312 volFace2BaryNode.insert
2313 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2316 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2317 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2319 nodes.push_back( iF_n->second = f_n->second );
2324 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2325 const int* volConn = splitMethod._connectivity;
2326 if ( splitMethod._nbCorners == 4 ) // tetra
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] ]));
2333 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2334 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2335 nodes[ volConn[1] ],
2336 nodes[ volConn[2] ],
2337 nodes[ volConn[3] ],
2338 nodes[ volConn[4] ],
2339 nodes[ volConn[5] ]));
2341 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2343 // Split faces on sides of the split volume
2345 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2346 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2348 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2349 if ( nbNodes < 4 ) continue;
2351 // find an existing face
2352 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2353 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2354 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2355 /*noMedium=*/false))
2358 helper.SetElementsOnShape( false );
2359 vector< const SMDS_MeshElement* > triangles;
2361 // find submesh to add new triangles in
2362 if ( !fSubMesh || !fSubMesh->Contains( face ))
2364 int shapeID = FindShape( face );
2365 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2367 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2368 if ( iF_n != splitMethod._faceBaryNode.end() )
2370 const SMDS_MeshNode *baryNode = iF_n->second;
2371 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2373 const SMDS_MeshNode* n1 = fNodes[iN];
2374 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2375 const SMDS_MeshNode *n3 = baryNode;
2376 if ( !volTool.IsFaceExternal( iF ))
2378 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2380 if ( fSubMesh ) // update position of the bary node on geometry
2383 subMesh->RemoveNode( baryNode, false );
2384 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2385 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2386 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2388 fHelper.SetSubShape( s );
2389 gp_XY uv( 1e100, 1e100 );
2391 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2392 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2395 // node is too far from the surface
2396 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2397 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2398 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2405 // among possible triangles create ones discribed by split method
2406 const int* nInd = volTool.GetFaceNodesIndices( iF );
2407 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2408 int iCom = 0; // common node of triangle faces to split into
2409 list< TTriangleFacet > facets;
2410 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2412 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2413 nInd[ iQ * ( (iCom+1)%nbNodes )],
2414 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2415 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2416 nInd[ iQ * ( (iCom+2)%nbNodes )],
2417 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2418 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2420 facets.push_back( t012 );
2421 facets.push_back( t023 );
2422 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2423 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2424 nInd[ iQ * ((iLast-1)%nbNodes )],
2425 nInd[ iQ * ((iLast )%nbNodes )]));
2429 list< TTriangleFacet >::iterator facet = facets.begin();
2430 if ( facet == facets.end() )
2432 for ( ; facet != facets.end(); ++facet )
2434 if ( !volTool.IsFaceExternal( iF ))
2435 swap( facet->_n2, facet->_n3 );
2436 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2437 volNodes[ facet->_n2 ],
2438 volNodes[ facet->_n3 ]));
2441 for ( size_t i = 0; i < triangles.size(); ++i )
2443 if ( !triangles[ i ]) continue;
2445 fSubMesh->AddElement( triangles[ i ]);
2446 newElems.Append( triangles[ i ]);
2448 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2449 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2451 } // while a face based on facet nodes exists
2452 } // loop on volume faces to split them into triangles
2454 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2456 if ( geomType == SMDSEntity_TriQuad_Hexa )
2458 // remove medium nodes that could become free
2459 for ( int i = 20; i < volTool.NbNodes(); ++i )
2460 if ( volNodes[i]->NbInverseElements() == 0 )
2461 GetMeshDS()->RemoveNode( volNodes[i] );
2463 } // loop on volumes to split
2465 myLastCreatedNodes = newNodes;
2466 myLastCreatedElems = newElems;
2469 //=======================================================================
2470 //function : GetHexaFacetsToSplit
2471 //purpose : For hexahedra that will be split into prisms, finds facets to
2472 // split into triangles. Only hexahedra adjacent to the one closest
2473 // to theFacetNormal.Location() are returned.
2474 //param [in,out] theHexas - the hexahedra
2475 //param [in] theFacetNormal - facet normal
2476 //param [out] theFacets - the hexahedra and found facet IDs
2477 //=======================================================================
2479 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2480 const gp_Ax1& theFacetNormal,
2481 TFacetOfElem & theFacets)
2483 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2485 // Find a hexa closest to the location of theFacetNormal
2487 const SMDS_MeshElement* startHex;
2489 // get SMDS_ElemIteratorPtr on theHexas
2490 typedef const SMDS_MeshElement* TValue;
2491 typedef TIDSortedElemSet::iterator TSetIterator;
2492 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2493 typedef SMDS_MeshElement::GeomFilter TFilter;
2494 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2495 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2496 ( new TElemSetIter( theHexas.begin(),
2498 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2500 SMESH_ElementSearcher* searcher =
2501 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2503 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2508 throw SALOME_Exception( THIS_METHOD "startHex not found");
2511 // Select a facet of startHex by theFacetNormal
2513 SMDS_VolumeTool vTool( startHex );
2514 double norm[3], dot, maxDot = 0;
2516 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2517 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2519 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2527 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2529 // Fill theFacets starting from facetID of startHex
2531 // facets used for seach of volumes adjacent to already treated ones
2532 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2533 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2534 TFacetMap facetsToCheck;
2536 set<const SMDS_MeshNode*> facetNodes;
2537 const SMDS_MeshElement* curHex;
2539 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2543 // move in two directions from startHex via facetID
2544 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2547 int curFacet = facetID;
2548 if ( is2nd ) // do not treat startHex twice
2550 vTool.Set( curHex );
2551 if ( vTool.IsFreeFace( curFacet, &curHex ))
2557 vTool.GetFaceNodes( curFacet, facetNodes );
2558 vTool.Set( curHex );
2559 curFacet = vTool.GetFaceIndex( facetNodes );
2564 // store a facet to split
2565 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2567 theFacets.insert( make_pair( curHex, -1 ));
2570 if ( !allHex && !theHexas.count( curHex ))
2573 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2574 theFacets.insert( make_pair( curHex, curFacet ));
2575 if ( !facetIt2isNew.second )
2578 // remember not-to-split facets in facetsToCheck
2579 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2580 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2582 if ( iF == curFacet && iF == oppFacet )
2584 TVolumeFaceKey facetKey ( vTool, iF );
2585 TElemFacets elemFacet( facetIt2isNew.first, iF );
2586 pair< TFacetMap::iterator, bool > it2isnew =
2587 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2588 if ( !it2isnew.second )
2589 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2591 // pass to a volume adjacent via oppFacet
2592 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2598 // get a new curFacet
2599 vTool.GetFaceNodes( oppFacet, facetNodes );
2600 vTool.Set( curHex );
2601 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2604 } // move in two directions from startHex via facetID
2606 // Find a new startHex by facetsToCheck
2610 TFacetMap::iterator fIt = facetsToCheck.begin();
2611 while ( !startHex && fIt != facetsToCheck.end() )
2613 const TElemFacets& elemFacets = fIt->second;
2614 const SMDS_MeshElement* hex = elemFacets.first->first;
2615 int splitFacet = elemFacets.first->second;
2616 int lateralFacet = elemFacets.second;
2617 facetsToCheck.erase( fIt );
2618 fIt = facetsToCheck.begin();
2621 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2622 curHex->GetGeomType() != SMDSGeom_HEXA )
2624 if ( !allHex && !theHexas.count( curHex ))
2629 // find a facet of startHex to split
2631 set<const SMDS_MeshNode*> lateralNodes;
2632 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2633 vTool.GetFaceNodes( splitFacet, facetNodes );
2634 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2635 vTool.Set( startHex );
2636 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2638 // look for a facet of startHex having common nodes with facetNodes
2639 // but not lateralFacet
2640 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2642 if ( iF == lateralFacet )
2644 int nbCommonNodes = 0;
2645 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2646 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2647 nbCommonNodes += facetNodes.count( nn[ iN ]);
2649 if ( nbCommonNodes >= 2 )
2656 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2658 } // while ( startHex )
2665 //================================================================================
2667 * \brief Selects nodes of several elements according to a given interlace
2668 * \param [in] srcNodes - nodes to select from
2669 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2670 * \param [in] interlace - indices of nodes for all elements
2671 * \param [in] nbElems - nb of elements
2672 * \param [in] nbNodes - nb of nodes in each element
2673 * \param [in] mesh - the mesh
2674 * \param [out] elemQueue - a list to push elements found by the selected nodes
2675 * \param [in] type - type of elements to look for
2677 //================================================================================
2679 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2680 vector< const SMDS_MeshNode* >* tgtNodesVec,
2681 const int* interlace,
2684 SMESHDS_Mesh* mesh = 0,
2685 list< const SMDS_MeshElement* >* elemQueue=0,
2686 SMDSAbs_ElementType type=SMDSAbs_All)
2688 for ( int iE = 0; iE < nbElems; ++iE )
2690 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2691 const int* select = & interlace[iE*nbNodes];
2692 elemNodes.resize( nbNodes );
2693 for ( int iN = 0; iN < nbNodes; ++iN )
2694 elemNodes[iN] = srcNodes[ select[ iN ]];
2696 const SMDS_MeshElement* e;
2698 for ( int iE = 0; iE < nbElems; ++iE )
2699 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2700 elemQueue->push_back( e );
2704 //=======================================================================
2706 * Split bi-quadratic elements into linear ones without creation of additional nodes
2707 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2708 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2709 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2710 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2711 * will be split in order to keep the mesh conformal.
2712 * \param elems - elements to split
2714 //=======================================================================
2716 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2718 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2719 vector<const SMDS_MeshElement* > splitElems;
2720 list< const SMDS_MeshElement* > elemQueue;
2721 list< const SMDS_MeshElement* >::iterator elemIt;
2723 SMESHDS_Mesh * mesh = GetMeshDS();
2724 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2725 int nbElems, nbNodes;
2727 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2728 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2731 elemQueue.push_back( *elemSetIt );
2732 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2734 const SMDS_MeshElement* elem = *elemIt;
2735 switch( elem->GetEntityType() )
2737 case SMDSEntity_TriQuad_Hexa: // HEX27
2739 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2740 nbElems = nbNodes = 8;
2741 elemType = & hexaType;
2743 // get nodes for new elements
2744 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2745 { 1,9,20,8, 17,22,26,21 },
2746 { 2,10,20,9, 18,23,26,22 },
2747 { 3,11,20,10, 19,24,26,23 },
2748 { 16,21,26,24, 4,12,25,15 },
2749 { 17,22,26,21, 5,13,25,12 },
2750 { 18,23,26,22, 6,14,25,13 },
2751 { 19,24,26,23, 7,15,25,14 }};
2752 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2754 // add boundary faces to elemQueue
2755 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2756 { 4,5,6,7, 12,13,14,15, 25 },
2757 { 0,1,5,4, 8,17,12,16, 21 },
2758 { 1,2,6,5, 9,18,13,17, 22 },
2759 { 2,3,7,6, 10,19,14,18, 23 },
2760 { 3,0,4,7, 11,16,15,19, 24 }};
2761 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2763 // add boundary segments to elemQueue
2764 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2765 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2766 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2767 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2770 case SMDSEntity_BiQuad_Triangle: // TRIA7
2772 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2775 elemType = & quadType;
2777 // get nodes for new elements
2778 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2779 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2781 // add boundary segments to elemQueue
2782 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2783 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2786 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2788 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2791 elemType = & quadType;
2793 // get nodes for new elements
2794 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2795 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2797 // add boundary segments to elemQueue
2798 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2799 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2802 case SMDSEntity_Quad_Edge:
2804 if ( elemIt == elemQueue.begin() )
2805 continue; // an elem is in theElems
2806 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2809 elemType = & segType;
2811 // get nodes for new elements
2812 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2813 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2817 } // switch( elem->GetEntityType() )
2819 // Create new elements
2821 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2825 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2826 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2827 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2828 //elemType->SetID( -1 );
2830 for ( int iE = 0; iE < nbElems; ++iE )
2831 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2834 ReplaceElemInGroups( elem, splitElems, mesh );
2837 for ( size_t i = 0; i < splitElems.size(); ++i )
2838 subMesh->AddElement( splitElems[i] );
2843 //=======================================================================
2844 //function : AddToSameGroups
2845 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2846 //=======================================================================
2848 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2849 const SMDS_MeshElement* elemInGroups,
2850 SMESHDS_Mesh * aMesh)
2852 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2853 if (!groups.empty()) {
2854 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2855 for ( ; grIt != groups.end(); grIt++ ) {
2856 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2857 if ( group && group->Contains( elemInGroups ))
2858 group->SMDSGroup().Add( elemToAdd );
2864 //=======================================================================
2865 //function : RemoveElemFromGroups
2866 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2867 //=======================================================================
2868 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2869 SMESHDS_Mesh * aMesh)
2871 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2872 if (!groups.empty())
2874 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2875 for (; GrIt != groups.end(); GrIt++)
2877 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2878 if (!grp || grp->IsEmpty()) continue;
2879 grp->SMDSGroup().Remove(removeelem);
2884 //================================================================================
2886 * \brief Replace elemToRm by elemToAdd in the all groups
2888 //================================================================================
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2891 const SMDS_MeshElement* elemToAdd,
2892 SMESHDS_Mesh * aMesh)
2894 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895 if (!groups.empty()) {
2896 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2897 for ( ; grIt != groups.end(); grIt++ ) {
2898 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2899 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2900 group->SMDSGroup().Add( elemToAdd );
2905 //================================================================================
2907 * \brief Replace elemToRm by elemToAdd in the all groups
2909 //================================================================================
2911 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2912 const vector<const SMDS_MeshElement*>& elemToAdd,
2913 SMESHDS_Mesh * aMesh)
2915 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2916 if (!groups.empty())
2918 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2919 for ( ; grIt != groups.end(); grIt++ ) {
2920 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2921 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2922 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2923 group->SMDSGroup().Add( elemToAdd[ i ] );
2928 //=======================================================================
2929 //function : QuadToTri
2930 //purpose : Cut quadrangles into triangles.
2931 // theCrit is used to select a diagonal to cut
2932 //=======================================================================
2934 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2935 const bool the13Diag)
2937 myLastCreatedElems.Clear();
2938 myLastCreatedNodes.Clear();
2940 SMESHDS_Mesh * aMesh = GetMeshDS();
2942 Handle(Geom_Surface) surface;
2943 SMESH_MesherHelper helper( *GetMesh() );
2945 TIDSortedElemSet::iterator itElem;
2946 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2948 const SMDS_MeshElement* elem = *itElem;
2949 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2952 if ( elem->NbNodes() == 4 ) {
2953 // retrieve element nodes
2954 const SMDS_MeshNode* aNodes [4];
2955 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2957 while ( itN->more() )
2958 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2960 int aShapeId = FindShape( elem );
2961 const SMDS_MeshElement* newElem1 = 0;
2962 const SMDS_MeshElement* newElem2 = 0;
2964 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2965 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2968 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2969 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2971 myLastCreatedElems.Append(newElem1);
2972 myLastCreatedElems.Append(newElem2);
2973 // put a new triangle on the same shape and add to the same groups
2976 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2977 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2979 AddToSameGroups( newElem1, elem, aMesh );
2980 AddToSameGroups( newElem2, elem, aMesh );
2981 aMesh->RemoveElement( elem );
2984 // Quadratic quadrangle
2986 else if ( elem->NbNodes() >= 8 )
2988 // get surface elem is on
2989 int aShapeId = FindShape( elem );
2990 if ( aShapeId != helper.GetSubShapeID() ) {
2994 shape = aMesh->IndexToShape( aShapeId );
2995 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2996 TopoDS_Face face = TopoDS::Face( shape );
2997 surface = BRep_Tool::Surface( face );
2998 if ( !surface.IsNull() )
2999 helper.SetSubShape( shape );
3003 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3004 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3005 for ( int i = 0; itN->more(); ++i )
3006 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3008 const SMDS_MeshNode* centrNode = aNodes[8];
3009 if ( centrNode == 0 )
3011 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3012 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3014 myLastCreatedNodes.Append(centrNode);
3017 // create a new element
3018 const SMDS_MeshElement* newElem1 = 0;
3019 const SMDS_MeshElement* newElem2 = 0;
3021 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3022 aNodes[6], aNodes[7], centrNode );
3023 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3024 centrNode, aNodes[4], aNodes[5] );
3027 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3028 aNodes[7], aNodes[4], centrNode );
3029 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3030 centrNode, aNodes[5], aNodes[6] );
3032 myLastCreatedElems.Append(newElem1);
3033 myLastCreatedElems.Append(newElem2);
3034 // put a new triangle on the same shape and add to the same groups
3037 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3038 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3040 AddToSameGroups( newElem1, elem, aMesh );
3041 AddToSameGroups( newElem2, elem, aMesh );
3042 aMesh->RemoveElement( elem );
3049 //=======================================================================
3050 //function : getAngle
3052 //=======================================================================
3054 double getAngle(const SMDS_MeshElement * tr1,
3055 const SMDS_MeshElement * tr2,
3056 const SMDS_MeshNode * n1,
3057 const SMDS_MeshNode * n2)
3059 double angle = 2. * M_PI; // bad angle
3062 SMESH::Controls::TSequenceOfXYZ P1, P2;
3063 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3064 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3067 if(!tr1->IsQuadratic())
3068 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3070 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3071 if ( N1.SquareMagnitude() <= gp::Resolution() )
3073 if(!tr2->IsQuadratic())
3074 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3076 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3077 if ( N2.SquareMagnitude() <= gp::Resolution() )
3080 // find the first diagonal node n1 in the triangles:
3081 // take in account a diagonal link orientation
3082 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3083 for ( int t = 0; t < 2; t++ ) {
3084 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3085 int i = 0, iDiag = -1;
3086 while ( it->more()) {
3087 const SMDS_MeshElement *n = it->next();
3088 if ( n == n1 || n == n2 ) {
3092 if ( i - iDiag == 1 )
3093 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3102 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3105 angle = N1.Angle( N2 );
3110 // =================================================
3111 // class generating a unique ID for a pair of nodes
3112 // and able to return nodes by that ID
3113 // =================================================
3117 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3118 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3121 long GetLinkID (const SMDS_MeshNode * n1,
3122 const SMDS_MeshNode * n2) const
3124 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3127 bool GetNodes (const long theLinkID,
3128 const SMDS_MeshNode* & theNode1,
3129 const SMDS_MeshNode* & theNode2) const
3131 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3132 if ( !theNode1 ) return false;
3133 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3134 if ( !theNode2 ) return false;
3140 const SMESHDS_Mesh* myMesh;
3145 //=======================================================================
3146 //function : TriToQuad
3147 //purpose : Fuse neighbour triangles into quadrangles.
3148 // theCrit is used to select a neighbour to fuse with.
3149 // theMaxAngle is a max angle between element normals at which
3150 // fusion is still performed.
3151 //=======================================================================
3153 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3154 SMESH::Controls::NumericalFunctorPtr theCrit,
3155 const double theMaxAngle)
3157 myLastCreatedElems.Clear();
3158 myLastCreatedNodes.Clear();
3160 if ( !theCrit.get() )
3163 SMESHDS_Mesh * aMesh = GetMeshDS();
3165 // Prepare data for algo: build
3166 // 1. map of elements with their linkIDs
3167 // 2. map of linkIDs with their elements
3169 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3170 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3171 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3172 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3174 TIDSortedElemSet::iterator itElem;
3175 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3177 const SMDS_MeshElement* elem = *itElem;
3178 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3179 bool IsTria = ( elem->NbCornerNodes()==3 );
3180 if (!IsTria) continue;
3182 // retrieve element nodes
3183 const SMDS_MeshNode* aNodes [4];
3184 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3187 aNodes[ i++ ] = itN->next();
3188 aNodes[ 3 ] = aNodes[ 0 ];
3191 for ( i = 0; i < 3; i++ ) {
3192 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3193 // check if elements sharing a link can be fused
3194 itLE = mapLi_listEl.find( link );
3195 if ( itLE != mapLi_listEl.end() ) {
3196 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3198 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3199 //if ( FindShape( elem ) != FindShape( elem2 ))
3200 // continue; // do not fuse triangles laying on different shapes
3201 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3202 continue; // avoid making badly shaped quads
3203 (*itLE).second.push_back( elem );
3206 mapLi_listEl[ link ].push_back( elem );
3208 mapEl_setLi [ elem ].insert( link );
3211 // Clean the maps from the links shared by a sole element, ie
3212 // links to which only one element is bound in mapLi_listEl
3214 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3215 int nbElems = (*itLE).second.size();
3216 if ( nbElems < 2 ) {
3217 const SMDS_MeshElement* elem = (*itLE).second.front();
3218 SMESH_TLink link = (*itLE).first;
3219 mapEl_setLi[ elem ].erase( link );
3220 if ( mapEl_setLi[ elem ].empty() )
3221 mapEl_setLi.erase( elem );
3225 // Algo: fuse triangles into quadrangles
3227 while ( ! mapEl_setLi.empty() ) {
3228 // Look for the start element:
3229 // the element having the least nb of shared links
3230 const SMDS_MeshElement* startElem = 0;
3232 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3233 int nbLinks = (*itEL).second.size();
3234 if ( nbLinks < minNbLinks ) {
3235 startElem = (*itEL).first;
3236 minNbLinks = nbLinks;
3237 if ( minNbLinks == 1 )
3242 // search elements to fuse starting from startElem or links of elements
3243 // fused earlyer - startLinks
3244 list< SMESH_TLink > startLinks;
3245 while ( startElem || !startLinks.empty() ) {
3246 while ( !startElem && !startLinks.empty() ) {
3247 // Get an element to start, by a link
3248 SMESH_TLink linkId = startLinks.front();
3249 startLinks.pop_front();
3250 itLE = mapLi_listEl.find( linkId );
3251 if ( itLE != mapLi_listEl.end() ) {
3252 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3253 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3254 for ( ; itE != listElem.end() ; itE++ )
3255 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3257 mapLi_listEl.erase( itLE );
3262 // Get candidates to be fused
3263 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3264 const SMESH_TLink *link12 = 0, *link13 = 0;
3266 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3267 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3268 ASSERT( !setLi.empty() );
3269 set< SMESH_TLink >::iterator itLi;
3270 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3272 const SMESH_TLink & link = (*itLi);
3273 itLE = mapLi_listEl.find( link );
3274 if ( itLE == mapLi_listEl.end() )
3277 const SMDS_MeshElement* elem = (*itLE).second.front();
3279 elem = (*itLE).second.back();
3280 mapLi_listEl.erase( itLE );
3281 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3292 // add other links of elem to list of links to re-start from
3293 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3294 set< SMESH_TLink >::iterator it;
3295 for ( it = links.begin(); it != links.end(); it++ ) {
3296 const SMESH_TLink& link2 = (*it);
3297 if ( link2 != link )
3298 startLinks.push_back( link2 );
3302 // Get nodes of possible quadrangles
3303 const SMDS_MeshNode *n12 [4], *n13 [4];
3304 bool Ok12 = false, Ok13 = false;
3305 const SMDS_MeshNode *linkNode1, *linkNode2;
3307 linkNode1 = link12->first;
3308 linkNode2 = link12->second;
3309 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3313 linkNode1 = link13->first;
3314 linkNode2 = link13->second;
3315 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3319 // Choose a pair to fuse
3320 if ( Ok12 && Ok13 ) {
3321 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3322 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3323 double aBadRate12 = getBadRate( &quad12, theCrit );
3324 double aBadRate13 = getBadRate( &quad13, theCrit );
3325 if ( aBadRate13 < aBadRate12 )
3332 // and remove fused elems and remove links from the maps
3333 mapEl_setLi.erase( tr1 );
3336 mapEl_setLi.erase( tr2 );
3337 mapLi_listEl.erase( *link12 );
3338 if ( tr1->NbNodes() == 3 )
3340 const SMDS_MeshElement* newElem = 0;
3341 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3342 myLastCreatedElems.Append(newElem);
3343 AddToSameGroups( newElem, tr1, aMesh );
3344 int aShapeId = tr1->getshapeId();
3346 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3347 aMesh->RemoveElement( tr1 );
3348 aMesh->RemoveElement( tr2 );
3351 vector< const SMDS_MeshNode* > N1;
3352 vector< const SMDS_MeshNode* > N2;
3353 getNodesFromTwoTria(tr1,tr2,N1,N2);
3354 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3355 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3356 // i.e. first nodes from both arrays form a new diagonal
3357 const SMDS_MeshNode* aNodes[8];
3366 const SMDS_MeshElement* newElem = 0;
3367 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3368 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3369 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3371 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3372 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3373 myLastCreatedElems.Append(newElem);
3374 AddToSameGroups( newElem, tr1, aMesh );
3375 int aShapeId = tr1->getshapeId();
3377 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3378 aMesh->RemoveElement( tr1 );
3379 aMesh->RemoveElement( tr2 );
3380 // remove middle node (9)
3381 if ( N1[4]->NbInverseElements() == 0 )
3382 aMesh->RemoveNode( N1[4] );
3383 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3384 aMesh->RemoveNode( N1[6] );
3385 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3386 aMesh->RemoveNode( N2[6] );
3391 mapEl_setLi.erase( tr3 );
3392 mapLi_listEl.erase( *link13 );
3393 if ( tr1->NbNodes() == 3 ) {
3394 const SMDS_MeshElement* newElem = 0;
3395 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3396 myLastCreatedElems.Append(newElem);
3397 AddToSameGroups( newElem, tr1, aMesh );
3398 int aShapeId = tr1->getshapeId();
3400 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3401 aMesh->RemoveElement( tr1 );
3402 aMesh->RemoveElement( tr3 );
3405 vector< const SMDS_MeshNode* > N1;
3406 vector< const SMDS_MeshNode* > N2;
3407 getNodesFromTwoTria(tr1,tr3,N1,N2);
3408 // now we receive following N1 and N2 (using numeration as above image)
3409 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3410 // i.e. first nodes from both arrays form a new diagonal
3411 const SMDS_MeshNode* aNodes[8];
3420 const SMDS_MeshElement* newElem = 0;
3421 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3422 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3423 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3425 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3426 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3427 myLastCreatedElems.Append(newElem);
3428 AddToSameGroups( newElem, tr1, aMesh );
3429 int aShapeId = tr1->getshapeId();
3431 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3432 aMesh->RemoveElement( tr1 );
3433 aMesh->RemoveElement( tr3 );
3434 // remove middle node (9)
3435 if ( N1[4]->NbInverseElements() == 0 )
3436 aMesh->RemoveNode( N1[4] );
3437 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3438 aMesh->RemoveNode( N1[6] );
3439 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3440 aMesh->RemoveNode( N2[6] );
3444 // Next element to fuse: the rejected one
3446 startElem = Ok12 ? tr3 : tr2;
3448 } // if ( startElem )
3449 } // while ( startElem || !startLinks.empty() )
3450 } // while ( ! mapEl_setLi.empty() )
3456 /*#define DUMPSO(txt) \
3457 // cout << txt << endl;
3458 //=============================================================================
3462 //=============================================================================
3463 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3467 int tmp = idNodes[ i1 ];
3468 idNodes[ i1 ] = idNodes[ i2 ];
3469 idNodes[ i2 ] = tmp;
3470 gp_Pnt Ptmp = P[ i1 ];
3473 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3476 //=======================================================================
3477 //function : SortQuadNodes
3478 //purpose : Set 4 nodes of a quadrangle face in a good order.
3479 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3481 //=======================================================================
3483 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3488 for ( i = 0; i < 4; i++ ) {
3489 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3491 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3494 gp_Vec V1(P[0], P[1]);
3495 gp_Vec V2(P[0], P[2]);
3496 gp_Vec V3(P[0], P[3]);
3498 gp_Vec Cross1 = V1 ^ V2;
3499 gp_Vec Cross2 = V2 ^ V3;
3502 if (Cross1.Dot(Cross2) < 0)
3507 if (Cross1.Dot(Cross2) < 0)
3511 swap ( i, i + 1, idNodes, P );
3513 // for ( int ii = 0; ii < 4; ii++ ) {
3514 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3515 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3521 //=======================================================================
3522 //function : SortHexaNodes
3523 //purpose : Set 8 nodes of a hexahedron in a good order.
3524 // Return success status
3525 //=======================================================================
3527 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3532 DUMPSO( "INPUT: ========================================");
3533 for ( i = 0; i < 8; i++ ) {
3534 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3535 if ( !n ) return false;
3536 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3537 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3539 DUMPSO( "========================================");
3542 set<int> faceNodes; // ids of bottom face nodes, to be found
3543 set<int> checkedId1; // ids of tried 2-nd nodes
3544 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3545 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3546 int iMin, iLoop1 = 0;
3548 // Loop to try the 2-nd nodes
3550 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3552 // Find not checked 2-nd node
3553 for ( i = 1; i < 8; i++ )
3554 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3555 int id1 = idNodes[i];
3556 swap ( 1, i, idNodes, P );
3557 checkedId1.insert ( id1 );
3561 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3562 // ie that all but meybe one (id3 which is on the same face) nodes
3563 // lay on the same side from the triangle plane.
3565 bool manyInPlane = false; // more than 4 nodes lay in plane
3567 while ( ++iLoop2 < 6 ) {
3569 // get 1-2-3 plane coeffs
3570 Standard_Real A, B, C, D;
3571 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3572 if ( N.SquareMagnitude() > gp::Resolution() )
3574 gp_Pln pln ( P[0], N );
3575 pln.Coefficients( A, B, C, D );
3577 // find the node (iMin) closest to pln
3578 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3580 for ( i = 3; i < 8; i++ ) {
3581 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3582 if ( fabs( dist[i] ) < minDist ) {
3583 minDist = fabs( dist[i] );
3586 if ( fabs( dist[i] ) <= tol )
3587 idInPln.insert( idNodes[i] );
3590 // there should not be more than 4 nodes in bottom plane
3591 if ( idInPln.size() > 1 )
3593 DUMPSO( "### idInPln.size() = " << idInPln.size());
3594 // idInPlane does not contain the first 3 nodes
3595 if ( manyInPlane || idInPln.size() == 5)
3596 return false; // all nodes in one plane
3599 // set the 1-st node to be not in plane
3600 for ( i = 3; i < 8; i++ ) {
3601 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3602 DUMPSO( "### Reset 0-th node");
3603 swap( 0, i, idNodes, P );
3608 // reset to re-check second nodes
3609 leastDist = DBL_MAX;
3613 break; // from iLoop2;
3616 // check that the other 4 nodes are on the same side
3617 bool sameSide = true;
3618 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3619 for ( i = 3; sameSide && i < 8; i++ ) {
3621 sameSide = ( isNeg == dist[i] <= 0.);
3624 // keep best solution
3625 if ( sameSide && minDist < leastDist ) {
3626 leastDist = minDist;
3628 faceNodes.insert( idNodes[ 1 ] );
3629 faceNodes.insert( idNodes[ 2 ] );
3630 faceNodes.insert( idNodes[ iMin ] );
3631 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3632 << " leastDist = " << leastDist);
3633 if ( leastDist <= DBL_MIN )
3638 // set next 3-d node to check
3639 int iNext = 2 + iLoop2;
3641 DUMPSO( "Try 2-nd");
3642 swap ( 2, iNext, idNodes, P );
3644 } // while ( iLoop2 < 6 )
3647 if ( faceNodes.empty() ) return false;
3649 // Put the faceNodes in proper places
3650 for ( i = 4; i < 8; i++ ) {
3651 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3652 // find a place to put
3654 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3656 DUMPSO( "Set faceNodes");
3657 swap ( iTo, i, idNodes, P );
3662 // Set nodes of the found bottom face in good order
3663 DUMPSO( " Found bottom face: ");
3664 i = SortQuadNodes( theMesh, idNodes );
3666 gp_Pnt Ptmp = P[ i ];
3671 // for ( int ii = 0; ii < 4; ii++ ) {
3672 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3673 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3676 // Gravity center of the top and bottom faces
3677 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3678 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3680 // Get direction from the bottom to the top face
3681 gp_Vec upDir ( aGCb, aGCt );
3682 Standard_Real upDirSize = upDir.Magnitude();
3683 if ( upDirSize <= gp::Resolution() ) return false;
3686 // Assure that the bottom face normal points up
3687 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3688 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3689 if ( Nb.Dot( upDir ) < 0 ) {
3690 DUMPSO( "Reverse bottom face");
3691 swap( 1, 3, idNodes, P );
3694 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3695 Standard_Real minDist = DBL_MAX;
3696 for ( i = 4; i < 8; i++ ) {
3697 // projection of P[i] to the plane defined by P[0] and upDir
3698 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3699 Standard_Real sqDist = P[0].SquareDistance( Pp );
3700 if ( sqDist < minDist ) {
3705 DUMPSO( "Set 4-th");
3706 swap ( 4, iMin, idNodes, P );
3708 // Set nodes of the top face in good order
3709 DUMPSO( "Sort top face");
3710 i = SortQuadNodes( theMesh, &idNodes[4] );
3713 gp_Pnt Ptmp = P[ i ];
3718 // Assure that direction of the top face normal is from the bottom face
3719 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3720 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3721 if ( Nt.Dot( upDir ) < 0 ) {
3722 DUMPSO( "Reverse top face");
3723 swap( 5, 7, idNodes, P );
3726 // DUMPSO( "OUTPUT: ========================================");
3727 // for ( i = 0; i < 8; i++ ) {
3728 // float *p = ugrid->GetPoint(idNodes[i]);
3729 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3735 //================================================================================
3737 * \brief Return nodes linked to the given one
3738 * \param theNode - the node
3739 * \param linkedNodes - the found nodes
3740 * \param type - the type of elements to check
3742 * Medium nodes are ignored
3744 //================================================================================
3746 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3747 TIDSortedElemSet & linkedNodes,
3748 SMDSAbs_ElementType type )
3750 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3751 while ( elemIt->more() )
3753 const SMDS_MeshElement* elem = elemIt->next();
3754 if(elem->GetType() == SMDSAbs_0DElement)
3757 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3758 if ( elem->GetType() == SMDSAbs_Volume )
3760 SMDS_VolumeTool vol( elem );
3761 while ( nodeIt->more() ) {
3762 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3763 if ( theNode != n && vol.IsLinked( theNode, n ))
3764 linkedNodes.insert( n );
3769 for ( int i = 0; nodeIt->more(); ++i ) {
3770 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3771 if ( n == theNode ) {
3772 int iBefore = i - 1;
3774 if ( elem->IsQuadratic() ) {
3775 int nb = elem->NbNodes() / 2;
3776 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3777 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3779 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3780 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3787 //=======================================================================
3788 //function : laplacianSmooth
3789 //purpose : pulls theNode toward the center of surrounding nodes directly
3790 // connected to that node along an element edge
3791 //=======================================================================
3793 void laplacianSmooth(const SMDS_MeshNode* theNode,
3794 const Handle(Geom_Surface)& theSurface,
3795 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3797 // find surrounding nodes
3799 TIDSortedElemSet nodeSet;
3800 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3802 // compute new coodrs
3804 double coord[] = { 0., 0., 0. };
3805 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3806 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3807 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3808 if ( theSurface.IsNull() ) { // smooth in 3D
3809 coord[0] += node->X();
3810 coord[1] += node->Y();
3811 coord[2] += node->Z();
3813 else { // smooth in 2D
3814 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3815 gp_XY* uv = theUVMap[ node ];
3816 coord[0] += uv->X();
3817 coord[1] += uv->Y();
3820 int nbNodes = nodeSet.size();
3823 coord[0] /= nbNodes;
3824 coord[1] /= nbNodes;
3826 if ( !theSurface.IsNull() ) {
3827 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3828 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3829 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3835 coord[2] /= nbNodes;
3839 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3842 //=======================================================================
3843 //function : centroidalSmooth
3844 //purpose : pulls theNode toward the element-area-weighted centroid of the
3845 // surrounding elements
3846 //=======================================================================
3848 void centroidalSmooth(const SMDS_MeshNode* theNode,
3849 const Handle(Geom_Surface)& theSurface,
3850 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3852 gp_XYZ aNewXYZ(0.,0.,0.);
3853 SMESH::Controls::Area anAreaFunc;
3854 double totalArea = 0.;
3859 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3860 while ( elemIt->more() )
3862 const SMDS_MeshElement* elem = elemIt->next();
3865 gp_XYZ elemCenter(0.,0.,0.);
3866 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3867 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3868 int nn = elem->NbNodes();
3869 if(elem->IsQuadratic()) nn = nn/2;
3871 //while ( itN->more() ) {
3873 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3875 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3876 aNodePoints.push_back( aP );
3877 if ( !theSurface.IsNull() ) { // smooth in 2D
3878 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3879 gp_XY* uv = theUVMap[ aNode ];
3880 aP.SetCoord( uv->X(), uv->Y(), 0. );
3884 double elemArea = anAreaFunc.GetValue( aNodePoints );
3885 totalArea += elemArea;
3887 aNewXYZ += elemCenter * elemArea;
3889 aNewXYZ /= totalArea;
3890 if ( !theSurface.IsNull() ) {
3891 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3892 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3897 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3900 //=======================================================================
3901 //function : getClosestUV
3902 //purpose : return UV of closest projection
3903 //=======================================================================
3905 static bool getClosestUV (Extrema_GenExtPS& projector,
3906 const gp_Pnt& point,
3909 projector.Perform( point );
3910 if ( projector.IsDone() ) {
3911 double u, v, minVal = DBL_MAX;
3912 for ( int i = projector.NbExt(); i > 0; i-- )
3913 if ( projector.SquareDistance( i ) < minVal ) {
3914 minVal = projector.SquareDistance( i );
3915 projector.Point( i ).Parameter( u, v );
3917 result.SetCoord( u, v );
3923 //=======================================================================
3925 //purpose : Smooth theElements during theNbIterations or until a worst
3926 // element has aspect ratio <= theTgtAspectRatio.
3927 // Aspect Ratio varies in range [1.0, inf].
3928 // If theElements is empty, the whole mesh is smoothed.
3929 // theFixedNodes contains additionally fixed nodes. Nodes built
3930 // on edges and boundary nodes are always fixed.
3931 //=======================================================================
3933 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3934 set<const SMDS_MeshNode*> & theFixedNodes,
3935 const SmoothMethod theSmoothMethod,
3936 const int theNbIterations,
3937 double theTgtAspectRatio,
3940 myLastCreatedElems.Clear();
3941 myLastCreatedNodes.Clear();
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 SMESHDS_Mesh* aMesh = GetMeshDS();
4481 const int nbNodes = elem->NbNodes();
4482 const int nbCorners = elem->NbCornerNodes();
4483 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4484 polyhedron creation !!! */
4485 // Loop on elem nodes:
4486 // find new nodes and detect same nodes indices
4487 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4488 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4489 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4490 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4492 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4493 vector<int> sames(nbNodes);
4494 vector<bool> isSingleNode(nbNodes);
4496 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4497 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4498 const SMDS_MeshNode* node = nnIt->first;
4499 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4500 if ( listNewNodes.empty() )
4503 itNN [ iNode ] = listNewNodes.begin();
4504 prevNod[ iNode ] = node;
4505 nextNod[ iNode ] = listNewNodes.front();
4507 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4508 corner node of linear */
4509 if ( prevNod[ iNode ] != nextNod [ iNode ])
4510 nbDouble += !isSingleNode[iNode];
4512 if( iNode < nbCorners ) { // check corners only
4513 if ( prevNod[ iNode ] == nextNod [ iNode ])
4514 sames[nbSame++] = iNode;
4516 iNotSameNode = iNode;
4520 if ( nbSame == nbNodes || nbSame > 2) {
4521 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4525 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4527 // fix nodes order to have bottom normal external
4528 if ( baseType == SMDSEntity_Polygon )
4530 std::reverse( itNN.begin(), itNN.end() );
4531 std::reverse( prevNod.begin(), prevNod.end() );
4532 std::reverse( midlNod.begin(), midlNod.end() );
4533 std::reverse( nextNod.begin(), nextNod.end() );
4534 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4538 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4539 SMDS_MeshCell::applyInterlace( ind, itNN );
4540 SMDS_MeshCell::applyInterlace( ind, prevNod );
4541 SMDS_MeshCell::applyInterlace( ind, nextNod );
4542 SMDS_MeshCell::applyInterlace( ind, midlNod );
4543 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4546 sames[nbSame] = iNotSameNode;
4547 for ( int j = 0; j <= nbSame; ++j )
4548 for ( size_t i = 0; i < ind.size(); ++i )
4549 if ( ind[i] == sames[j] )
4554 iNotSameNode = sames[nbSame];
4558 else if ( elem->GetType() == SMDSAbs_Edge )
4560 // orient a new face same as adjacent one
4562 const SMDS_MeshElement* e;
4563 TIDSortedElemSet dummy;
4564 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4565 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4566 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4568 // there is an adjacent face, check order of nodes in it
4569 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4572 std::swap( itNN[0], itNN[1] );
4573 std::swap( prevNod[0], prevNod[1] );
4574 std::swap( nextNod[0], nextNod[1] );
4575 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4577 sames[0] = 1 - sames[0];
4578 iNotSameNode = 1 - iNotSameNode;
4583 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4585 iSameNode = sames[ nbSame-1 ];
4586 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4587 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4588 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4591 if ( baseType == SMDSEntity_Polygon )
4593 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4594 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4596 else if ( baseType == SMDSEntity_Quad_Polygon )
4598 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4599 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4602 // make new elements
4603 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4606 for ( iNode = 0; iNode < nbNodes; iNode++ )
4608 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4609 nextNod[ iNode ] = *itNN[ iNode ]++;
4612 SMDS_MeshElement* aNewElem = 0;
4613 /*if(!elem->IsPoly())*/ {
4614 switch ( baseType ) {
4616 case SMDSEntity_Node: { // sweep NODE
4617 if ( nbSame == 0 ) {
4618 if ( isSingleNode[0] )
4619 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4621 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4627 case SMDSEntity_Edge: { // sweep EDGE
4628 if ( nbDouble == 0 )
4630 if ( nbSame == 0 ) // ---> quadrangle
4631 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4632 nextNod[ 1 ], nextNod[ 0 ] );
4633 else // ---> triangle
4634 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4635 nextNod[ iNotSameNode ] );
4637 else // ---> polygon
4639 vector<const SMDS_MeshNode*> poly_nodes;
4640 poly_nodes.push_back( prevNod[0] );
4641 poly_nodes.push_back( prevNod[1] );
4642 if ( prevNod[1] != nextNod[1] )
4644 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4645 poly_nodes.push_back( nextNod[1] );
4647 if ( prevNod[0] != nextNod[0] )
4649 poly_nodes.push_back( nextNod[0] );
4650 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4652 switch ( poly_nodes.size() ) {
4654 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4657 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4658 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4661 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4666 case SMDSEntity_Triangle: // TRIANGLE --->
4668 if ( nbDouble > 0 ) break;
4669 if ( nbSame == 0 ) // ---> pentahedron
4670 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4671 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4673 else if ( nbSame == 1 ) // ---> pyramid
4674 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4675 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4676 nextNod[ iSameNode ]);
4678 else // 2 same nodes: ---> tetrahedron
4679 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4680 nextNod[ iNotSameNode ]);
4683 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4687 if ( nbDouble+nbSame == 2 )
4689 if(nbSame==0) { // ---> quadratic quadrangle
4690 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4691 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4693 else { //(nbSame==1) // ---> quadratic triangle
4695 return; // medium node on axis
4697 else if(sames[0]==0)
4698 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4699 prevNod[2], midlNod[1], nextNod[2] );
4701 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4702 prevNod[2], nextNod[2], midlNod[0]);
4705 else if ( nbDouble == 3 )
4707 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4708 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4709 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4716 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4717 if ( nbDouble > 0 ) break;
4719 if ( nbSame == 0 ) // ---> hexahedron
4720 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4721 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4723 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4724 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4725 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4726 nextNod[ iSameNode ]);
4727 newElems.push_back( aNewElem );
4728 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4729 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4730 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4732 else if ( nbSame == 2 ) { // ---> pentahedron
4733 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4734 // iBeforeSame is same too
4735 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4736 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4737 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4739 // iAfterSame is same too
4740 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4741 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4742 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4746 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4747 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4748 if ( nbDouble+nbSame != 3 ) break;
4750 // ---> pentahedron with 15 nodes
4751 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4752 nextNod[0], nextNod[1], nextNod[2],
4753 prevNod[3], prevNod[4], prevNod[5],
4754 nextNod[3], nextNod[4], nextNod[5],
4755 midlNod[0], midlNod[1], midlNod[2]);
4757 else if(nbSame==1) {
4758 // ---> 2d order pyramid of 13 nodes
4759 int apex = iSameNode;
4760 int i0 = ( apex + 1 ) % nbCorners;
4761 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4765 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4766 nextNod[i0], nextNod[i1], prevNod[apex],
4767 prevNod[i01], midlNod[i0],
4768 nextNod[i01], midlNod[i1],
4769 prevNod[i1a], prevNod[i0a],
4770 nextNod[i0a], nextNod[i1a]);
4772 else if(nbSame==2) {
4773 // ---> 2d order tetrahedron of 10 nodes
4774 int n1 = iNotSameNode;
4775 int n2 = ( n1 + 1 ) % nbCorners;
4776 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4780 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4781 prevNod[n12], prevNod[n23], prevNod[n31],
4782 midlNod[n1], nextNod[n12], nextNod[n31]);
4786 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4788 if ( nbDouble != 4 ) break;
4789 // ---> hexahedron with 20 nodes
4790 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4791 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4792 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4793 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4794 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4796 else if(nbSame==1) {
4797 // ---> pyramid + pentahedron - can not be created since it is needed
4798 // additional middle node at the center of face
4799 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4802 else if( nbSame == 2 ) {
4803 if ( nbDouble != 2 ) break;
4804 // ---> 2d order Pentahedron with 15 nodes
4806 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4807 // iBeforeSame is same too
4814 // iAfterSame is same too
4824 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4825 prevNod[n4], prevNod[n5], nextNod[n5],
4826 prevNod[n12], midlNod[n2], nextNod[n12],
4827 prevNod[n45], midlNod[n5], nextNod[n45],
4828 prevNod[n14], prevNod[n25], nextNod[n25]);
4832 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4834 if( nbSame == 0 && nbDouble == 9 ) {
4835 // ---> tri-quadratic hexahedron with 27 nodes
4836 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4837 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4838 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4839 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4840 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4841 prevNod[8], // bottom center
4842 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4843 nextNod[8], // top center
4844 midlNod[8]);// elem center
4852 case SMDSEntity_Polygon: { // sweep POLYGON
4854 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4855 // ---> hexagonal prism
4856 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4857 prevNod[3], prevNod[4], prevNod[5],
4858 nextNod[0], nextNod[1], nextNod[2],
4859 nextNod[3], nextNod[4], nextNod[5]);
4863 case SMDSEntity_Ball:
4868 } // switch ( baseType )
4871 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4873 if ( baseType != SMDSEntity_Polygon )
4875 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4876 SMDS_MeshCell::applyInterlace( ind, prevNod );
4877 SMDS_MeshCell::applyInterlace( ind, nextNod );
4878 SMDS_MeshCell::applyInterlace( ind, midlNod );
4879 SMDS_MeshCell::applyInterlace( ind, itNN );
4880 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4881 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4883 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4884 vector<int> quantities (nbNodes + 2);
4885 polyedre_nodes.clear();
4889 for (int inode = 0; inode < nbNodes; inode++)
4890 polyedre_nodes.push_back( prevNod[inode] );
4891 quantities.push_back( nbNodes );
4894 polyedre_nodes.push_back( nextNod[0] );
4895 for (int inode = nbNodes; inode-1; --inode )
4896 polyedre_nodes.push_back( nextNod[inode-1] );
4897 quantities.push_back( nbNodes );
4905 const int iQuad = elem->IsQuadratic();
4906 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4908 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4909 int inextface = (iface+1+iQuad) % nbNodes;
4910 int imid = (iface+1) % nbNodes;
4911 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4912 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4913 polyedre_nodes.push_back( prevNod[iface] ); // 1
4914 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4916 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4917 polyedre_nodes.push_back( nextNod[iface] ); // 2
4919 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4920 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4922 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4923 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4925 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4926 if ( nbFaceNodes > 2 )
4927 quantities.push_back( nbFaceNodes );
4928 else // degenerated face
4929 polyedre_nodes.resize( prevNbNodes );
4931 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4933 } // try to create a polyherdal prism
4936 newElems.push_back( aNewElem );
4937 myLastCreatedElems.Append(aNewElem);
4938 srcElements.Append( elem );
4941 // set new prev nodes
4942 for ( iNode = 0; iNode < nbNodes; iNode++ )
4943 prevNod[ iNode ] = nextNod[ iNode ];
4948 //=======================================================================
4950 * \brief Create 1D and 2D elements around swept elements
4951 * \param mapNewNodes - source nodes and ones generated from them
4952 * \param newElemsMap - source elements and ones generated from them
4953 * \param elemNewNodesMap - nodes generated from each node of each element
4954 * \param elemSet - all swept elements
4955 * \param nbSteps - number of sweeping steps
4956 * \param srcElements - to append elem for each generated element
4958 //=======================================================================
4960 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4961 TTElemOfElemListMap & newElemsMap,
4962 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4963 TIDSortedElemSet& elemSet,
4965 SMESH_SequenceOfElemPtr& srcElements)
4967 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4968 SMESHDS_Mesh* aMesh = GetMeshDS();
4970 // Find nodes belonging to only one initial element - sweep them into edges.
4972 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4973 for ( ; nList != mapNewNodes.end(); nList++ )
4975 const SMDS_MeshNode* node =
4976 static_cast<const SMDS_MeshNode*>( nList->first );
4977 if ( newElemsMap.count( node ))
4978 continue; // node was extruded into edge
4979 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4980 int nbInitElems = 0;
4981 const SMDS_MeshElement* el = 0;
4982 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4983 while ( eIt->more() && nbInitElems < 2 ) {
4984 const SMDS_MeshElement* e = eIt->next();
4985 SMDSAbs_ElementType type = e->GetType();
4986 if ( type == SMDSAbs_Volume ||
4990 if ( type > highType ) {
4997 if ( nbInitElems == 1 ) {
4998 bool NotCreateEdge = el && el->IsMediumNode(node);
4999 if(!NotCreateEdge) {
5000 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5001 list<const SMDS_MeshElement*> newEdges;
5002 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5007 // Make a ceiling for each element ie an equal element of last new nodes.
5008 // Find free links of faces - make edges and sweep them into faces.
5010 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5012 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5013 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5014 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5016 const SMDS_MeshElement* elem = itElem->first;
5017 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5019 if(itElem->second.size()==0) continue;
5021 const bool isQuadratic = elem->IsQuadratic();
5023 if ( elem->GetType() == SMDSAbs_Edge ) {
5024 // create a ceiling edge
5025 if ( !isQuadratic ) {
5026 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5027 vecNewNodes[ 1 ]->second.back())) {
5028 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5029 vecNewNodes[ 1 ]->second.back()));
5030 srcElements.Append( elem );
5034 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5035 vecNewNodes[ 1 ]->second.back(),
5036 vecNewNodes[ 2 ]->second.back())) {
5037 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5038 vecNewNodes[ 1 ]->second.back(),
5039 vecNewNodes[ 2 ]->second.back()));
5040 srcElements.Append( elem );
5044 if ( elem->GetType() != SMDSAbs_Face )
5047 bool hasFreeLinks = false;
5049 TIDSortedElemSet avoidSet;
5050 avoidSet.insert( elem );
5052 set<const SMDS_MeshNode*> aFaceLastNodes;
5053 int iNode, nbNodes = vecNewNodes.size();
5054 if ( !isQuadratic ) {
5055 // loop on the face nodes
5056 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5057 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5058 // look for free links of the face
5059 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5060 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5061 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5062 // check if a link n1-n2 is free
5063 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5064 hasFreeLinks = true;
5065 // make a new edge and a ceiling for a new edge
5066 const SMDS_MeshElement* edge;
5067 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5068 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5069 srcElements.Append( myLastCreatedElems.Last() );
5071 n1 = vecNewNodes[ iNode ]->second.back();
5072 n2 = vecNewNodes[ iNext ]->second.back();
5073 if ( !aMesh->FindEdge( n1, n2 )) {
5074 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5075 srcElements.Append( edge );
5080 else { // elem is quadratic face
5081 int nbn = nbNodes/2;
5082 for ( iNode = 0; iNode < nbn; iNode++ ) {
5083 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5084 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5085 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5086 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5087 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5088 // check if a link is free
5089 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5090 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5091 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5092 hasFreeLinks = true;
5093 // make an edge and a ceiling for a new edge
5095 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5096 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5097 srcElements.Append( elem );
5099 n1 = vecNewNodes[ iNode ]->second.back();
5100 n2 = vecNewNodes[ iNext ]->second.back();
5101 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5102 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5103 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5104 srcElements.Append( elem );
5108 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5109 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5113 // sweep free links into faces
5115 if ( hasFreeLinks ) {
5116 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5117 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5119 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5120 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5121 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5122 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5123 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5125 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5126 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5127 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5129 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5130 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5131 std::advance( v, volNb );
5132 // find indices of free faces of a volume and their source edges
5133 list< int > freeInd;
5134 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5135 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5136 int iF, nbF = vTool.NbFaces();
5137 for ( iF = 0; iF < nbF; iF ++ ) {
5138 if (vTool.IsFreeFace( iF ) &&
5139 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5140 initNodeSet != faceNodeSet) // except an initial face
5142 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5144 if ( faceNodeSet == initNodeSetNoCenter )
5146 freeInd.push_back( iF );
5147 // find source edge of a free face iF
5148 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5149 vector<const SMDS_MeshNode*>::iterator lastCommom;
5150 commonNodes.resize( nbNodes, 0 );
5151 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5152 initNodeSet.begin(), initNodeSet.end(),
5153 commonNodes.begin());
5154 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5155 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5157 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5159 if ( !srcEdges.back() )
5161 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5162 << iF << " of volume #" << vTool.ID() << endl;
5167 if ( freeInd.empty() )
5170 // create wall faces for all steps;
5171 // if such a face has been already created by sweep of edge,
5172 // assure that its orientation is OK
5173 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5175 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5176 vTool.SetExternalNormal();
5177 const int nextShift = vTool.IsForward() ? +1 : -1;
5178 list< int >::iterator ind = freeInd.begin();
5179 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5180 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5182 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5183 int nbn = vTool.NbFaceNodes( *ind );
5184 const SMDS_MeshElement * f = 0;
5185 if ( nbn == 3 ) ///// triangle
5187 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5189 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5191 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5193 nodes[ 1 + nextShift ] };
5195 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5197 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5201 else if ( nbn == 4 ) ///// quadrangle
5203 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5205 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5207 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5208 nodes[ 2 ], nodes[ 2+nextShift ] };
5210 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5212 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5213 newOrder[ 2 ], newOrder[ 3 ]));
5216 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5218 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5220 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5222 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5224 nodes[2 + 2*nextShift],
5225 nodes[3 - 2*nextShift],
5227 nodes[3 + 2*nextShift]};
5229 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5231 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5239 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5241 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5242 nodes[1], nodes[3], nodes[5], nodes[7] );
5244 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5246 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5247 nodes[4 - 2*nextShift],
5249 nodes[4 + 2*nextShift],
5251 nodes[5 - 2*nextShift],
5253 nodes[5 + 2*nextShift] };
5255 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5257 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5258 newOrder[ 2 ], newOrder[ 3 ],
5259 newOrder[ 4 ], newOrder[ 5 ],
5260 newOrder[ 6 ], newOrder[ 7 ]));
5263 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5265 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5266 SMDSAbs_Face, /*noMedium=*/false);
5268 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5270 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5271 nodes[4 - 2*nextShift],
5273 nodes[4 + 2*nextShift],
5275 nodes[5 - 2*nextShift],
5277 nodes[5 + 2*nextShift],
5280 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5282 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5283 newOrder[ 2 ], newOrder[ 3 ],
5284 newOrder[ 4 ], newOrder[ 5 ],
5285 newOrder[ 6 ], newOrder[ 7 ],
5289 else //////// polygon
5291 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5292 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5294 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5296 if ( !vTool.IsForward() )
5297 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5299 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5301 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5305 while ( srcElements.Length() < myLastCreatedElems.Length() )
5306 srcElements.Append( *srcEdge );
5308 } // loop on free faces
5310 // go to the next volume
5312 while ( iVol++ < nbVolumesByStep ) v++;
5315 } // loop on volumes of one step
5316 } // sweep free links into faces
5318 // Make a ceiling face with a normal external to a volume
5320 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5321 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5322 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5324 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5325 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5326 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5330 lastVol.SetExternalNormal();
5331 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5332 const int nbn = lastVol.NbFaceNodes( iF );
5333 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5334 if ( !hasFreeLinks ||
5335 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5337 const vector<int>& interlace =
5338 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5339 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5341 AddElement( nodeVec, anyFace.Init( elem ));
5343 while ( srcElements.Length() < myLastCreatedElems.Length() )
5344 srcElements.Append( elem );
5347 } // loop on swept elements
5350 //=======================================================================
5351 //function : RotationSweep
5353 //=======================================================================
5355 SMESH_MeshEditor::PGroupIDs
5356 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5357 const gp_Ax1& theAxis,
5358 const double theAngle,
5359 const int theNbSteps,
5360 const double theTol,
5361 const bool theMakeGroups,
5362 const bool theMakeWalls)
5364 myLastCreatedElems.Clear();
5365 myLastCreatedNodes.Clear();
5367 // source elements for each generated one
5368 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5371 aTrsf.SetRotation( theAxis, theAngle );
5373 aTrsf2.SetRotation( theAxis, theAngle/2. );
5375 gp_Lin aLine( theAxis );
5376 double aSqTol = theTol * theTol;
5378 SMESHDS_Mesh* aMesh = GetMeshDS();
5380 TNodeOfNodeListMap mapNewNodes;
5381 TElemOfVecOfNnlmiMap mapElemNewNodes;
5382 TTElemOfElemListMap newElemsMap;
5384 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5385 myMesh->NbFaces(ORDER_QUADRATIC) +
5386 myMesh->NbVolumes(ORDER_QUADRATIC) );
5387 // loop on theElemSets
5388 setElemsFirst( theElemSets );
5389 TIDSortedElemSet::iterator itElem;
5390 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5392 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5393 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5394 const SMDS_MeshElement* elem = *itElem;
5395 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5397 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5398 newNodesItVec.reserve( elem->NbNodes() );
5400 // loop on elem nodes
5401 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402 while ( itN->more() )
5404 const SMDS_MeshNode* node = cast2Node( itN->next() );
5406 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5408 aXYZ.Coord( coord[0], coord[1], coord[2] );
5409 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5411 // check if a node has been already sweeped
5412 TNodeOfNodeListMapItr nIt =
5413 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5414 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5415 if ( listNewNodes.empty() )
5417 // check if we are to create medium nodes between corner ones
5418 bool needMediumNodes = false;
5419 if ( isQuadraticMesh )
5421 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5422 while (it->more() && !needMediumNodes )
5424 const SMDS_MeshElement* invElem = it->next();
5425 if ( invElem != elem && !theElems.count( invElem )) continue;
5426 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5427 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5428 needMediumNodes = true;
5433 const SMDS_MeshNode * newNode = node;
5434 for ( int i = 0; i < theNbSteps; i++ ) {
5436 if ( needMediumNodes ) // create a medium node
5438 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5439 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5440 myLastCreatedNodes.Append(newNode);
5441 srcNodes.Append( node );
5442 listNewNodes.push_back( newNode );
5443 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5446 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5448 // create a corner node
5449 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5450 myLastCreatedNodes.Append(newNode);
5451 srcNodes.Append( node );
5452 listNewNodes.push_back( newNode );
5455 listNewNodes.push_back( newNode );
5456 // if ( needMediumNodes )
5457 // listNewNodes.push_back( newNode );
5461 newNodesItVec.push_back( nIt );
5463 // make new elements
5464 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5469 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5471 PGroupIDs newGroupIDs;
5472 if ( theMakeGroups )
5473 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5478 //=======================================================================
5479 //function : ExtrusParam
5480 //purpose : standard construction
5481 //=======================================================================
5483 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5484 const int theNbSteps,
5486 const double theTolerance):
5488 myFlags( theFlags ),
5489 myTolerance( theTolerance ),
5490 myElemsToUse( NULL )
5492 mySteps = new TColStd_HSequenceOfReal;
5493 const double stepSize = theStep.Magnitude();
5494 for (int i=1; i<=theNbSteps; i++ )
5495 mySteps->Append( stepSize );
5497 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5498 ( theTolerance > 0 ))
5500 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5504 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5508 //=======================================================================
5509 //function : ExtrusParam
5510 //purpose : steps are given explicitly
5511 //=======================================================================
5513 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5514 Handle(TColStd_HSequenceOfReal) theSteps,
5516 const double theTolerance):
5518 mySteps( theSteps ),
5519 myFlags( theFlags ),
5520 myTolerance( theTolerance ),
5521 myElemsToUse( NULL )
5523 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5524 ( theTolerance > 0 ))
5526 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5530 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5534 //=======================================================================
5535 //function : ExtrusParam
5536 //purpose : for extrusion by normal
5537 //=======================================================================
5539 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5540 const int theNbSteps,
5544 mySteps( new TColStd_HSequenceOfReal ),
5545 myFlags( theFlags ),
5547 myElemsToUse( NULL )
5549 for (int i = 0; i < theNbSteps; i++ )
5550 mySteps->Append( theStepSize );
5554 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5558 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5562 //=======================================================================
5563 //function : ExtrusParam::SetElementsToUse
5564 //purpose : stores elements to use for extrusion by normal, depending on
5565 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5566 //=======================================================================
5568 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5570 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5573 //=======================================================================
5574 //function : ExtrusParam::beginStepIter
5575 //purpose : prepare iteration on steps
5576 //=======================================================================
5578 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5580 myWithMediumNodes = withMediumNodes;
5584 //=======================================================================
5585 //function : ExtrusParam::moreSteps
5586 //purpose : are there more steps?
5587 //=======================================================================
5589 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5591 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5593 //=======================================================================
5594 //function : ExtrusParam::nextStep
5595 //purpose : returns the next step
5596 //=======================================================================
5598 double SMESH_MeshEditor::ExtrusParam::nextStep()
5601 if ( !myCurSteps.empty() )
5603 res = myCurSteps.back();
5604 myCurSteps.pop_back();
5606 else if ( myNextStep <= mySteps->Length() )
5608 myCurSteps.push_back( mySteps->Value( myNextStep ));
5610 if ( myWithMediumNodes )
5612 myCurSteps.back() /= 2.;
5613 myCurSteps.push_back( myCurSteps.back() );
5620 //=======================================================================
5621 //function : ExtrusParam::makeNodesByDir
5622 //purpose : create nodes for standard extrusion
5623 //=======================================================================
5625 int SMESH_MeshEditor::ExtrusParam::
5626 makeNodesByDir( SMESHDS_Mesh* mesh,
5627 const SMDS_MeshNode* srcNode,
5628 std::list<const SMDS_MeshNode*> & newNodes,
5629 const bool makeMediumNodes)
5631 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5634 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5636 p += myDir.XYZ() * nextStep();
5637 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5638 newNodes.push_back( newNode );
5643 //=======================================================================
5644 //function : ExtrusParam::makeNodesByDirAndSew
5645 //purpose : create nodes for standard extrusion with sewing
5646 //=======================================================================
5648 int SMESH_MeshEditor::ExtrusParam::
5649 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5650 const SMDS_MeshNode* srcNode,
5651 std::list<const SMDS_MeshNode*> & newNodes,
5652 const bool makeMediumNodes)
5654 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5657 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5659 P1 += myDir.XYZ() * nextStep();
5661 // try to search in sequence of existing nodes
5662 // if myNodes.Length()>0 we 'nave to use given sequence
5663 // else - use all nodes of mesh
5664 const SMDS_MeshNode * node = 0;
5665 if ( myNodes.Length() > 0 ) {
5667 for(i=1; i<=myNodes.Length(); i++) {
5668 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5669 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5671 node = myNodes.Value(i);
5677 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5678 while(itn->more()) {
5679 SMESH_TNodeXYZ P2( itn->next() );
5680 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5689 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5691 newNodes.push_back( node );
5698 //=======================================================================
5699 //function : ExtrusParam::makeNodesByNormal2D
5700 //purpose : create nodes for extrusion using normals of faces
5701 //=======================================================================
5703 int SMESH_MeshEditor::ExtrusParam::
5704 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5705 const SMDS_MeshNode* srcNode,
5706 std::list<const SMDS_MeshNode*> & newNodes,
5707 const bool makeMediumNodes)
5709 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5711 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5713 // get normals to faces sharing srcNode
5714 vector< gp_XYZ > norms, baryCenters;
5715 gp_XYZ norm, avgNorm( 0,0,0 );
5716 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5717 while ( faceIt->more() )
5719 const SMDS_MeshElement* face = faceIt->next();
5720 if ( myElemsToUse && !myElemsToUse->count( face ))
5722 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5724 norms.push_back( norm );
5726 if ( !alongAvgNorm )
5730 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5731 bc += SMESH_TNodeXYZ( nIt->next() );
5732 baryCenters.push_back( bc / nbN );
5737 if ( norms.empty() ) return 0;
5739 double normSize = avgNorm.Modulus();
5740 if ( normSize < std::numeric_limits<double>::min() )
5743 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5746 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5749 avgNorm /= normSize;
5752 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5755 double stepSize = nextStep();
5757 if ( norms.size() > 1 )
5759 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5761 // translate plane of a face
5762 baryCenters[ iF ] += norms[ iF ] * stepSize;
5764 // find point of intersection of the face plane located at baryCenters[ iF ]
5765 // and avgNorm located at pNew
5766 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5767 double dot = ( norms[ iF ] * avgNorm );
5768 if ( dot < std::numeric_limits<double>::min() )
5769 dot = stepSize * 1e-3;
5770 double step = -( norms[ iF ] * pNew + d ) / dot;
5771 pNew += step * avgNorm;
5776 pNew += stepSize * avgNorm;
5780 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5781 newNodes.push_back( newNode );
5786 //=======================================================================
5787 //function : ExtrusParam::makeNodesByNormal1D
5788 //purpose : create nodes for extrusion using normals of edges
5789 //=======================================================================
5791 int SMESH_MeshEditor::ExtrusParam::
5792 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5793 const SMDS_MeshNode* srcNode,
5794 std::list<const SMDS_MeshNode*> & newNodes,
5795 const bool makeMediumNodes)
5797 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5801 //=======================================================================
5802 //function : ExtrusionSweep
5804 //=======================================================================
5806 SMESH_MeshEditor::PGroupIDs
5807 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5808 const gp_Vec& theStep,
5809 const int theNbSteps,
5810 TTElemOfElemListMap& newElemsMap,
5812 const double theTolerance)
5814 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5815 return ExtrusionSweep( theElems, aParams, newElemsMap );
5819 //=======================================================================
5820 //function : ExtrusionSweep
5822 //=======================================================================
5824 SMESH_MeshEditor::PGroupIDs
5825 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5826 ExtrusParam& theParams,
5827 TTElemOfElemListMap& newElemsMap)
5829 myLastCreatedElems.Clear();
5830 myLastCreatedNodes.Clear();
5832 // source elements for each generated one
5833 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5835 //SMESHDS_Mesh* aMesh = GetMeshDS();
5837 setElemsFirst( theElemSets );
5838 const int nbSteps = theParams.NbSteps();
5839 theParams.SetElementsToUse( theElemSets[0] );
5841 TNodeOfNodeListMap mapNewNodes;
5842 //TNodeOfNodeVecMap mapNewNodes;
5843 TElemOfVecOfNnlmiMap mapElemNewNodes;
5844 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5846 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5847 myMesh->NbFaces(ORDER_QUADRATIC) +
5848 myMesh->NbVolumes(ORDER_QUADRATIC) );
5850 TIDSortedElemSet::iterator itElem;
5851 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5853 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5854 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5856 // check element type
5857 const SMDS_MeshElement* elem = *itElem;
5858 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5861 const size_t nbNodes = elem->NbNodes();
5862 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5863 newNodesItVec.reserve( nbNodes );
5865 // loop on elem nodes
5866 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5867 while ( itN->more() )
5869 // check if a node has been already sweeped
5870 const SMDS_MeshNode* node = cast2Node( itN->next() );
5871 TNodeOfNodeListMap::iterator nIt =
5872 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5873 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5874 if ( listNewNodes.empty() )
5878 // check if we are to create medium nodes between corner ones
5879 bool needMediumNodes = false;
5880 if ( isQuadraticMesh )
5882 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5883 while (it->more() && !needMediumNodes )
5885 const SMDS_MeshElement* invElem = it->next();
5886 if ( invElem != elem && !theElems.count( invElem )) continue;
5887 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5888 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5889 needMediumNodes = true;
5892 // create nodes for all steps
5893 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5895 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5896 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5898 myLastCreatedNodes.Append( *newNodesIt );
5899 srcNodes.Append( node );
5904 break; // newNodesItVec will be shorter than nbNodes
5907 newNodesItVec.push_back( nIt );
5909 // make new elements
5910 if ( newNodesItVec.size() == nbNodes )
5911 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5915 if ( theParams.ToMakeBoundary() ) {
5916 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5918 PGroupIDs newGroupIDs;
5919 if ( theParams.ToMakeGroups() )
5920 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5925 //=======================================================================
5926 //function : ExtrusionAlongTrack
5928 //=======================================================================
5929 SMESH_MeshEditor::Extrusion_Error
5930 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5931 SMESH_subMesh* theTrack,
5932 const SMDS_MeshNode* theN1,
5933 const bool theHasAngles,
5934 list<double>& theAngles,
5935 const bool theLinearVariation,
5936 const bool theHasRefPoint,
5937 const gp_Pnt& theRefPoint,
5938 const bool theMakeGroups)
5940 myLastCreatedElems.Clear();
5941 myLastCreatedNodes.Clear();
5944 std::list<double> aPrms;
5945 TIDSortedElemSet::iterator itElem;
5948 TopoDS_Edge aTrackEdge;
5949 TopoDS_Vertex aV1, aV2;
5951 SMDS_ElemIteratorPtr aItE;
5952 SMDS_NodeIteratorPtr aItN;
5953 SMDSAbs_ElementType aTypeE;
5955 TNodeOfNodeListMap mapNewNodes;
5958 aNbE = theElements[0].size() + theElements[1].size();
5961 return EXTR_NO_ELEMENTS;
5963 // 1.1 Track Pattern
5966 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5968 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5969 theHasAngles, theAngles, theLinearVariation,
5970 theHasRefPoint, theRefPoint, theMakeGroups );
5972 aItE = pSubMeshDS->GetElements();
5973 while ( aItE->more() ) {
5974 const SMDS_MeshElement* pE = aItE->next();
5975 aTypeE = pE->GetType();
5976 // Pattern must contain links only
5977 if ( aTypeE != SMDSAbs_Edge )
5978 return EXTR_PATH_NOT_EDGE;
5981 list<SMESH_MeshEditor_PathPoint> fullList;
5983 const TopoDS_Shape& aS = theTrack->GetSubShape();
5984 // Sub-shape for the Pattern must be an Edge or Wire
5985 if( aS.ShapeType() == TopAbs_EDGE ) {
5986 aTrackEdge = TopoDS::Edge( aS );
5987 // the Edge must not be degenerated
5988 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5989 return EXTR_BAD_PATH_SHAPE;
5990 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5991 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5992 const SMDS_MeshNode* aN1 = aItN->next();
5993 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5994 const SMDS_MeshNode* aN2 = aItN->next();
5995 // starting node must be aN1 or aN2
5996 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5997 return EXTR_BAD_STARTING_NODE;
5998 aItN = pSubMeshDS->GetNodes();
5999 while ( aItN->more() ) {
6000 const SMDS_MeshNode* pNode = aItN->next();
6001 const SMDS_EdgePosition* pEPos =
6002 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6003 double aT = pEPos->GetUParameter();
6004 aPrms.push_back( aT );
6006 //Extrusion_Error err =
6007 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6008 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6009 list< SMESH_subMesh* > LSM;
6010 TopTools_SequenceOfShape Edges;
6011 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6012 while(itSM->more()) {
6013 SMESH_subMesh* SM = itSM->next();
6015 const TopoDS_Shape& aS = SM->GetSubShape();
6018 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6019 int startNid = theN1->GetID();
6020 TColStd_MapOfInteger UsedNums;
6022 int NbEdges = Edges.Length();
6024 for(; i<=NbEdges; i++) {
6026 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6027 for(; itLSM!=LSM.end(); itLSM++) {
6029 if(UsedNums.Contains(k)) continue;
6030 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6031 SMESH_subMesh* locTrack = *itLSM;
6032 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6033 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6034 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6035 const SMDS_MeshNode* aN1 = aItN->next();
6036 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6037 const SMDS_MeshNode* aN2 = aItN->next();
6038 // starting node must be aN1 or aN2
6039 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6040 // 2. Collect parameters on the track edge
6042 aItN = locMeshDS->GetNodes();
6043 while ( aItN->more() ) {
6044 const SMDS_MeshNode* pNode = aItN->next();
6045 const SMDS_EdgePosition* pEPos =
6046 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6047 double aT = pEPos->GetUParameter();
6048 aPrms.push_back( aT );
6050 list<SMESH_MeshEditor_PathPoint> LPP;
6051 //Extrusion_Error err =
6052 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6053 LLPPs.push_back(LPP);
6055 // update startN for search following egde
6056 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6057 else startNid = aN1->GetID();
6061 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6062 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6063 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6064 for(; itPP!=firstList.end(); itPP++) {
6065 fullList.push_back( *itPP );
6067 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6068 fullList.pop_back();
6070 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6071 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6072 itPP = currList.begin();
6073 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6074 gp_Dir D1 = PP1.Tangent();
6075 gp_Dir D2 = PP2.Tangent();
6076 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6077 (D1.Z()+D2.Z())/2 ) );
6078 PP1.SetTangent(Dnew);
6079 fullList.push_back(PP1);
6081 for(; itPP!=firstList.end(); itPP++) {
6082 fullList.push_back( *itPP );
6084 PP1 = fullList.back();
6085 fullList.pop_back();
6087 // if wire not closed
6088 fullList.push_back(PP1);
6092 return EXTR_BAD_PATH_SHAPE;
6095 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6096 theHasRefPoint, theRefPoint, theMakeGroups);
6100 //=======================================================================
6101 //function : ExtrusionAlongTrack
6103 //=======================================================================
6104 SMESH_MeshEditor::Extrusion_Error
6105 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6106 SMESH_Mesh* theTrack,
6107 const SMDS_MeshNode* theN1,
6108 const bool theHasAngles,
6109 list<double>& theAngles,
6110 const bool theLinearVariation,
6111 const bool theHasRefPoint,
6112 const gp_Pnt& theRefPoint,
6113 const bool theMakeGroups)
6115 myLastCreatedElems.Clear();
6116 myLastCreatedNodes.Clear();
6119 std::list<double> aPrms;
6120 TIDSortedElemSet::iterator itElem;
6123 TopoDS_Edge aTrackEdge;
6124 TopoDS_Vertex aV1, aV2;
6126 SMDS_ElemIteratorPtr aItE;
6127 SMDS_NodeIteratorPtr aItN;
6128 SMDSAbs_ElementType aTypeE;
6130 TNodeOfNodeListMap mapNewNodes;
6133 aNbE = theElements[0].size() + theElements[1].size();
6136 return EXTR_NO_ELEMENTS;
6138 // 1.1 Track Pattern
6141 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6143 aItE = pMeshDS->elementsIterator();
6144 while ( aItE->more() ) {
6145 const SMDS_MeshElement* pE = aItE->next();
6146 aTypeE = pE->GetType();
6147 // Pattern must contain links only
6148 if ( aTypeE != SMDSAbs_Edge )
6149 return EXTR_PATH_NOT_EDGE;
6152 list<SMESH_MeshEditor_PathPoint> fullList;
6154 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6156 if ( !theTrack->HasShapeToMesh() ) {
6157 //Mesh without shape
6158 const SMDS_MeshNode* currentNode = NULL;
6159 const SMDS_MeshNode* prevNode = theN1;
6160 std::vector<const SMDS_MeshNode*> aNodesList;
6161 aNodesList.push_back(theN1);
6162 int nbEdges = 0, conn=0;
6163 const SMDS_MeshElement* prevElem = NULL;
6164 const SMDS_MeshElement* currentElem = NULL;
6165 int totalNbEdges = theTrack->NbEdges();
6166 SMDS_ElemIteratorPtr nIt;
6169 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6170 return EXTR_BAD_STARTING_NODE;
6173 conn = nbEdgeConnectivity(theN1);
6175 return EXTR_PATH_NOT_EDGE;
6177 aItE = theN1->GetInverseElementIterator();
6178 prevElem = aItE->next();
6179 currentElem = prevElem;
6181 if(totalNbEdges == 1 ) {
6182 nIt = currentElem->nodesIterator();
6183 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6184 if(currentNode == prevNode)
6185 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6186 aNodesList.push_back(currentNode);
6188 nIt = currentElem->nodesIterator();
6189 while( nIt->more() ) {
6190 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6191 if(currentNode == prevNode)
6192 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6193 aNodesList.push_back(currentNode);
6195 //case of the closed mesh
6196 if(currentNode == theN1) {
6201 conn = nbEdgeConnectivity(currentNode);
6203 return EXTR_PATH_NOT_EDGE;
6204 }else if( conn == 1 && nbEdges > 0 ) {
6209 prevNode = currentNode;
6210 aItE = currentNode->GetInverseElementIterator();
6211 currentElem = aItE->next();
6212 if( currentElem == prevElem)
6213 currentElem = aItE->next();
6214 nIt = currentElem->nodesIterator();
6215 prevElem = currentElem;
6221 if(nbEdges != totalNbEdges)
6222 return EXTR_PATH_NOT_EDGE;
6224 TopTools_SequenceOfShape Edges;
6225 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6226 int startNid = theN1->GetID();
6227 for ( size_t i = 1; i < aNodesList.size(); i++ )
6229 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6230 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6231 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6232 list<SMESH_MeshEditor_PathPoint> LPP;
6234 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6235 LLPPs.push_back(LPP);
6236 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6237 else startNid = aNodesList[i-1]->GetID();
6240 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6241 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6242 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6243 for(; itPP!=firstList.end(); itPP++) {
6244 fullList.push_back( *itPP );
6247 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6248 SMESH_MeshEditor_PathPoint PP2;
6249 fullList.pop_back();
6251 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6252 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6253 itPP = currList.begin();
6254 PP2 = currList.front();
6255 gp_Dir D1 = PP1.Tangent();
6256 gp_Dir D2 = PP2.Tangent();
6257 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6258 PP1.SetTangent(Dnew);
6259 fullList.push_back(PP1);
6261 for(; itPP!=currList.end(); itPP++) {
6262 fullList.push_back( *itPP );
6264 PP1 = fullList.back();
6265 fullList.pop_back();
6267 fullList.push_back(PP1);
6269 } // Sub-shape for the Pattern must be an Edge or Wire
6270 else if ( aS.ShapeType() == TopAbs_EDGE )
6272 aTrackEdge = TopoDS::Edge( aS );
6273 // the Edge must not be degenerated
6274 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6275 return EXTR_BAD_PATH_SHAPE;
6276 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6277 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6278 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6279 // starting node must be aN1 or aN2
6280 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6281 return EXTR_BAD_STARTING_NODE;
6282 aItN = pMeshDS->nodesIterator();
6283 while ( aItN->more() ) {
6284 const SMDS_MeshNode* pNode = aItN->next();
6285 if( pNode==aN1 || pNode==aN2 ) continue;
6286 const SMDS_EdgePosition* pEPos =
6287 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6288 double aT = pEPos->GetUParameter();
6289 aPrms.push_back( aT );
6291 //Extrusion_Error err =
6292 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6294 else if( aS.ShapeType() == TopAbs_WIRE ) {
6295 list< SMESH_subMesh* > LSM;
6296 TopTools_SequenceOfShape Edges;
6297 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6298 for(; eExp.More(); eExp.Next()) {
6299 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6300 if( SMESH_Algo::isDegenerated(E) ) continue;
6301 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6307 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6308 TopoDS_Vertex aVprev;
6309 TColStd_MapOfInteger UsedNums;
6310 int NbEdges = Edges.Length();
6312 for(; i<=NbEdges; i++) {
6314 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6315 for(; itLSM!=LSM.end(); itLSM++) {
6317 if(UsedNums.Contains(k)) continue;
6318 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6319 SMESH_subMesh* locTrack = *itLSM;
6320 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6321 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6322 bool aN1isOK = false, aN2isOK = false;
6323 if ( aVprev.IsNull() ) {
6324 // if previous vertex is not yet defined, it means that we in the beginning of wire
6325 // and we have to find initial vertex corresponding to starting node theN1
6326 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6327 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6328 // starting node must be aN1 or aN2
6329 aN1isOK = ( aN1 && aN1 == theN1 );
6330 aN2isOK = ( aN2 && aN2 == theN1 );
6333 // we have specified ending vertex of the previous edge on the previous iteration
6334 // and we have just to check that it corresponds to any vertex in current segment
6335 aN1isOK = aVprev.IsSame( aV1 );
6336 aN2isOK = aVprev.IsSame( aV2 );
6338 if ( !aN1isOK && !aN2isOK ) continue;
6339 // 2. Collect parameters on the track edge
6341 aItN = locMeshDS->GetNodes();
6342 while ( aItN->more() ) {
6343 const SMDS_MeshNode* pNode = aItN->next();
6344 const SMDS_EdgePosition* pEPos =
6345 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6346 double aT = pEPos->GetUParameter();
6347 aPrms.push_back( aT );
6349 list<SMESH_MeshEditor_PathPoint> LPP;
6350 //Extrusion_Error err =
6351 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6352 LLPPs.push_back(LPP);
6354 // update startN for search following egde
6355 if ( aN1isOK ) aVprev = aV2;
6360 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6361 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6362 fullList.splice( fullList.end(), firstList );
6364 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6365 fullList.pop_back();
6367 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6368 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6369 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6370 gp_Dir D1 = PP1.Tangent();
6371 gp_Dir D2 = PP2.Tangent();
6372 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6373 PP1.SetTangent(Dnew);
6374 fullList.push_back(PP1);
6375 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6376 PP1 = fullList.back();
6377 fullList.pop_back();
6379 // if wire not closed
6380 fullList.push_back(PP1);
6384 return EXTR_BAD_PATH_SHAPE;
6387 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6388 theHasRefPoint, theRefPoint, theMakeGroups);
6392 //=======================================================================
6393 //function : MakeEdgePathPoints
6394 //purpose : auxilary for ExtrusionAlongTrack
6395 //=======================================================================
6396 SMESH_MeshEditor::Extrusion_Error
6397 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6398 const TopoDS_Edge& aTrackEdge,
6400 list<SMESH_MeshEditor_PathPoint>& LPP)
6402 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6404 aTolVec2=aTolVec*aTolVec;
6406 TopoDS_Vertex aV1, aV2;
6407 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6408 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6409 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6410 // 2. Collect parameters on the track edge
6411 aPrms.push_front( aT1 );
6412 aPrms.push_back( aT2 );
6415 if( FirstIsStart ) {
6426 SMESH_MeshEditor_PathPoint aPP;
6427 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6428 std::list<double>::iterator aItD = aPrms.begin();
6429 for(; aItD != aPrms.end(); ++aItD) {
6433 aC3D->D1( aT, aP3D, aVec );
6434 aL2 = aVec.SquareMagnitude();
6435 if ( aL2 < aTolVec2 )
6436 return EXTR_CANT_GET_TANGENT;
6437 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6439 aPP.SetTangent( aTgt );
6440 aPP.SetParameter( aT );
6447 //=======================================================================
6448 //function : MakeExtrElements
6449 //purpose : auxilary for ExtrusionAlongTrack
6450 //=======================================================================
6451 SMESH_MeshEditor::Extrusion_Error
6452 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6453 list<SMESH_MeshEditor_PathPoint>& fullList,
6454 const bool theHasAngles,
6455 list<double>& theAngles,
6456 const bool theLinearVariation,
6457 const bool theHasRefPoint,
6458 const gp_Pnt& theRefPoint,
6459 const bool theMakeGroups)
6461 const int aNbTP = fullList.size();
6464 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6465 LinearAngleVariation(aNbTP-1, theAngles);
6467 // fill vector of path points with angles
6468 vector<SMESH_MeshEditor_PathPoint> aPPs;
6469 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6470 list<double>::iterator itAngles = theAngles.begin();
6471 aPPs.push_back( *itPP++ );
6472 for( ; itPP != fullList.end(); itPP++) {
6473 aPPs.push_back( *itPP );
6474 if ( theHasAngles && itAngles != theAngles.end() )
6475 aPPs.back().SetAngle( *itAngles++ );
6478 TNodeOfNodeListMap mapNewNodes;
6479 TElemOfVecOfNnlmiMap mapElemNewNodes;
6480 TTElemOfElemListMap newElemsMap;
6481 TIDSortedElemSet::iterator itElem;
6482 // source elements for each generated one
6483 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6485 // 3. Center of rotation aV0
6486 gp_Pnt aV0 = theRefPoint;
6487 if ( !theHasRefPoint )
6489 gp_XYZ aGC( 0.,0.,0. );
6490 TIDSortedElemSet newNodes;
6492 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6494 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6495 itElem = theElements.begin();
6496 for ( ; itElem != theElements.end(); itElem++ )
6498 const SMDS_MeshElement* elem = *itElem;
6499 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6500 while ( itN->more() ) {
6501 const SMDS_MeshElement* node = itN->next();
6502 if ( newNodes.insert( node ).second )
6503 aGC += SMESH_TNodeXYZ( node );
6507 aGC /= newNodes.size();
6509 } // if (!theHasRefPoint) {
6511 // 4. Processing the elements
6512 SMESHDS_Mesh* aMesh = GetMeshDS();
6513 list<const SMDS_MeshNode*> emptyList;
6515 setElemsFirst( theElemSets );
6516 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6518 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6519 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6521 const SMDS_MeshElement* elem = *itElem;
6523 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6524 newNodesItVec.reserve( elem->NbNodes() );
6526 // loop on elem nodes
6528 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6529 while ( itN->more() )
6532 // check if a node has been already processed
6533 const SMDS_MeshNode* node = cast2Node( itN->next() );
6534 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6535 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6536 if ( listNewNodes.empty() )
6539 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6540 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6541 gp_Ax1 anAx1, anAxT1T0;
6542 gp_Dir aDT1x, aDT0x, aDT1T0;
6547 aPN0 = SMESH_TNodeXYZ( node );
6549 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6551 aDT0x= aPP0.Tangent();
6553 for ( int j = 1; j < aNbTP; ++j ) {
6554 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6556 aDT1x = aPP1.Tangent();
6557 aAngle1x = aPP1.Angle();
6559 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6561 gp_Vec aV01x( aP0x, aP1x );
6562 aTrsf.SetTranslation( aV01x );
6565 aV1x = aV0x.Transformed( aTrsf );
6566 aPN1 = aPN0.Transformed( aTrsf );
6568 // rotation 1 [ T1,T0 ]
6569 aAngleT1T0=-aDT1x.Angle( aDT0x );
6570 if (fabs(aAngleT1T0) > aTolAng)
6573 anAxT1T0.SetLocation( aV1x );
6574 anAxT1T0.SetDirection( aDT1T0 );
6575 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6577 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6581 if ( theHasAngles ) {
6582 anAx1.SetLocation( aV1x );
6583 anAx1.SetDirection( aDT1x );
6584 aTrsfRot.SetRotation( anAx1, aAngle1x );
6586 aPN1 = aPN1.Transformed( aTrsfRot );
6590 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6592 // create additional node
6593 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6594 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6595 myLastCreatedNodes.Append(newNode);
6596 srcNodes.Append( node );
6597 listNewNodes.push_back( newNode );
6599 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6600 myLastCreatedNodes.Append(newNode);
6601 srcNodes.Append( node );
6602 listNewNodes.push_back( newNode );
6610 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6612 // if current elem is quadratic and current node is not medium
6613 // we have to check - may be it is needed to insert additional nodes
6614 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6615 if ((int) listNewNodes.size() == aNbTP-1 )
6617 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6618 gp_XYZ P(node->X(), node->Y(), node->Z());
6619 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6621 for(i=0; i<aNbTP-1; i++) {
6622 const SMDS_MeshNode* N = *it;
6623 double x = ( N->X() + P.X() )/2.;
6624 double y = ( N->Y() + P.Y() )/2.;
6625 double z = ( N->Z() + P.Z() )/2.;
6626 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6627 srcNodes.Append( node );
6628 myLastCreatedNodes.Append(newN);
6631 P = gp_XYZ(N->X(),N->Y(),N->Z());
6633 listNewNodes.clear();
6634 for(i=0; i<2*(aNbTP-1); i++) {
6635 listNewNodes.push_back(aNodes[i]);
6640 newNodesItVec.push_back( nIt );
6643 // make new elements
6644 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6648 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6650 if ( theMakeGroups )
6651 generateGroups( srcNodes, srcElems, "extruded");
6657 //=======================================================================
6658 //function : LinearAngleVariation
6659 //purpose : auxilary for ExtrusionAlongTrack
6660 //=======================================================================
6661 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6662 list<double>& Angles)
6664 int nbAngles = Angles.size();
6665 if( nbSteps > nbAngles ) {
6666 vector<double> theAngles(nbAngles);
6667 list<double>::iterator it = Angles.begin();
6669 for(; it!=Angles.end(); it++) {
6671 theAngles[i] = (*it);
6674 double rAn2St = double( nbAngles ) / double( nbSteps );
6675 double angPrev = 0, angle;
6676 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6677 double angCur = rAn2St * ( iSt+1 );
6678 double angCurFloor = floor( angCur );
6679 double angPrevFloor = floor( angPrev );
6680 if ( angPrevFloor == angCurFloor )
6681 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6683 int iP = int( angPrevFloor );
6684 double angPrevCeil = ceil(angPrev);
6685 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6687 int iC = int( angCurFloor );
6688 if ( iC < nbAngles )
6689 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6691 iP = int( angPrevCeil );
6693 angle += theAngles[ iC ];
6695 res.push_back(angle);
6700 for(; it!=res.end(); it++)
6701 Angles.push_back( *it );
6706 //================================================================================
6708 * \brief Move or copy theElements applying theTrsf to their nodes
6709 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6710 * \param theTrsf - transformation to apply
6711 * \param theCopy - if true, create translated copies of theElems
6712 * \param theMakeGroups - if true and theCopy, create translated groups
6713 * \param theTargetMesh - mesh to copy translated elements into
6714 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6716 //================================================================================
6718 SMESH_MeshEditor::PGroupIDs
6719 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6720 const gp_Trsf& theTrsf,
6722 const bool theMakeGroups,
6723 SMESH_Mesh* theTargetMesh)
6725 myLastCreatedElems.Clear();
6726 myLastCreatedNodes.Clear();
6728 bool needReverse = false;
6729 string groupPostfix;
6730 switch ( theTrsf.Form() ) {
6733 groupPostfix = "mirrored";
6736 groupPostfix = "mirrored";
6740 groupPostfix = "mirrored";
6743 groupPostfix = "rotated";
6745 case gp_Translation:
6746 groupPostfix = "translated";
6749 groupPostfix = "scaled";
6751 case gp_CompoundTrsf: // different scale by axis
6752 groupPostfix = "scaled";
6755 needReverse = false;
6756 groupPostfix = "transformed";
6759 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6760 SMESHDS_Mesh* aMesh = GetMeshDS();
6762 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6763 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6764 SMESH_MeshEditor::ElemFeatures elemType;
6766 // map old node to new one
6767 TNodeNodeMap nodeMap;
6769 // elements sharing moved nodes; those of them which have all
6770 // nodes mirrored but are not in theElems are to be reversed
6771 TIDSortedElemSet inverseElemSet;
6773 // source elements for each generated one
6774 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6776 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6777 TIDSortedElemSet orphanNode;
6779 if ( theElems.empty() ) // transform the whole mesh
6782 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6783 while ( eIt->more() ) theElems.insert( eIt->next() );
6785 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6786 while ( nIt->more() )
6788 const SMDS_MeshNode* node = nIt->next();
6789 if ( node->NbInverseElements() == 0)
6790 orphanNode.insert( node );
6794 // loop on elements to transform nodes : first orphan nodes then elems
6795 TIDSortedElemSet::iterator itElem;
6796 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6797 for (int i=0; i<2; i++)
6798 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6800 const SMDS_MeshElement* elem = *itElem;
6804 // loop on elem nodes
6806 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6807 while ( itN->more() )
6809 const SMDS_MeshNode* node = cast2Node( itN->next() );
6810 // check if a node has been already transformed
6811 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6812 nodeMap.insert( make_pair ( node, node ));
6813 if ( !n2n_isnew.second )
6816 node->GetXYZ( coord );
6817 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6818 if ( theTargetMesh ) {
6819 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6820 n2n_isnew.first->second = newNode;
6821 myLastCreatedNodes.Append(newNode);
6822 srcNodes.Append( node );
6824 else if ( theCopy ) {
6825 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6826 n2n_isnew.first->second = newNode;
6827 myLastCreatedNodes.Append(newNode);
6828 srcNodes.Append( node );
6831 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6832 // node position on shape becomes invalid
6833 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6834 ( SMDS_SpacePosition::originSpacePosition() );
6837 // keep inverse elements
6838 if ( !theCopy && !theTargetMesh && needReverse ) {
6839 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6840 while ( invElemIt->more() ) {
6841 const SMDS_MeshElement* iel = invElemIt->next();
6842 inverseElemSet.insert( iel );
6846 } // loop on elems in { &orphanNode, &theElems };
6848 // either create new elements or reverse mirrored ones
6849 if ( !theCopy && !needReverse && !theTargetMesh )
6852 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6854 // Replicate or reverse elements
6856 std::vector<int> iForw;
6857 vector<const SMDS_MeshNode*> nodes;
6858 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6860 const SMDS_MeshElement* elem = *itElem;
6861 if ( !elem ) continue;
6863 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6864 size_t nbNodes = elem->NbNodes();
6865 if ( geomType == SMDSGeom_NONE ) continue; // node
6867 nodes.resize( nbNodes );
6869 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6871 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6875 bool allTransformed = true;
6876 int nbFaces = aPolyedre->NbFaces();
6877 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6879 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6880 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6882 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6883 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6884 if ( nodeMapIt == nodeMap.end() )
6885 allTransformed = false; // not all nodes transformed
6887 nodes.push_back((*nodeMapIt).second);
6889 if ( needReverse && allTransformed )
6890 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6892 if ( !allTransformed )
6893 continue; // not all nodes transformed
6895 else // ----------------------- the rest element types
6897 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6898 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6899 const vector<int>& i = needReverse ? iRev : iForw;
6901 // find transformed nodes
6903 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6904 while ( itN->more() ) {
6905 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6906 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6907 if ( nodeMapIt == nodeMap.end() )
6908 break; // not all nodes transformed
6909 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6911 if ( iNode != nbNodes )
6912 continue; // not all nodes transformed
6916 // copy in this or a new mesh
6917 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6918 srcElems.Append( elem );
6921 // reverse element as it was reversed by transformation
6923 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6926 } // loop on elements
6928 if ( editor && editor != this )
6929 myLastCreatedElems = editor->myLastCreatedElems;
6931 PGroupIDs newGroupIDs;
6933 if ( ( theMakeGroups && theCopy ) ||
6934 ( theMakeGroups && theTargetMesh ) )
6935 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6940 //=======================================================================
6942 * \brief Create groups of elements made during transformation
6943 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6944 * \param elemGens - elements making corresponding myLastCreatedElems
6945 * \param postfix - to append to names of new groups
6946 * \param targetMesh - mesh to create groups in
6947 * \param topPresent - is there "top" elements that are created by sweeping
6949 //=======================================================================
6951 SMESH_MeshEditor::PGroupIDs
6952 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6953 const SMESH_SequenceOfElemPtr& elemGens,
6954 const std::string& postfix,
6955 SMESH_Mesh* targetMesh,
6956 const bool topPresent)
6958 PGroupIDs newGroupIDs( new list<int> );
6959 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6961 // Sort existing groups by types and collect their names
6963 // containers to store an old group and generated new ones;
6964 // 1st new group is for result elems of different type than a source one;
6965 // 2nd new group is for same type result elems ("top" group at extrusion)
6967 using boost::make_tuple;
6968 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6969 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6970 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6972 set< string > groupNames;
6974 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6975 if ( !groupIt->more() ) return newGroupIDs;
6977 int newGroupID = mesh->GetGroupIds().back()+1;
6978 while ( groupIt->more() )
6980 SMESH_Group * group = groupIt->next();
6981 if ( !group ) continue;
6982 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6983 if ( !groupDS || groupDS->IsEmpty() ) continue;
6984 groupNames.insert ( group->GetName() );
6985 groupDS->SetStoreName( group->GetName() );
6986 const SMDSAbs_ElementType type = groupDS->GetType();
6987 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6988 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6989 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6990 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6993 // Loop on nodes and elements to add them in new groups
6995 vector< const SMDS_MeshElement* > resultElems;
6996 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6998 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6999 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7000 if ( gens.Length() != elems.Length() )
7001 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7003 // loop on created elements
7004 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7006 const SMDS_MeshElement* sourceElem = gens( iElem );
7007 if ( !sourceElem ) {
7008 MESSAGE("generateGroups(): NULL source element");
7011 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7012 if ( groupsOldNew.empty() ) { // no groups of this type at all
7013 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7014 ++iElem; // skip all elements made by sourceElem
7017 // collect all elements made by the iElem-th sourceElem
7018 resultElems.clear();
7019 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7020 if ( resElem != sourceElem )
7021 resultElems.push_back( resElem );
7022 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7023 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7024 if ( resElem != sourceElem )
7025 resultElems.push_back( resElem );
7027 const SMDS_MeshElement* topElem = 0;
7028 if ( isNodes ) // there must be a top element
7030 topElem = resultElems.back();
7031 resultElems.pop_back();
7035 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7036 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7037 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7039 topElem = *resElemIt;
7040 *resElemIt = 0; // erase *resElemIt
7044 // add resultElems to groups originted from ones the sourceElem belongs to
7045 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7046 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7048 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7049 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7051 // fill in a new group
7052 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7053 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7054 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7056 newGroup.Add( *resElemIt );
7058 // fill a "top" group
7061 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7062 newTopGroup.Add( topElem );
7066 } // loop on created elements
7067 }// loop on nodes and elements
7069 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7071 list<int> topGrouIds;
7072 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7074 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7075 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7076 orderedOldNewGroups[i]->get<2>() };
7077 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7079 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7080 if ( newGroupDS->IsEmpty() )
7082 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7087 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7090 const bool isTop = ( topPresent &&
7091 newGroupDS->GetType() == oldGroupDS->GetType() &&
7094 string name = oldGroupDS->GetStoreName();
7095 { // remove trailing whitespaces (issue 22599)
7096 size_t size = name.size();
7097 while ( size > 1 && isspace( name[ size-1 ]))
7099 if ( size != name.size() )
7101 name.resize( size );
7102 oldGroupDS->SetStoreName( name.c_str() );
7105 if ( !targetMesh ) {
7106 string suffix = ( isTop ? "top": postfix.c_str() );
7110 while ( !groupNames.insert( name ).second ) // name exists
7111 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7116 newGroupDS->SetStoreName( name.c_str() );
7118 // make a SMESH_Groups
7119 mesh->AddGroup( newGroupDS );
7121 topGrouIds.push_back( newGroupDS->GetID() );
7123 newGroupIDs->push_back( newGroupDS->GetID() );
7127 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7132 //================================================================================
7134 * * \brief Return list of group of nodes close to each other within theTolerance
7135 * * Search among theNodes or in the whole mesh if theNodes is empty using
7136 * * an Octree algorithm
7137 * \param [in,out] theNodes - the nodes to treat
7138 * \param [in] theTolerance - the tolerance
7139 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7140 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7141 * corner and medium nodes in separate groups
7143 //================================================================================
7145 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7146 const double theTolerance,
7147 TListOfListOfNodes & theGroupsOfNodes,
7148 bool theSeparateCornersAndMedium)
7150 myLastCreatedElems.Clear();
7151 myLastCreatedNodes.Clear();
7153 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7154 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7155 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7156 theSeparateCornersAndMedium = false;
7158 TIDSortedNodeSet& corners = theNodes;
7159 TIDSortedNodeSet medium;
7161 if ( theNodes.empty() ) // get all nodes in the mesh
7163 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7164 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7165 if ( theSeparateCornersAndMedium )
7166 while ( nIt->more() )
7168 const SMDS_MeshNode* n = nIt->next();
7169 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7170 nodeSet->insert( nodeSet->end(), n );
7173 while ( nIt->more() )
7174 theNodes.insert( theNodes.end(),nIt->next() );
7176 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7178 TIDSortedNodeSet::iterator nIt = corners.begin();
7179 while ( nIt != corners.end() )
7180 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7182 medium.insert( medium.end(), *nIt );
7183 corners.erase( nIt++ );
7191 if ( !corners.empty() )
7192 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7193 if ( !medium.empty() )
7194 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7197 //=======================================================================
7198 //function : SimplifyFace
7199 //purpose : split a chain of nodes into several closed chains
7200 //=======================================================================
7202 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7203 vector<const SMDS_MeshNode *>& poly_nodes,
7204 vector<int>& quantities) const
7206 int nbNodes = faceNodes.size();
7207 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7211 size_t prevNbQuant = quantities.size();
7213 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7214 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7215 map< const SMDS_MeshNode*, int >::iterator nInd;
7217 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7218 simpleNodes.push_back( faceNodes[0] );
7219 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7221 if ( faceNodes[ iCur ] != simpleNodes.back() )
7223 int index = simpleNodes.size();
7224 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7225 int prevIndex = nInd->second;
7226 if ( prevIndex < index )
7229 int loopLen = index - prevIndex;
7232 // store the sub-loop
7233 quantities.push_back( loopLen );
7234 for ( int i = prevIndex; i < index; i++ )
7235 poly_nodes.push_back( simpleNodes[ i ]);
7237 simpleNodes.resize( prevIndex+1 );
7241 simpleNodes.push_back( faceNodes[ iCur ]);
7246 if ( simpleNodes.size() > 2 )
7248 quantities.push_back( simpleNodes.size() );
7249 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7252 return quantities.size() - prevNbQuant;
7255 //=======================================================================
7256 //function : MergeNodes
7257 //purpose : In each group, the cdr of nodes are substituted by the first one
7259 //=======================================================================
7261 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7263 myLastCreatedElems.Clear();
7264 myLastCreatedNodes.Clear();
7266 SMESHDS_Mesh* aMesh = GetMeshDS();
7268 TNodeNodeMap nodeNodeMap; // node to replace - new node
7269 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7270 list< int > rmElemIds, rmNodeIds;
7272 // Fill nodeNodeMap and elems
7274 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7275 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7277 list<const SMDS_MeshNode*>& nodes = *grIt;
7278 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7279 const SMDS_MeshNode* nToKeep = *nIt;
7280 for ( ++nIt; nIt != nodes.end(); nIt++ )
7282 const SMDS_MeshNode* nToRemove = *nIt;
7283 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7284 if ( nToRemove != nToKeep )
7286 rmNodeIds.push_back( nToRemove->GetID() );
7287 AddToSameGroups( nToKeep, nToRemove, aMesh );
7288 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7289 // after MergeNodes() w/o creating node in place of merged ones.
7290 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7291 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7292 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7293 sm->SetIsAlwaysComputed( true );
7295 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7296 while ( invElemIt->more() ) {
7297 const SMDS_MeshElement* elem = invElemIt->next();
7302 // Change element nodes or remove an element
7304 set<const SMDS_MeshNode*> nodeSet;
7305 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7307 ElemFeatures elemType;
7309 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7310 for ( ; eIt != elems.end(); eIt++ )
7312 const SMDS_MeshElement* elem = *eIt;
7313 const int nbNodes = elem->NbNodes();
7314 const int aShapeId = FindShape( elem );
7315 SMDSAbs_EntityType entity = elem->GetEntityType();
7318 curNodes.resize( nbNodes );
7319 uniqueNodes.resize( nbNodes );
7320 iRepl.resize( nbNodes );
7321 int iUnique = 0, iCur = 0, nbRepl = 0;
7323 // get new seq of nodes
7324 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7325 while ( itN->more() )
7327 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7329 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7330 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7332 { ////////// BUG 0020185: begin
7333 bool stopRecur = false;
7334 set<const SMDS_MeshNode*> nodesRecur;
7335 nodesRecur.insert(n);
7336 while (!stopRecur) {
7337 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7338 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7339 n = (*nnIt_i).second;
7340 if (!nodesRecur.insert(n).second) {
7341 // error: recursive dependency
7348 } ////////// BUG 0020185: end
7350 curNodes[ iCur ] = n;
7351 bool isUnique = nodeSet.insert( n ).second;
7353 uniqueNodes[ iUnique++ ] = n;
7355 iRepl[ nbRepl++ ] = iCur;
7359 // Analyse element topology after replacement
7362 int nbUniqueNodes = nodeSet.size();
7363 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7365 if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7367 if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7369 elemType.Init( elem );
7370 const bool isQuad = elemType.myIsQuad;
7372 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7373 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7375 // a polygon can divide into several elements
7376 vector<const SMDS_MeshNode *> polygons_nodes;
7377 vector<int> quantities;
7378 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7381 vector<const SMDS_MeshNode *> face_nodes;
7383 for (int iface = 0; iface < nbNew; iface++)
7385 int nbNewNodes = quantities[iface];
7386 face_nodes.assign( polygons_nodes.begin() + inode,
7387 polygons_nodes.begin() + inode + nbNewNodes );
7388 inode += nbNewNodes;
7389 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7391 bool isValid = ( nbNewNodes % 2 == 0 );
7392 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7393 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7394 elemType.SetQuad( isValid );
7395 if ( isValid ) // put medium nodes after corners
7396 SMDS_MeshCell::applyInterlaceRev
7397 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7398 nbNewNodes ), face_nodes );
7400 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7402 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7404 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7407 rmElemIds.push_back(elem->GetID());
7411 else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7413 if ( nbUniqueNodes < 4 ) {
7414 rmElemIds.push_back(elem->GetID());
7417 // each face has to be analyzed in order to check volume validity
7418 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7421 int nbFaces = aPolyedre->NbFaces();
7423 vector<const SMDS_MeshNode *> poly_nodes;
7424 vector<int> quantities;
7425 vector<const SMDS_MeshNode *> faceNodes;
7427 for (int iface = 1; iface <= nbFaces; iface++)
7429 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7430 faceNodes.resize( nbFaceNodes );
7431 for (int inode = 1; inode <= nbFaceNodes; inode++)
7433 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7434 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7435 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7436 faceNode = (*nnIt).second;
7437 faceNodes[inode - 1] = faceNode;
7439 SimplifyFace(faceNodes, poly_nodes, quantities);
7442 if ( quantities.size() > 3 ) {
7443 // TODO: remove coincident faces
7446 if ( quantities.size() > 3 )
7448 const SMDS_MeshElement* newElem =
7449 aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7450 myLastCreatedElems.Append( newElem );
7451 if ( aShapeId && newElem )
7452 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7453 rmElemIds.push_back( elem->GetID() );
7457 rmElemIds.push_back( elem->GetID() );
7468 // TODO not all the possible cases are solved. Find something more generic?
7470 case SMDSEntity_Edge: //////// EDGE
7471 case SMDSEntity_Triangle: //// TRIANGLE
7472 case SMDSEntity_Quad_Triangle:
7473 case SMDSEntity_Tetra:
7474 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7479 case SMDSEntity_Quad_Edge:
7481 isOk = false; // to linear EDGE ???????
7484 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7486 if ( nbUniqueNodes < 3 )
7488 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7489 isOk = false; // opposite nodes stick
7492 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7501 if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7502 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7503 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7504 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7505 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7511 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7520 if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7521 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7522 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7523 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7524 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7530 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7533 if ( nbUniqueNodes == 4 ) {
7534 // ---------------------------------> tetrahedron
7535 if ( curNodes[3] == curNodes[4] &&
7536 curNodes[3] == curNodes[5] ) {
7540 else if ( curNodes[0] == curNodes[1] &&
7541 curNodes[0] == curNodes[2] ) {
7542 // bottom nodes stick: set a top before
7543 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7544 uniqueNodes[ 0 ] = curNodes [ 5 ];
7545 uniqueNodes[ 1 ] = curNodes [ 4 ];
7546 uniqueNodes[ 2 ] = curNodes [ 3 ];
7549 else if (( curNodes[0] == curNodes[3] ) +
7550 ( curNodes[1] == curNodes[4] ) +
7551 ( curNodes[2] == curNodes[5] ) == 2 ) {
7552 // a lateral face turns into a line
7556 else if ( nbUniqueNodes == 5 ) {
7557 // PENTAHEDRON --------------------> pyramid
7558 if ( curNodes[0] == curNodes[3] )
7560 uniqueNodes[ 0 ] = curNodes[ 1 ];
7561 uniqueNodes[ 1 ] = curNodes[ 4 ];
7562 uniqueNodes[ 2 ] = curNodes[ 5 ];
7563 uniqueNodes[ 3 ] = curNodes[ 2 ];
7564 uniqueNodes[ 4 ] = curNodes[ 0 ];
7567 if ( curNodes[1] == curNodes[4] )
7569 uniqueNodes[ 0 ] = curNodes[ 0 ];
7570 uniqueNodes[ 1 ] = curNodes[ 2 ];
7571 uniqueNodes[ 2 ] = curNodes[ 5 ];
7572 uniqueNodes[ 3 ] = curNodes[ 3 ];
7573 uniqueNodes[ 4 ] = curNodes[ 1 ];
7576 if ( curNodes[2] == curNodes[5] )
7578 uniqueNodes[ 0 ] = curNodes[ 0 ];
7579 uniqueNodes[ 1 ] = curNodes[ 3 ];
7580 uniqueNodes[ 2 ] = curNodes[ 4 ];
7581 uniqueNodes[ 3 ] = curNodes[ 1 ];
7582 uniqueNodes[ 4 ] = curNodes[ 2 ];
7588 case SMDSEntity_Hexa:
7590 //////////////////////////////////// HEXAHEDRON
7592 SMDS_VolumeTool hexa (elem);
7593 hexa.SetExternalNormal();
7594 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7595 //////////////////////// HEX ---> tetrahedron
7596 for ( int iFace = 0; iFace < 6; iFace++ ) {
7597 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7598 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7599 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7600 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7601 // one face turns into a point ...
7602 int pickInd = ind[ 0 ];
7603 int iOppFace = hexa.GetOppFaceIndex( iFace );
7604 ind = hexa.GetFaceNodesIndices( iOppFace );
7606 uniqueNodes.clear();
7607 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7608 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7611 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7613 if ( nbStick == 1 ) {
7614 // ... and the opposite one - into a triangle.
7616 uniqueNodes.push_back( curNodes[ pickInd ]);
7623 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7624 //////////////////////// HEX ---> prism
7625 int nbTria = 0, iTria[3];
7626 const int *ind; // indices of face nodes
7627 // look for triangular faces
7628 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7629 ind = hexa.GetFaceNodesIndices( iFace );
7630 TIDSortedNodeSet faceNodes;
7631 for ( iCur = 0; iCur < 4; iCur++ )
7632 faceNodes.insert( curNodes[ind[iCur]] );
7633 if ( faceNodes.size() == 3 )
7634 iTria[ nbTria++ ] = iFace;
7636 // check if triangles are opposite
7637 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7639 // set nodes of the bottom triangle
7640 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7642 for ( iCur = 0; iCur < 4; iCur++ )
7643 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7644 indB.push_back( ind[iCur] );
7645 if ( !hexa.IsForward() )
7646 std::swap( indB[0], indB[2] );
7647 for ( iCur = 0; iCur < 3; iCur++ )
7648 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7649 // set nodes of the top triangle
7650 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7651 for ( iCur = 0; iCur < 3; ++iCur )
7652 for ( int j = 0; j < 4; ++j )
7653 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7655 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7662 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7663 //////////////////// HEXAHEDRON ---> pyramid
7664 for ( int iFace = 0; iFace < 6; iFace++ ) {
7665 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7666 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7667 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7668 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7669 // one face turns into a point ...
7670 int iOppFace = hexa.GetOppFaceIndex( iFace );
7671 ind = hexa.GetFaceNodesIndices( iOppFace );
7672 uniqueNodes.clear();
7673 for ( iCur = 0; iCur < 4; iCur++ ) {
7674 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7677 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7679 if ( uniqueNodes.size() == 4 ) {
7680 // ... and the opposite one is a quadrangle
7682 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7683 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7691 if ( !isOk && nbUniqueNodes > 4 ) {
7692 ////////////////// HEXAHEDRON ---> polyhedron
7693 hexa.SetExternalNormal();
7694 vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7695 vector<int> quantities; quantities.reserve( 6 );
7696 for ( int iFace = 0; iFace < 6; iFace++ )
7698 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7699 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7700 curNodes[ind[1]] == curNodes[ind[3]] )
7703 break; // opposite nodes stick
7706 for ( iCur = 0; iCur < 4; iCur++ )
7708 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7709 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7711 if ( nodeSet.size() < 3 )
7712 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7714 quantities.push_back( nodeSet.size() );
7716 if ( quantities.size() >= 4 )
7718 const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7719 myLastCreatedElems.Append( newElem );
7720 if ( aShapeId && newElem )
7721 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7722 rmElemIds.push_back( elem->GetID() );
7726 } // case HEXAHEDRON
7730 } // switch ( nbNodes )
7732 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7734 if ( isOk ) // a non-poly elem remains valid after sticking nodes
7736 if ( nbNodes != nbUniqueNodes ||
7737 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7739 elemType.Init( elem ).SetID( elem->GetID() );
7741 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7742 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7744 uniqueNodes.resize(nbUniqueNodes);
7745 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7746 if ( sm && newElem )
7747 sm->AddElement( newElem );
7748 if ( elem != newElem )
7749 ReplaceElemInGroups( elem, newElem, aMesh );
7753 // Remove invalid regular element or invalid polygon
7754 rmElemIds.push_back( elem->GetID() );
7757 } // loop on elements
7759 // Remove bad elements, then equal nodes (order important)
7761 Remove( rmElemIds, false );
7762 Remove( rmNodeIds, true );
7768 // ========================================================
7769 // class : SortableElement
7770 // purpose : allow sorting elements basing on their nodes
7771 // ========================================================
7772 class SortableElement : public set <const SMDS_MeshElement*>
7776 SortableElement( const SMDS_MeshElement* theElem )
7779 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7780 while ( nodeIt->more() )
7781 this->insert( nodeIt->next() );
7784 const SMDS_MeshElement* Get() const
7788 mutable const SMDS_MeshElement* myElem;
7791 //=======================================================================
7792 //function : FindEqualElements
7793 //purpose : Return list of group of elements built on the same nodes.
7794 // Search among theElements or in the whole mesh if theElements is empty
7795 //=======================================================================
7797 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7798 TListOfListOfElementsID & theGroupsOfElementsID)
7800 myLastCreatedElems.Clear();
7801 myLastCreatedNodes.Clear();
7803 typedef map< SortableElement, int > TMapOfNodeSet;
7804 typedef list<int> TGroupOfElems;
7806 if ( theElements.empty() )
7807 { // get all elements in the mesh
7808 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7809 while ( eIt->more() )
7810 theElements.insert( theElements.end(), eIt->next() );
7813 vector< TGroupOfElems > arrayOfGroups;
7814 TGroupOfElems groupOfElems;
7815 TMapOfNodeSet mapOfNodeSet;
7817 TIDSortedElemSet::iterator elemIt = theElements.begin();
7818 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7820 const SMDS_MeshElement* curElem = *elemIt;
7821 SortableElement SE(curElem);
7823 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7824 if ( !pp.second ) { // one more coincident elem
7825 TMapOfNodeSet::iterator& itSE = pp.first;
7826 int ind = (*itSE).second;
7827 arrayOfGroups[ind].push_back( curElem->GetID() );
7830 arrayOfGroups.push_back( groupOfElems );
7831 arrayOfGroups.back().push_back( curElem->GetID() );
7836 groupOfElems.clear();
7837 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7838 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7840 if ( groupIt->size() > 1 ) {
7841 //groupOfElems.sort(); -- theElements is sorted already
7842 theGroupsOfElementsID.push_back( groupOfElems );
7843 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7848 //=======================================================================
7849 //function : MergeElements
7850 //purpose : In each given group, substitute all elements by the first one.
7851 //=======================================================================
7853 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7855 myLastCreatedElems.Clear();
7856 myLastCreatedNodes.Clear();
7858 typedef list<int> TListOfIDs;
7859 TListOfIDs rmElemIds; // IDs of elems to remove
7861 SMESHDS_Mesh* aMesh = GetMeshDS();
7863 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7864 while ( groupsIt != theGroupsOfElementsID.end() ) {
7865 TListOfIDs& aGroupOfElemID = *groupsIt;
7866 aGroupOfElemID.sort();
7867 int elemIDToKeep = aGroupOfElemID.front();
7868 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7869 aGroupOfElemID.pop_front();
7870 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7871 while ( idIt != aGroupOfElemID.end() ) {
7872 int elemIDToRemove = *idIt;
7873 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7874 // add the kept element in groups of removed one (PAL15188)
7875 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7876 rmElemIds.push_back( elemIDToRemove );
7882 Remove( rmElemIds, false );
7885 //=======================================================================
7886 //function : MergeEqualElements
7887 //purpose : Remove all but one of elements built on the same nodes.
7888 //=======================================================================
7890 void SMESH_MeshEditor::MergeEqualElements()
7892 TIDSortedElemSet aMeshElements; /* empty input ==
7893 to merge equal elements in the whole mesh */
7894 TListOfListOfElementsID aGroupsOfElementsID;
7895 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7896 MergeElements(aGroupsOfElementsID);
7899 //=======================================================================
7900 //function : findAdjacentFace
7902 //=======================================================================
7904 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7905 const SMDS_MeshNode* n2,
7906 const SMDS_MeshElement* elem)
7908 TIDSortedElemSet elemSet, avoidSet;
7910 avoidSet.insert ( elem );
7911 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7914 //=======================================================================
7915 //function : findSegment
7916 //purpose : Return a mesh segment by two nodes one of which can be medium
7917 //=======================================================================
7919 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7920 const SMDS_MeshNode* n2)
7922 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7923 while ( it->more() )
7925 const SMDS_MeshElement* seg = it->next();
7926 if ( seg->GetNodeIndex( n2 ) >= 0 )
7932 //=======================================================================
7933 //function : FindFreeBorder
7935 //=======================================================================
7937 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7939 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7940 const SMDS_MeshNode* theSecondNode,
7941 const SMDS_MeshNode* theLastNode,
7942 list< const SMDS_MeshNode* > & theNodes,
7943 list< const SMDS_MeshElement* >& theFaces)
7945 if ( !theFirstNode || !theSecondNode )
7947 // find border face between theFirstNode and theSecondNode
7948 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7952 theFaces.push_back( curElem );
7953 theNodes.push_back( theFirstNode );
7954 theNodes.push_back( theSecondNode );
7956 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7957 TIDSortedElemSet foundElems;
7958 bool needTheLast = ( theLastNode != 0 );
7960 while ( nStart != theLastNode ) {
7961 if ( nStart == theFirstNode )
7962 return !needTheLast;
7964 // find all free border faces sharing form nStart
7966 list< const SMDS_MeshElement* > curElemList;
7967 list< const SMDS_MeshNode* > nStartList;
7968 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7969 while ( invElemIt->more() ) {
7970 const SMDS_MeshElement* e = invElemIt->next();
7971 if ( e == curElem || foundElems.insert( e ).second ) {
7973 int iNode = 0, nbNodes = e->NbNodes();
7974 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7976 if ( e->IsQuadratic() ) {
7977 const SMDS_VtkFace* F =
7978 dynamic_cast<const SMDS_VtkFace*>(e);
7979 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7980 // use special nodes iterator
7981 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7982 while( anIter->more() ) {
7983 nodes[ iNode++ ] = cast2Node(anIter->next());
7987 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7988 while ( nIt->more() )
7989 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7991 nodes[ iNode ] = nodes[ 0 ];
7993 for ( iNode = 0; iNode < nbNodes; iNode++ )
7994 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7995 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7996 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7998 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7999 curElemList.push_back( e );
8003 // analyse the found
8005 int nbNewBorders = curElemList.size();
8006 if ( nbNewBorders == 0 ) {
8007 // no free border furthermore
8008 return !needTheLast;
8010 else if ( nbNewBorders == 1 ) {
8011 // one more element found
8013 nStart = nStartList.front();
8014 curElem = curElemList.front();
8015 theFaces.push_back( curElem );
8016 theNodes.push_back( nStart );
8019 // several continuations found
8020 list< const SMDS_MeshElement* >::iterator curElemIt;
8021 list< const SMDS_MeshNode* >::iterator nStartIt;
8022 // check if one of them reached the last node
8023 if ( needTheLast ) {
8024 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8025 curElemIt!= curElemList.end();
8026 curElemIt++, nStartIt++ )
8027 if ( *nStartIt == theLastNode ) {
8028 theFaces.push_back( *curElemIt );
8029 theNodes.push_back( *nStartIt );
8033 // find the best free border by the continuations
8034 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8035 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8036 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8037 curElemIt!= curElemList.end();
8038 curElemIt++, nStartIt++ )
8040 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8041 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8042 // find one more free border
8043 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8047 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8048 // choice: clear a worse one
8049 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8050 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8051 contNodes[ iWorse ].clear();
8052 contFaces[ iWorse ].clear();
8055 if ( contNodes[0].empty() && contNodes[1].empty() )
8058 // append the best free border
8059 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8060 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8061 theNodes.pop_back(); // remove nIgnore
8062 theNodes.pop_back(); // remove nStart
8063 theFaces.pop_back(); // remove curElem
8064 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8065 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8066 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8067 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8070 } // several continuations found
8071 } // while ( nStart != theLastNode )
8076 //=======================================================================
8077 //function : CheckFreeBorderNodes
8078 //purpose : Return true if the tree nodes are on a free border
8079 //=======================================================================
8081 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8082 const SMDS_MeshNode* theNode2,
8083 const SMDS_MeshNode* theNode3)
8085 list< const SMDS_MeshNode* > nodes;
8086 list< const SMDS_MeshElement* > faces;
8087 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8090 //=======================================================================
8091 //function : SewFreeBorder
8093 //warning : for border-to-side sewing theSideSecondNode is considered as
8094 // the last side node and theSideThirdNode is not used
8095 //=======================================================================
8097 SMESH_MeshEditor::Sew_Error
8098 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8099 const SMDS_MeshNode* theBordSecondNode,
8100 const SMDS_MeshNode* theBordLastNode,
8101 const SMDS_MeshNode* theSideFirstNode,
8102 const SMDS_MeshNode* theSideSecondNode,
8103 const SMDS_MeshNode* theSideThirdNode,
8104 const bool theSideIsFreeBorder,
8105 const bool toCreatePolygons,
8106 const bool toCreatePolyedrs)
8108 myLastCreatedElems.Clear();
8109 myLastCreatedNodes.Clear();
8111 Sew_Error aResult = SEW_OK;
8113 // ====================================
8114 // find side nodes and elements
8115 // ====================================
8117 list< const SMDS_MeshNode* > nSide[ 2 ];
8118 list< const SMDS_MeshElement* > eSide[ 2 ];
8119 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8120 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8124 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8125 nSide[0], eSide[0])) {
8126 MESSAGE(" Free Border 1 not found " );
8127 aResult = SEW_BORDER1_NOT_FOUND;
8129 if (theSideIsFreeBorder) {
8132 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8133 nSide[1], eSide[1])) {
8134 MESSAGE(" Free Border 2 not found " );
8135 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8138 if ( aResult != SEW_OK )
8141 if (!theSideIsFreeBorder) {
8145 // -------------------------------------------------------------------------
8147 // 1. If nodes to merge are not coincident, move nodes of the free border
8148 // from the coord sys defined by the direction from the first to last
8149 // nodes of the border to the correspondent sys of the side 2
8150 // 2. On the side 2, find the links most co-directed with the correspondent
8151 // links of the free border
8152 // -------------------------------------------------------------------------
8154 // 1. Since sewing may break if there are volumes to split on the side 2,
8155 // we wont move nodes but just compute new coordinates for them
8156 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8157 TNodeXYZMap nBordXYZ;
8158 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8159 list< const SMDS_MeshNode* >::iterator nBordIt;
8161 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8162 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8163 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8164 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8165 double tol2 = 1.e-8;
8166 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8167 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8168 // Need node movement.
8170 // find X and Z axes to create trsf
8171 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8173 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8175 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8178 gp_Ax3 toBordAx( Pb1, Zb, X );
8179 gp_Ax3 fromSideAx( Ps1, Zs, X );
8180 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8182 gp_Trsf toBordSys, fromSide2Sys;
8183 toBordSys.SetTransformation( toBordAx );
8184 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8185 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8188 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8189 const SMDS_MeshNode* n = *nBordIt;
8190 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8191 toBordSys.Transforms( xyz );
8192 fromSide2Sys.Transforms( xyz );
8193 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8197 // just insert nodes XYZ in the nBordXYZ map
8198 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8199 const SMDS_MeshNode* n = *nBordIt;
8200 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8204 // 2. On the side 2, find the links most co-directed with the correspondent
8205 // links of the free border
8207 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8208 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8209 sideNodes.push_back( theSideFirstNode );
8211 bool hasVolumes = false;
8212 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8213 set<long> foundSideLinkIDs, checkedLinkIDs;
8214 SMDS_VolumeTool volume;
8215 //const SMDS_MeshNode* faceNodes[ 4 ];
8217 const SMDS_MeshNode* sideNode;
8218 const SMDS_MeshElement* sideElem = 0;
8219 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8220 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8221 nBordIt = bordNodes.begin();
8223 // border node position and border link direction to compare with
8224 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8225 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8226 // choose next side node by link direction or by closeness to
8227 // the current border node:
8228 bool searchByDir = ( *nBordIt != theBordLastNode );
8230 // find the next node on the Side 2
8232 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8234 checkedLinkIDs.clear();
8235 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8237 // loop on inverse elements of current node (prevSideNode) on the Side 2
8238 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8239 while ( invElemIt->more() )
8241 const SMDS_MeshElement* elem = invElemIt->next();
8242 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8243 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8244 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8245 bool isVolume = volume.Set( elem );
8246 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8247 if ( isVolume ) // --volume
8249 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8250 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8251 if(elem->IsQuadratic()) {
8252 const SMDS_VtkFace* F =
8253 dynamic_cast<const SMDS_VtkFace*>(elem);
8254 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8255 // use special nodes iterator
8256 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8257 while( anIter->more() ) {
8258 nodes[ iNode ] = cast2Node(anIter->next());
8259 if ( nodes[ iNode++ ] == prevSideNode )
8260 iPrevNode = iNode - 1;
8264 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8265 while ( nIt->more() ) {
8266 nodes[ iNode ] = cast2Node( nIt->next() );
8267 if ( nodes[ iNode++ ] == prevSideNode )
8268 iPrevNode = iNode - 1;
8271 // there are 2 links to check
8276 // loop on links, to be precise, on the second node of links
8277 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8278 const SMDS_MeshNode* n = nodes[ iNode ];
8280 if ( !volume.IsLinked( n, prevSideNode ))
8284 if ( iNode ) // a node before prevSideNode
8285 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8286 else // a node after prevSideNode
8287 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8289 // check if this link was already used
8290 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8291 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8292 if (!isJustChecked &&
8293 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8295 // test a link geometrically
8296 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8297 bool linkIsBetter = false;
8298 double dot = 0.0, dist = 0.0;
8299 if ( searchByDir ) { // choose most co-directed link
8300 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8301 linkIsBetter = ( dot > maxDot );
8303 else { // choose link with the node closest to bordPos
8304 dist = ( nextXYZ - bordPos ).SquareModulus();
8305 linkIsBetter = ( dist < minDist );
8307 if ( linkIsBetter ) {
8316 } // loop on inverse elements of prevSideNode
8319 MESSAGE(" Cant find path by links of the Side 2 ");
8320 return SEW_BAD_SIDE_NODES;
8322 sideNodes.push_back( sideNode );
8323 sideElems.push_back( sideElem );
8324 foundSideLinkIDs.insert ( linkID );
8325 prevSideNode = sideNode;
8327 if ( *nBordIt == theBordLastNode )
8328 searchByDir = false;
8330 // find the next border link to compare with
8331 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8332 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8333 // move to next border node if sideNode is before forward border node (bordPos)
8334 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8335 prevBordNode = *nBordIt;
8337 bordPos = nBordXYZ[ *nBordIt ];
8338 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8339 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8343 while ( sideNode != theSideSecondNode );
8345 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8346 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8347 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8349 } // end nodes search on the side 2
8351 // ============================
8352 // sew the border to the side 2
8353 // ============================
8355 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8356 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8358 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8359 if ( toMergeConformal && toCreatePolygons )
8361 // do not merge quadrangles if polygons are OK (IPAL0052824)
8362 eIt[0] = eSide[0].begin();
8363 eIt[1] = eSide[1].begin();
8364 bool allQuads[2] = { true, true };
8365 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8366 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8367 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8369 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8372 TListOfListOfNodes nodeGroupsToMerge;
8373 if (( toMergeConformal ) ||
8374 ( theSideIsFreeBorder && !theSideThirdNode )) {
8376 // all nodes are to be merged
8378 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8379 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8380 nIt[0]++, nIt[1]++ )
8382 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8383 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8384 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8389 // insert new nodes into the border and the side to get equal nb of segments
8391 // get normalized parameters of nodes on the borders
8392 vector< double > param[ 2 ];
8393 param[0].resize( maxNbNodes );
8394 param[1].resize( maxNbNodes );
8396 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8397 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8398 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8399 const SMDS_MeshNode* nPrev = *nIt;
8400 double bordLength = 0;
8401 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8402 const SMDS_MeshNode* nCur = *nIt;
8403 gp_XYZ segment (nCur->X() - nPrev->X(),
8404 nCur->Y() - nPrev->Y(),
8405 nCur->Z() - nPrev->Z());
8406 double segmentLen = segment.Modulus();
8407 bordLength += segmentLen;
8408 param[ iBord ][ iNode ] = bordLength;
8411 // normalize within [0,1]
8412 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8413 param[ iBord ][ iNode ] /= bordLength;
8417 // loop on border segments
8418 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8419 int i[ 2 ] = { 0, 0 };
8420 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8421 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8423 TElemOfNodeListMap insertMap;
8424 TElemOfNodeListMap::iterator insertMapIt;
8426 // key: elem to insert nodes into
8427 // value: 2 nodes to insert between + nodes to be inserted
8429 bool next[ 2 ] = { false, false };
8431 // find min adjacent segment length after sewing
8432 double nextParam = 10., prevParam = 0;
8433 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8434 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8435 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8436 if ( i[ iBord ] > 0 )
8437 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8439 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8440 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8441 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8443 // choose to insert or to merge nodes
8444 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8445 if ( Abs( du ) <= minSegLen * 0.2 ) {
8448 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8449 const SMDS_MeshNode* n0 = *nIt[0];
8450 const SMDS_MeshNode* n1 = *nIt[1];
8451 nodeGroupsToMerge.back().push_back( n1 );
8452 nodeGroupsToMerge.back().push_back( n0 );
8453 // position of node of the border changes due to merge
8454 param[ 0 ][ i[0] ] += du;
8455 // move n1 for the sake of elem shape evaluation during insertion.
8456 // n1 will be removed by MergeNodes() anyway
8457 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8458 next[0] = next[1] = true;
8463 int intoBord = ( du < 0 ) ? 0 : 1;
8464 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8465 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8466 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8467 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8468 if ( intoBord == 1 ) {
8469 // move node of the border to be on a link of elem of the side
8470 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8471 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8472 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8473 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8474 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8476 insertMapIt = insertMap.find( elem );
8477 bool notFound = ( insertMapIt == insertMap.end() );
8478 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8480 // insert into another link of the same element:
8481 // 1. perform insertion into the other link of the elem
8482 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8483 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8484 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8485 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8486 // 2. perform insertion into the link of adjacent faces
8487 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8488 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8490 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8491 InsertNodesIntoLink( seg, n12, n22, nodeList );
8493 if (toCreatePolyedrs) {
8494 // perform insertion into the links of adjacent volumes
8495 UpdateVolumes(n12, n22, nodeList);
8497 // 3. find an element appeared on n1 and n2 after the insertion
8498 insertMap.erase( elem );
8499 elem = findAdjacentFace( n1, n2, 0 );
8501 if ( notFound || otherLink ) {
8502 // add element and nodes of the side into the insertMap
8503 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8504 (*insertMapIt).second.push_back( n1 );
8505 (*insertMapIt).second.push_back( n2 );
8507 // add node to be inserted into elem
8508 (*insertMapIt).second.push_back( nIns );
8509 next[ 1 - intoBord ] = true;
8512 // go to the next segment
8513 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8514 if ( next[ iBord ] ) {
8515 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8517 nPrev[ iBord ] = *nIt[ iBord ];
8518 nIt[ iBord ]++; i[ iBord ]++;
8522 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8524 // perform insertion of nodes into elements
8526 for (insertMapIt = insertMap.begin();
8527 insertMapIt != insertMap.end();
8530 const SMDS_MeshElement* elem = (*insertMapIt).first;
8531 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8532 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8533 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8535 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8537 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8538 InsertNodesIntoLink( seg, n1, n2, nodeList );
8541 if ( !theSideIsFreeBorder ) {
8542 // look for and insert nodes into the faces adjacent to elem
8543 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8544 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8547 if (toCreatePolyedrs) {
8548 // perform insertion into the links of adjacent volumes
8549 UpdateVolumes(n1, n2, nodeList);
8552 } // end: insert new nodes
8554 MergeNodes ( nodeGroupsToMerge );
8557 // Remove coincident segments
8560 TIDSortedElemSet segments;
8561 SMESH_SequenceOfElemPtr newFaces;
8562 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8564 if ( !myLastCreatedElems(i) ) continue;
8565 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8566 segments.insert( segments.end(), myLastCreatedElems(i) );
8568 newFaces.Append( myLastCreatedElems(i) );
8570 // get segments adjacent to merged nodes
8571 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8572 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8574 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8575 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8576 while ( segIt->more() )
8577 segments.insert( segIt->next() );
8581 TListOfListOfElementsID equalGroups;
8582 if ( !segments.empty() )
8583 FindEqualElements( segments, equalGroups );
8584 if ( !equalGroups.empty() )
8586 // remove from segments those that will be removed
8587 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8588 for ( ; itGroups != equalGroups.end(); ++itGroups )
8590 list< int >& group = *itGroups;
8591 list< int >::iterator id = group.begin();
8592 for ( ++id; id != group.end(); ++id )
8593 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8594 segments.erase( seg );
8596 // remove equal segments
8597 MergeElements( equalGroups );
8599 // restore myLastCreatedElems
8600 myLastCreatedElems = newFaces;
8601 TIDSortedElemSet::iterator seg = segments.begin();
8602 for ( ; seg != segments.end(); ++seg )
8603 myLastCreatedElems.Append( *seg );
8609 //=======================================================================
8610 //function : InsertNodesIntoLink
8611 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8612 // and theBetweenNode2 and split theElement
8613 //=======================================================================
8615 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8616 const SMDS_MeshNode* theBetweenNode1,
8617 const SMDS_MeshNode* theBetweenNode2,
8618 list<const SMDS_MeshNode*>& theNodesToInsert,
8619 const bool toCreatePoly)
8621 if ( !theElement ) return;
8623 SMESHDS_Mesh *aMesh = GetMeshDS();
8624 vector<const SMDS_MeshElement*> newElems;
8626 if ( theElement->GetType() == SMDSAbs_Edge )
8628 theNodesToInsert.push_front( theBetweenNode1 );
8629 theNodesToInsert.push_back ( theBetweenNode2 );
8630 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8631 const SMDS_MeshNode* n1 = *n;
8632 for ( ++n; n != theNodesToInsert.end(); ++n )
8634 const SMDS_MeshNode* n2 = *n;
8635 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8636 AddToSameGroups( seg, theElement, aMesh );
8638 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8641 theNodesToInsert.pop_front();
8642 theNodesToInsert.pop_back();
8644 if ( theElement->IsQuadratic() ) // add a not split part
8646 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8647 theElement->end_nodes() );
8648 int iOther = 0, nbN = nodes.size();
8649 for ( ; iOther < nbN; ++iOther )
8650 if ( nodes[iOther] != theBetweenNode1 &&
8651 nodes[iOther] != theBetweenNode2 )
8655 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8656 AddToSameGroups( seg, theElement, aMesh );
8658 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8660 else if ( iOther == 2 )
8662 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8663 AddToSameGroups( seg, theElement, aMesh );
8665 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8668 // treat new elements
8669 for ( size_t i = 0; i < newElems.size(); ++i )
8672 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8673 myLastCreatedElems.Append( newElems[i] );
8675 ReplaceElemInGroups( theElement, newElems, aMesh );
8676 aMesh->RemoveElement( theElement );
8679 } // if ( theElement->GetType() == SMDSAbs_Edge )
8681 const SMDS_MeshElement* theFace = theElement;
8682 if ( theFace->GetType() != SMDSAbs_Face ) return;
8684 // find indices of 2 link nodes and of the rest nodes
8685 int iNode = 0, il1, il2, i3, i4;
8686 il1 = il2 = i3 = i4 = -1;
8687 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8689 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8690 while ( nodeIt->more() ) {
8691 const SMDS_MeshNode* n = nodeIt->next();
8692 if ( n == theBetweenNode1 )
8694 else if ( n == theBetweenNode2 )
8700 nodes[ iNode++ ] = n;
8702 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8705 // arrange link nodes to go one after another regarding the face orientation
8706 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8707 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8712 aNodesToInsert.reverse();
8714 // check that not link nodes of a quadrangles are in good order
8715 int nbFaceNodes = theFace->NbNodes();
8716 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8722 if (toCreatePoly || theFace->IsPoly()) {
8725 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8727 // add nodes of face up to first node of link
8730 if ( theFace->IsQuadratic() ) {
8731 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8732 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8733 // use special nodes iterator
8734 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8735 while( anIter->more() && !isFLN ) {
8736 const SMDS_MeshNode* n = cast2Node(anIter->next());
8737 poly_nodes[iNode++] = n;
8738 if (n == nodes[il1]) {
8742 // add nodes to insert
8743 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8744 for (; nIt != aNodesToInsert.end(); nIt++) {
8745 poly_nodes[iNode++] = *nIt;
8747 // add nodes of face starting from last node of link
8748 while ( anIter->more() ) {
8749 poly_nodes[iNode++] = cast2Node(anIter->next());
8753 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8754 while ( nodeIt->more() && !isFLN ) {
8755 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8756 poly_nodes[iNode++] = n;
8757 if (n == nodes[il1]) {
8761 // add nodes to insert
8762 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8763 for (; nIt != aNodesToInsert.end(); nIt++) {
8764 poly_nodes[iNode++] = *nIt;
8766 // add nodes of face starting from last node of link
8767 while ( nodeIt->more() ) {
8768 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8769 poly_nodes[iNode++] = n;
8774 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8777 else if ( !theFace->IsQuadratic() )
8779 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8780 int nbLinkNodes = 2 + aNodesToInsert.size();
8781 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8782 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8783 linkNodes[ 0 ] = nodes[ il1 ];
8784 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8785 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8786 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8787 linkNodes[ iNode++ ] = *nIt;
8789 // decide how to split a quadrangle: compare possible variants
8790 // and choose which of splits to be a quadrangle
8791 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8792 if ( nbFaceNodes == 3 ) {
8793 iBestQuad = nbSplits;
8796 else if ( nbFaceNodes == 4 ) {
8797 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8798 double aBestRate = DBL_MAX;
8799 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8801 double aBadRate = 0;
8802 // evaluate elements quality
8803 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8804 if ( iSplit == iQuad ) {
8805 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8809 aBadRate += getBadRate( &quad, aCrit );
8812 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8814 nodes[ iSplit < iQuad ? i4 : i3 ]);
8815 aBadRate += getBadRate( &tria, aCrit );
8819 if ( aBadRate < aBestRate ) {
8821 aBestRate = aBadRate;
8826 // create new elements
8828 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8830 if ( iSplit == iBestQuad )
8831 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8836 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8838 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8841 const SMDS_MeshNode* newNodes[ 4 ];
8842 newNodes[ 0 ] = linkNodes[ i1 ];
8843 newNodes[ 1 ] = linkNodes[ i2 ];
8844 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8845 newNodes[ 3 ] = nodes[ i4 ];
8846 if (iSplit == iBestQuad)
8847 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8849 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8851 } // end if(!theFace->IsQuadratic())
8853 else { // theFace is quadratic
8854 // we have to split theFace on simple triangles and one simple quadrangle
8856 int nbshift = tmp*2;
8857 // shift nodes in nodes[] by nbshift
8859 for(i=0; i<nbshift; i++) {
8860 const SMDS_MeshNode* n = nodes[0];
8861 for(j=0; j<nbFaceNodes-1; j++) {
8862 nodes[j] = nodes[j+1];
8864 nodes[nbFaceNodes-1] = n;
8866 il1 = il1 - nbshift;
8867 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8868 // n0 n1 n2 n0 n1 n2
8869 // +-----+-----+ +-----+-----+
8878 // create new elements
8880 if ( nbFaceNodes == 6 ) { // quadratic triangle
8881 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8882 if ( theFace->IsMediumNode(nodes[il1]) ) {
8883 // create quadrangle
8884 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8890 // create quadrangle
8891 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8897 else { // nbFaceNodes==8 - quadratic quadrangle
8898 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8899 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8900 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8901 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8902 // create quadrangle
8903 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8909 // create quadrangle
8910 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8916 // create needed triangles using n1,n2,n3 and inserted nodes
8917 int nbn = 2 + aNodesToInsert.size();
8918 vector<const SMDS_MeshNode*> aNodes(nbn);
8919 aNodes[0 ] = nodes[n1];
8920 aNodes[nbn-1] = nodes[n2];
8921 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8923 aNodes[iNode++] = *nIt;
8925 for ( i = 1; i < nbn; i++ )
8926 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8929 // remove the old face
8930 for ( size_t i = 0; i < newElems.size(); ++i )
8933 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8934 myLastCreatedElems.Append( newElems[i] );
8936 ReplaceElemInGroups( theFace, newElems, aMesh );
8937 aMesh->RemoveElement(theFace);
8939 } // InsertNodesIntoLink()
8941 //=======================================================================
8942 //function : UpdateVolumes
8944 //=======================================================================
8946 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8947 const SMDS_MeshNode* theBetweenNode2,
8948 list<const SMDS_MeshNode*>& theNodesToInsert)
8950 myLastCreatedElems.Clear();
8951 myLastCreatedNodes.Clear();
8953 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8954 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8955 const SMDS_MeshElement* elem = invElemIt->next();
8957 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8958 SMDS_VolumeTool aVolume (elem);
8959 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8962 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8963 int iface, nbFaces = aVolume.NbFaces();
8964 vector<const SMDS_MeshNode *> poly_nodes;
8965 vector<int> quantities (nbFaces);
8967 for (iface = 0; iface < nbFaces; iface++) {
8968 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8969 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8970 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8972 for (int inode = 0; inode < nbFaceNodes; inode++) {
8973 poly_nodes.push_back(faceNodes[inode]);
8975 if (nbInserted == 0) {
8976 if (faceNodes[inode] == theBetweenNode1) {
8977 if (faceNodes[inode + 1] == theBetweenNode2) {
8978 nbInserted = theNodesToInsert.size();
8980 // add nodes to insert
8981 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8982 for (; nIt != theNodesToInsert.end(); nIt++) {
8983 poly_nodes.push_back(*nIt);
8987 else if (faceNodes[inode] == theBetweenNode2) {
8988 if (faceNodes[inode + 1] == theBetweenNode1) {
8989 nbInserted = theNodesToInsert.size();
8991 // add nodes to insert in reversed order
8992 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8994 for (; nIt != theNodesToInsert.begin(); nIt--) {
8995 poly_nodes.push_back(*nIt);
8997 poly_nodes.push_back(*nIt);
9004 quantities[iface] = nbFaceNodes + nbInserted;
9007 // Replace the volume
9008 SMESHDS_Mesh *aMesh = GetMeshDS();
9010 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9012 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9013 myLastCreatedElems.Append( newElem );
9014 ReplaceElemInGroups( elem, newElem, aMesh );
9016 aMesh->RemoveElement( elem );
9022 //================================================================================
9024 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9026 //================================================================================
9028 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9029 vector<const SMDS_MeshNode *> & nodes,
9030 vector<int> & nbNodeInFaces )
9033 nbNodeInFaces.clear();
9034 SMDS_VolumeTool vTool ( elem );
9035 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9037 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9038 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9039 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9044 //=======================================================================
9046 * \brief Convert elements contained in a sub-mesh to quadratic
9047 * \return int - nb of checked elements
9049 //=======================================================================
9051 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9052 SMESH_MesherHelper& theHelper,
9053 const bool theForce3d)
9056 if( !theSm ) return nbElem;
9058 vector<int> nbNodeInFaces;
9059 vector<const SMDS_MeshNode *> nodes;
9060 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9061 while(ElemItr->more())
9064 const SMDS_MeshElement* elem = ElemItr->next();
9065 if( !elem ) continue;
9067 // analyse a necessity of conversion
9068 const SMDSAbs_ElementType aType = elem->GetType();
9069 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9071 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9072 bool hasCentralNodes = false;
9073 if ( elem->IsQuadratic() )
9076 switch ( aGeomType ) {
9077 case SMDSEntity_Quad_Triangle:
9078 case SMDSEntity_Quad_Quadrangle:
9079 case SMDSEntity_Quad_Hexa:
9080 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9082 case SMDSEntity_BiQuad_Triangle:
9083 case SMDSEntity_BiQuad_Quadrangle:
9084 case SMDSEntity_TriQuad_Hexa:
9085 alreadyOK = theHelper.GetIsBiQuadratic();
9086 hasCentralNodes = true;
9091 // take into account already present modium nodes
9093 case SMDSAbs_Volume:
9094 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9096 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9098 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9104 // get elem data needed to re-create it
9106 const int id = elem->GetID();
9107 const int nbNodes = elem->NbCornerNodes();
9108 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9109 if ( aGeomType == SMDSEntity_Polyhedra )
9110 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9111 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9112 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9114 // remove a linear element
9115 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9117 // remove central nodes of biquadratic elements (biquad->quad convertion)
9118 if ( hasCentralNodes )
9119 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9120 if ( nodes[i]->NbInverseElements() == 0 )
9121 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9123 const SMDS_MeshElement* NewElem = 0;
9129 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9137 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9140 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9143 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9147 case SMDSAbs_Volume :
9151 case SMDSEntity_Tetra:
9152 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9154 case SMDSEntity_Pyramid:
9155 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9157 case SMDSEntity_Penta:
9158 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9160 case SMDSEntity_Hexa:
9161 case SMDSEntity_Quad_Hexa:
9162 case SMDSEntity_TriQuad_Hexa:
9163 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9164 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9166 case SMDSEntity_Hexagonal_Prism:
9168 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9175 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9176 if( NewElem && NewElem->getshapeId() < 1 )
9177 theSm->AddElement( NewElem );
9181 //=======================================================================
9182 //function : ConvertToQuadratic
9184 //=======================================================================
9186 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9188 SMESHDS_Mesh* meshDS = GetMeshDS();
9190 SMESH_MesherHelper aHelper(*myMesh);
9192 aHelper.SetIsQuadratic( true );
9193 aHelper.SetIsBiQuadratic( theToBiQuad );
9194 aHelper.SetElementsOnShape(true);
9195 aHelper.ToFixNodeParameters( true );
9197 // convert elements assigned to sub-meshes
9198 int nbCheckedElems = 0;
9199 if ( myMesh->HasShapeToMesh() )
9201 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9203 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9204 while ( smIt->more() ) {
9205 SMESH_subMesh* sm = smIt->next();
9206 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9207 aHelper.SetSubShape( sm->GetSubShape() );
9208 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9214 // convert elements NOT assigned to sub-meshes
9215 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9216 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9218 aHelper.SetElementsOnShape(false);
9219 SMESHDS_SubMesh *smDS = 0;
9222 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9223 while( aEdgeItr->more() )
9225 const SMDS_MeshEdge* edge = aEdgeItr->next();
9226 if ( !edge->IsQuadratic() )
9228 int id = edge->GetID();
9229 const SMDS_MeshNode* n1 = edge->GetNode(0);
9230 const SMDS_MeshNode* n2 = edge->GetNode(1);
9232 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9234 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9235 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9239 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9244 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9245 while( aFaceItr->more() )
9247 const SMDS_MeshFace* face = aFaceItr->next();
9248 if ( !face ) continue;
9250 const SMDSAbs_EntityType type = face->GetEntityType();
9254 case SMDSEntity_Quad_Triangle:
9255 case SMDSEntity_Quad_Quadrangle:
9256 alreadyOK = !theToBiQuad;
9257 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9259 case SMDSEntity_BiQuad_Triangle:
9260 case SMDSEntity_BiQuad_Quadrangle:
9261 alreadyOK = theToBiQuad;
9262 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9264 default: alreadyOK = false;
9269 const int id = face->GetID();
9270 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9272 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9274 SMDS_MeshFace * NewFace = 0;
9277 case SMDSEntity_Triangle:
9278 case SMDSEntity_Quad_Triangle:
9279 case SMDSEntity_BiQuad_Triangle:
9280 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9281 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9282 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9285 case SMDSEntity_Quadrangle:
9286 case SMDSEntity_Quad_Quadrangle:
9287 case SMDSEntity_BiQuad_Quadrangle:
9288 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9289 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9290 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9294 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9296 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9300 vector<int> nbNodeInFaces;
9301 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9302 while(aVolumeItr->more())
9304 const SMDS_MeshVolume* volume = aVolumeItr->next();
9305 if ( !volume ) continue;
9307 const SMDSAbs_EntityType type = volume->GetEntityType();
9308 if ( volume->IsQuadratic() )
9313 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9314 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9315 default: alreadyOK = true;
9319 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9323 const int id = volume->GetID();
9324 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9325 if ( type == SMDSEntity_Polyhedra )
9326 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9327 else if ( type == SMDSEntity_Hexagonal_Prism )
9328 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9330 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9332 SMDS_MeshVolume * NewVolume = 0;
9335 case SMDSEntity_Tetra:
9336 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9338 case SMDSEntity_Hexa:
9339 case SMDSEntity_Quad_Hexa:
9340 case SMDSEntity_TriQuad_Hexa:
9341 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9342 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9343 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9344 if ( nodes[i]->NbInverseElements() == 0 )
9345 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9347 case SMDSEntity_Pyramid:
9348 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9349 nodes[3], nodes[4], id, theForce3d);
9351 case SMDSEntity_Penta:
9352 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9353 nodes[3], nodes[4], nodes[5], id, theForce3d);
9355 case SMDSEntity_Hexagonal_Prism:
9357 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9359 ReplaceElemInGroups(volume, NewVolume, meshDS);
9364 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9365 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9366 // aHelper.FixQuadraticElements(myError);
9367 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9371 //================================================================================
9373 * \brief Makes given elements quadratic
9374 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9375 * \param theElements - elements to make quadratic
9377 //================================================================================
9379 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9380 TIDSortedElemSet& theElements,
9381 const bool theToBiQuad)
9383 if ( theElements.empty() ) return;
9385 // we believe that all theElements are of the same type
9386 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9388 // get all nodes shared by theElements
9389 TIDSortedNodeSet allNodes;
9390 TIDSortedElemSet::iterator eIt = theElements.begin();
9391 for ( ; eIt != theElements.end(); ++eIt )
9392 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9394 // complete theElements with elements of lower dim whose all nodes are in allNodes
9396 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9397 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9398 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9399 for ( ; nIt != allNodes.end(); ++nIt )
9401 const SMDS_MeshNode* n = *nIt;
9402 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9403 while ( invIt->more() )
9405 const SMDS_MeshElement* e = invIt->next();
9406 const SMDSAbs_ElementType type = e->GetType();
9407 if ( e->IsQuadratic() )
9409 quadAdjacentElems[ type ].insert( e );
9412 switch ( e->GetEntityType() ) {
9413 case SMDSEntity_Quad_Triangle:
9414 case SMDSEntity_Quad_Quadrangle:
9415 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9416 case SMDSEntity_BiQuad_Triangle:
9417 case SMDSEntity_BiQuad_Quadrangle:
9418 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9419 default: alreadyOK = true;
9424 if ( type >= elemType )
9425 continue; // same type or more complex linear element
9427 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9428 continue; // e is already checked
9432 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9433 while ( nodeIt->more() && allIn )
9434 allIn = allNodes.count( nodeIt->next() );
9436 theElements.insert(e );
9440 SMESH_MesherHelper helper(*myMesh);
9441 helper.SetIsQuadratic( true );
9442 helper.SetIsBiQuadratic( theToBiQuad );
9444 // add links of quadratic adjacent elements to the helper
9446 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9447 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9448 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9450 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9452 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9453 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9454 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9456 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9458 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9459 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9460 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9462 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9465 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9467 SMESHDS_Mesh* meshDS = GetMeshDS();
9468 SMESHDS_SubMesh* smDS = 0;
9469 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9471 const SMDS_MeshElement* elem = *eIt;
9474 int nbCentralNodes = 0;
9475 switch ( elem->GetEntityType() ) {
9476 // linear convertible
9477 case SMDSEntity_Edge:
9478 case SMDSEntity_Triangle:
9479 case SMDSEntity_Quadrangle:
9480 case SMDSEntity_Tetra:
9481 case SMDSEntity_Pyramid:
9482 case SMDSEntity_Hexa:
9483 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9484 // quadratic that can become bi-quadratic
9485 case SMDSEntity_Quad_Triangle:
9486 case SMDSEntity_Quad_Quadrangle:
9487 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9489 case SMDSEntity_BiQuad_Triangle:
9490 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9491 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9493 default: alreadyOK = true;
9495 if ( alreadyOK ) continue;
9497 const SMDSAbs_ElementType type = elem->GetType();
9498 const int id = elem->GetID();
9499 const int nbNodes = elem->NbCornerNodes();
9500 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9502 helper.SetSubShape( elem->getshapeId() );
9504 if ( !smDS || !smDS->Contains( elem ))
9505 smDS = meshDS->MeshElements( elem->getshapeId() );
9506 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9508 SMDS_MeshElement * newElem = 0;
9511 case 4: // cases for most frequently used element types go first (for optimization)
9512 if ( type == SMDSAbs_Volume )
9513 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9515 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9518 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9519 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9522 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9525 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9528 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9529 nodes[4], id, theForce3d);
9532 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9533 nodes[4], nodes[5], id, theForce3d);
9537 ReplaceElemInGroups( elem, newElem, meshDS);
9538 if( newElem && smDS )
9539 smDS->AddElement( newElem );
9541 // remove central nodes
9542 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9543 if ( nodes[i]->NbInverseElements() == 0 )
9544 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9546 } // loop on theElements
9549 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9550 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9551 // helper.FixQuadraticElements( myError );
9552 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9556 //=======================================================================
9558 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9559 * \return int - nb of checked elements
9561 //=======================================================================
9563 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9564 SMDS_ElemIteratorPtr theItr,
9565 const int theShapeID)
9568 SMESHDS_Mesh* meshDS = GetMeshDS();
9569 ElemFeatures elemType;
9570 vector<const SMDS_MeshNode *> nodes;
9572 while( theItr->more() )
9574 const SMDS_MeshElement* elem = theItr->next();
9576 if( elem && elem->IsQuadratic())
9579 int nbCornerNodes = elem->NbCornerNodes();
9580 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9582 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9584 //remove a quadratic element
9585 if ( !theSm || !theSm->Contains( elem ))
9586 theSm = meshDS->MeshElements( elem->getshapeId() );
9587 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9589 // remove medium nodes
9590 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9591 if ( nodes[i]->NbInverseElements() == 0 )
9592 meshDS->RemoveFreeNode( nodes[i], theSm );
9594 // add a linear element
9595 nodes.resize( nbCornerNodes );
9596 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9597 ReplaceElemInGroups(elem, newElem, meshDS);
9598 if( theSm && newElem )
9599 theSm->AddElement( newElem );
9605 //=======================================================================
9606 //function : ConvertFromQuadratic
9608 //=======================================================================
9610 bool SMESH_MeshEditor::ConvertFromQuadratic()
9612 int nbCheckedElems = 0;
9613 if ( myMesh->HasShapeToMesh() )
9615 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9617 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9618 while ( smIt->more() ) {
9619 SMESH_subMesh* sm = smIt->next();
9620 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9621 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9627 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9628 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9630 SMESHDS_SubMesh *aSM = 0;
9631 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9639 //================================================================================
9641 * \brief Return true if all medium nodes of the element are in the node set
9643 //================================================================================
9645 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9647 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9648 if ( !nodeSet.count( elem->GetNode(i) ))
9654 //================================================================================
9656 * \brief Makes given elements linear
9658 //================================================================================
9660 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9662 if ( theElements.empty() ) return;
9664 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9665 set<int> mediumNodeIDs;
9666 TIDSortedElemSet::iterator eIt = theElements.begin();
9667 for ( ; eIt != theElements.end(); ++eIt )
9669 const SMDS_MeshElement* e = *eIt;
9670 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9671 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9674 // replace given elements by linear ones
9675 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9676 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9678 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9679 // except those elements sharing medium nodes of quadratic element whose medium nodes
9680 // are not all in mediumNodeIDs
9682 // get remaining medium nodes
9683 TIDSortedNodeSet mediumNodes;
9684 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9685 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9686 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9687 mediumNodes.insert( mediumNodes.end(), n );
9689 // find more quadratic elements to convert
9690 TIDSortedElemSet moreElemsToConvert;
9691 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9692 for ( ; nIt != mediumNodes.end(); ++nIt )
9694 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9695 while ( invIt->more() )
9697 const SMDS_MeshElement* e = invIt->next();
9698 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9700 // find a more complex element including e and
9701 // whose medium nodes are not in mediumNodes
9702 bool complexFound = false;
9703 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9705 SMDS_ElemIteratorPtr invIt2 =
9706 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9707 while ( invIt2->more() )
9709 const SMDS_MeshElement* eComplex = invIt2->next();
9710 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9712 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9713 if ( nbCommonNodes == e->NbNodes())
9715 complexFound = true;
9716 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9722 if ( !complexFound )
9723 moreElemsToConvert.insert( e );
9727 elemIt = elemSetIterator( moreElemsToConvert );
9728 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9731 //=======================================================================
9732 //function : SewSideElements
9734 //=======================================================================
9736 SMESH_MeshEditor::Sew_Error
9737 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9738 TIDSortedElemSet& theSide2,
9739 const SMDS_MeshNode* theFirstNode1,
9740 const SMDS_MeshNode* theFirstNode2,
9741 const SMDS_MeshNode* theSecondNode1,
9742 const SMDS_MeshNode* theSecondNode2)
9744 myLastCreatedElems.Clear();
9745 myLastCreatedNodes.Clear();
9747 if ( theSide1.size() != theSide2.size() )
9748 return SEW_DIFF_NB_OF_ELEMENTS;
9750 Sew_Error aResult = SEW_OK;
9752 // 1. Build set of faces representing each side
9753 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9754 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9756 // =======================================================================
9757 // 1. Build set of faces representing each side:
9758 // =======================================================================
9759 // a. build set of nodes belonging to faces
9760 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9761 // c. create temporary faces representing side of volumes if correspondent
9762 // face does not exist
9764 SMESHDS_Mesh* aMesh = GetMeshDS();
9765 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9766 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9767 TIDSortedElemSet faceSet1, faceSet2;
9768 set<const SMDS_MeshElement*> volSet1, volSet2;
9769 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9770 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9771 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9772 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9773 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9774 int iSide, iFace, iNode;
9776 list<const SMDS_MeshElement* > tempFaceList;
9777 for ( iSide = 0; iSide < 2; iSide++ ) {
9778 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9779 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9780 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9781 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9782 set<const SMDS_MeshElement*>::iterator vIt;
9783 TIDSortedElemSet::iterator eIt;
9784 set<const SMDS_MeshNode*>::iterator nIt;
9786 // check that given nodes belong to given elements
9787 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9788 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9789 int firstIndex = -1, secondIndex = -1;
9790 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9791 const SMDS_MeshElement* elem = *eIt;
9792 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9793 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9794 if ( firstIndex > -1 && secondIndex > -1 ) break;
9796 if ( firstIndex < 0 || secondIndex < 0 ) {
9797 // we can simply return until temporary faces created
9798 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9801 // -----------------------------------------------------------
9802 // 1a. Collect nodes of existing faces
9803 // and build set of face nodes in order to detect missing
9804 // faces corresponding to sides of volumes
9805 // -----------------------------------------------------------
9807 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9809 // loop on the given element of a side
9810 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9811 //const SMDS_MeshElement* elem = *eIt;
9812 const SMDS_MeshElement* elem = *eIt;
9813 if ( elem->GetType() == SMDSAbs_Face ) {
9814 faceSet->insert( elem );
9815 set <const SMDS_MeshNode*> faceNodeSet;
9816 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9817 while ( nodeIt->more() ) {
9818 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9819 nodeSet->insert( n );
9820 faceNodeSet.insert( n );
9822 setOfFaceNodeSet.insert( faceNodeSet );
9824 else if ( elem->GetType() == SMDSAbs_Volume )
9825 volSet->insert( elem );
9827 // ------------------------------------------------------------------------------
9828 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9829 // ------------------------------------------------------------------------------
9831 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9832 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9833 while ( fIt->more() ) { // loop on faces sharing a node
9834 const SMDS_MeshElement* f = fIt->next();
9835 if ( faceSet->find( f ) == faceSet->end() ) {
9836 // check if all nodes are in nodeSet and
9837 // complete setOfFaceNodeSet if they are
9838 set <const SMDS_MeshNode*> faceNodeSet;
9839 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9840 bool allInSet = true;
9841 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9842 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9843 if ( nodeSet->find( n ) == nodeSet->end() )
9846 faceNodeSet.insert( n );
9849 faceSet->insert( f );
9850 setOfFaceNodeSet.insert( faceNodeSet );
9856 // -------------------------------------------------------------------------
9857 // 1c. Create temporary faces representing sides of volumes if correspondent
9858 // face does not exist
9859 // -------------------------------------------------------------------------
9861 if ( !volSet->empty() ) {
9862 //int nodeSetSize = nodeSet->size();
9864 // loop on given volumes
9865 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9866 SMDS_VolumeTool vol (*vIt);
9867 // loop on volume faces: find free faces
9868 // --------------------------------------
9869 list<const SMDS_MeshElement* > freeFaceList;
9870 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9871 if ( !vol.IsFreeFace( iFace ))
9873 // check if there is already a face with same nodes in a face set
9874 const SMDS_MeshElement* aFreeFace = 0;
9875 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9876 int nbNodes = vol.NbFaceNodes( iFace );
9877 set <const SMDS_MeshNode*> faceNodeSet;
9878 vol.GetFaceNodes( iFace, faceNodeSet );
9879 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9881 // no such a face is given but it still can exist, check it
9882 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9883 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9886 // create a temporary face
9887 if ( nbNodes == 3 ) {
9888 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9889 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9891 else if ( nbNodes == 4 ) {
9892 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9893 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9896 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9897 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9898 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9901 tempFaceList.push_back( aFreeFace );
9905 freeFaceList.push_back( aFreeFace );
9907 } // loop on faces of a volume
9909 // choose one of several free faces of a volume
9910 // --------------------------------------------
9911 if ( freeFaceList.size() > 1 ) {
9912 // choose a face having max nb of nodes shared by other elems of a side
9913 int maxNbNodes = -1;
9914 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9915 while ( fIt != freeFaceList.end() ) { // loop on free faces
9916 int nbSharedNodes = 0;
9917 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9918 while ( nodeIt->more() ) { // loop on free face nodes
9919 const SMDS_MeshNode* n =
9920 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9921 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9922 while ( invElemIt->more() ) {
9923 const SMDS_MeshElement* e = invElemIt->next();
9924 nbSharedNodes += faceSet->count( e );
9925 nbSharedNodes += elemSet->count( e );
9928 if ( nbSharedNodes > maxNbNodes ) {
9929 maxNbNodes = nbSharedNodes;
9930 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9932 else if ( nbSharedNodes == maxNbNodes ) {
9936 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9939 if ( freeFaceList.size() > 1 )
9941 // could not choose one face, use another way
9942 // choose a face most close to the bary center of the opposite side
9943 gp_XYZ aBC( 0., 0., 0. );
9944 set <const SMDS_MeshNode*> addedNodes;
9945 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9946 eIt = elemSet2->begin();
9947 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9948 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9949 while ( nodeIt->more() ) { // loop on free face nodes
9950 const SMDS_MeshNode* n =
9951 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9952 if ( addedNodes.insert( n ).second )
9953 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9956 aBC /= addedNodes.size();
9957 double minDist = DBL_MAX;
9958 fIt = freeFaceList.begin();
9959 while ( fIt != freeFaceList.end() ) { // loop on free faces
9961 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9962 while ( nodeIt->more() ) { // loop on free face nodes
9963 const SMDS_MeshNode* n =
9964 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9965 gp_XYZ p( n->X(),n->Y(),n->Z() );
9966 dist += ( aBC - p ).SquareModulus();
9968 if ( dist < minDist ) {
9970 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9973 fIt = freeFaceList.erase( fIt++ );
9976 } // choose one of several free faces of a volume
9978 if ( freeFaceList.size() == 1 ) {
9979 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9980 faceSet->insert( aFreeFace );
9981 // complete a node set with nodes of a found free face
9982 // for ( iNode = 0; iNode < ; iNode++ )
9983 // nodeSet->insert( fNodes[ iNode ] );
9986 } // loop on volumes of a side
9988 // // complete a set of faces if new nodes in a nodeSet appeared
9989 // // ----------------------------------------------------------
9990 // if ( nodeSetSize != nodeSet->size() ) {
9991 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9992 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9993 // while ( fIt->more() ) { // loop on faces sharing a node
9994 // const SMDS_MeshElement* f = fIt->next();
9995 // if ( faceSet->find( f ) == faceSet->end() ) {
9996 // // check if all nodes are in nodeSet and
9997 // // complete setOfFaceNodeSet if they are
9998 // set <const SMDS_MeshNode*> faceNodeSet;
9999 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10000 // bool allInSet = true;
10001 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10002 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10003 // if ( nodeSet->find( n ) == nodeSet->end() )
10004 // allInSet = false;
10006 // faceNodeSet.insert( n );
10008 // if ( allInSet ) {
10009 // faceSet->insert( f );
10010 // setOfFaceNodeSet.insert( faceNodeSet );
10016 } // Create temporary faces, if there are volumes given
10019 if ( faceSet1.size() != faceSet2.size() ) {
10020 // delete temporary faces: they are in reverseElements of actual nodes
10021 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10022 // while ( tmpFaceIt->more() )
10023 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10024 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10025 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10026 // aMesh->RemoveElement(*tmpFaceIt);
10027 MESSAGE("Diff nb of faces");
10028 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10031 // ============================================================
10032 // 2. Find nodes to merge:
10033 // bind a node to remove to a node to put instead
10034 // ============================================================
10036 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10037 if ( theFirstNode1 != theFirstNode2 )
10038 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10039 if ( theSecondNode1 != theSecondNode2 )
10040 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10042 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10043 set< long > linkIdSet; // links to process
10044 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10046 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10047 list< NLink > linkList[2];
10048 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10049 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10050 // loop on links in linkList; find faces by links and append links
10051 // of the found faces to linkList
10052 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10053 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10055 NLink link[] = { *linkIt[0], *linkIt[1] };
10056 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10057 if ( !linkIdSet.count( linkID ) )
10060 // by links, find faces in the face sets,
10061 // and find indices of link nodes in the found faces;
10062 // in a face set, there is only one or no face sharing a link
10063 // ---------------------------------------------------------------
10065 const SMDS_MeshElement* face[] = { 0, 0 };
10066 vector<const SMDS_MeshNode*> fnodes[2];
10067 int iLinkNode[2][2];
10068 TIDSortedElemSet avoidSet;
10069 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10070 const SMDS_MeshNode* n1 = link[iSide].first;
10071 const SMDS_MeshNode* n2 = link[iSide].second;
10072 //cout << "Side " << iSide << " ";
10073 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10074 // find a face by two link nodes
10075 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10076 *faceSetPtr[ iSide ], avoidSet,
10077 &iLinkNode[iSide][0],
10078 &iLinkNode[iSide][1] );
10079 if ( face[ iSide ])
10081 //cout << " F " << face[ iSide]->GetID() <<endl;
10082 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10083 // put face nodes to fnodes
10084 if ( face[ iSide ]->IsQuadratic() )
10086 // use interlaced nodes iterator
10087 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10088 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10089 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10090 while ( nIter->more() )
10091 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10095 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10096 face[ iSide ]->end_nodes() );
10098 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10102 // check similarity of elements of the sides
10103 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10104 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10105 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10106 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10109 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10111 break; // do not return because it's necessary to remove tmp faces
10114 // set nodes to merge
10115 // -------------------
10117 if ( face[0] && face[1] ) {
10118 const int nbNodes = face[0]->NbNodes();
10119 if ( nbNodes != face[1]->NbNodes() ) {
10120 MESSAGE("Diff nb of face nodes");
10121 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10122 break; // do not return because it s necessary to remove tmp faces
10124 bool reverse[] = { false, false }; // order of nodes in the link
10125 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10126 // analyse link orientation in faces
10127 int i1 = iLinkNode[ iSide ][ 0 ];
10128 int i2 = iLinkNode[ iSide ][ 1 ];
10129 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10131 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10132 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10133 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10135 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10136 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10139 // add other links of the faces to linkList
10140 // -----------------------------------------
10142 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10143 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10144 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10145 if ( !iter_isnew.second ) { // already in a set: no need to process
10146 linkIdSet.erase( iter_isnew.first );
10148 else // new in set == encountered for the first time: add
10150 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10151 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10152 linkList[0].push_back ( NLink( n1, n2 ));
10153 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10158 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10161 } // loop on link lists
10163 if ( aResult == SEW_OK &&
10164 ( //linkIt[0] != linkList[0].end() ||
10165 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10166 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10167 " " << (faceSetPtr[1]->empty()));
10168 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10171 // ====================================================================
10172 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10173 // ====================================================================
10175 // delete temporary faces
10176 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10177 // while ( tmpFaceIt->more() )
10178 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10179 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10180 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10181 aMesh->RemoveElement(*tmpFaceIt);
10183 if ( aResult != SEW_OK)
10186 list< int > nodeIDsToRemove;
10187 vector< const SMDS_MeshNode*> nodes;
10188 ElemFeatures elemType;
10190 // loop on nodes replacement map
10191 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10192 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10193 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10195 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10196 nodeIDsToRemove.push_back( nToRemove->GetID() );
10197 // loop on elements sharing nToRemove
10198 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10199 while ( invElemIt->more() ) {
10200 const SMDS_MeshElement* e = invElemIt->next();
10201 // get a new suite of nodes: make replacement
10202 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10203 nodes.resize( nbNodes );
10204 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10205 while ( nIt->more() ) {
10206 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10207 nnIt = nReplaceMap.find( n );
10208 if ( nnIt != nReplaceMap.end() ) {
10210 n = (*nnIt).second;
10214 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10215 // elemIDsToRemove.push_back( e->GetID() );
10219 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10220 aMesh->RemoveElement( e );
10222 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10224 AddToSameGroups( newElem, e, aMesh );
10225 if ( int aShapeId = e->getshapeId() )
10226 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10232 Remove( nodeIDsToRemove, true );
10237 //================================================================================
10239 * \brief Find corresponding nodes in two sets of faces
10240 * \param theSide1 - first face set
10241 * \param theSide2 - second first face
10242 * \param theFirstNode1 - a boundary node of set 1
10243 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10244 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10245 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10246 * \param nReplaceMap - output map of corresponding nodes
10247 * \return bool - is a success or not
10249 //================================================================================
10252 //#define DEBUG_MATCHING_NODES
10255 SMESH_MeshEditor::Sew_Error
10256 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10257 set<const SMDS_MeshElement*>& theSide2,
10258 const SMDS_MeshNode* theFirstNode1,
10259 const SMDS_MeshNode* theFirstNode2,
10260 const SMDS_MeshNode* theSecondNode1,
10261 const SMDS_MeshNode* theSecondNode2,
10262 TNodeNodeMap & nReplaceMap)
10264 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10266 nReplaceMap.clear();
10267 if ( theFirstNode1 != theFirstNode2 )
10268 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10269 if ( theSecondNode1 != theSecondNode2 )
10270 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10272 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10273 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10275 list< NLink > linkList[2];
10276 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10277 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10279 // loop on links in linkList; find faces by links and append links
10280 // of the found faces to linkList
10281 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10282 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10283 NLink link[] = { *linkIt[0], *linkIt[1] };
10284 if ( linkSet.find( link[0] ) == linkSet.end() )
10287 // by links, find faces in the face sets,
10288 // and find indices of link nodes in the found faces;
10289 // in a face set, there is only one or no face sharing a link
10290 // ---------------------------------------------------------------
10292 const SMDS_MeshElement* face[] = { 0, 0 };
10293 list<const SMDS_MeshNode*> notLinkNodes[2];
10294 //bool reverse[] = { false, false }; // order of notLinkNodes
10296 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10298 const SMDS_MeshNode* n1 = link[iSide].first;
10299 const SMDS_MeshNode* n2 = link[iSide].second;
10300 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10301 set< const SMDS_MeshElement* > facesOfNode1;
10302 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10304 // during a loop of the first node, we find all faces around n1,
10305 // during a loop of the second node, we find one face sharing both n1 and n2
10306 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10307 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10308 while ( fIt->more() ) { // loop on faces sharing a node
10309 const SMDS_MeshElement* f = fIt->next();
10310 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10311 ! facesOfNode1.insert( f ).second ) // f encounters twice
10313 if ( face[ iSide ] ) {
10314 MESSAGE( "2 faces per link " );
10315 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10318 faceSet->erase( f );
10320 // get not link nodes
10321 int nbN = f->NbNodes();
10322 if ( f->IsQuadratic() )
10324 nbNodes[ iSide ] = nbN;
10325 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10326 int i1 = f->GetNodeIndex( n1 );
10327 int i2 = f->GetNodeIndex( n2 );
10328 int iEnd = nbN, iBeg = -1, iDelta = 1;
10329 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10331 std::swap( iEnd, iBeg ); iDelta = -1;
10336 if ( i == iEnd ) i = iBeg + iDelta;
10337 if ( i == i1 ) break;
10338 nodes.push_back ( f->GetNode( i ) );
10344 // check similarity of elements of the sides
10345 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10346 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10347 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10348 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10351 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10355 // set nodes to merge
10356 // -------------------
10358 if ( face[0] && face[1] ) {
10359 if ( nbNodes[0] != nbNodes[1] ) {
10360 MESSAGE("Diff nb of face nodes");
10361 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10363 #ifdef DEBUG_MATCHING_NODES
10364 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10365 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10366 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10368 int nbN = nbNodes[0];
10370 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10371 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10372 for ( int i = 0 ; i < nbN - 2; ++i ) {
10373 #ifdef DEBUG_MATCHING_NODES
10374 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10376 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10380 // add other links of the face 1 to linkList
10381 // -----------------------------------------
10383 const SMDS_MeshElement* f0 = face[0];
10384 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10385 for ( int i = 0; i < nbN; i++ )
10387 const SMDS_MeshNode* n2 = f0->GetNode( i );
10388 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10389 linkSet.insert( SMESH_TLink( n1, n2 ));
10390 if ( !iter_isnew.second ) { // already in a set: no need to process
10391 linkSet.erase( iter_isnew.first );
10393 else // new in set == encountered for the first time: add
10395 #ifdef DEBUG_MATCHING_NODES
10396 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10397 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10399 linkList[0].push_back ( NLink( n1, n2 ));
10400 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10405 } // loop on link lists
10410 //================================================================================
10412 * \brief Create elements equal (on same nodes) to given ones
10413 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10414 * elements of the uppest dimension are duplicated.
10416 //================================================================================
10418 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10420 ClearLastCreated();
10421 SMESHDS_Mesh* mesh = GetMeshDS();
10423 // get an element type and an iterator over elements
10425 SMDSAbs_ElementType type = SMDSAbs_All;
10426 SMDS_ElemIteratorPtr elemIt;
10427 vector< const SMDS_MeshElement* > allElems;
10428 if ( theElements.empty() )
10430 if ( mesh->NbNodes() == 0 )
10432 // get most complex type
10433 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10434 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10435 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10437 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10438 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10443 // put all elements in the vector <allElems>
10444 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10445 elemIt = mesh->elementsIterator( type );
10446 while ( elemIt->more() )
10447 allElems.push_back( elemIt->next());
10448 elemIt = elemSetIterator( allElems );
10452 type = (*theElements.begin())->GetType();
10453 elemIt = elemSetIterator( theElements );
10456 // duplicate elements
10458 ElemFeatures elemType;
10460 vector< const SMDS_MeshNode* > nodes;
10461 while ( elemIt->more() )
10463 const SMDS_MeshElement* elem = elemIt->next();
10464 if ( elem->GetType() != type )
10467 elemType.Init( elem, /*basicOnly=*/false );
10468 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10470 AddElement( nodes, elemType );
10474 //================================================================================
10476 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10477 \param theElems - the list of elements (edges or faces) to be replicated
10478 The nodes for duplication could be found from these elements
10479 \param theNodesNot - list of nodes to NOT replicate
10480 \param theAffectedElems - the list of elements (cells and edges) to which the
10481 replicated nodes should be associated to.
10482 \return TRUE if operation has been completed successfully, FALSE otherwise
10484 //================================================================================
10486 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10487 const TIDSortedElemSet& theNodesNot,
10488 const TIDSortedElemSet& theAffectedElems )
10490 myLastCreatedElems.Clear();
10491 myLastCreatedNodes.Clear();
10493 if ( theElems.size() == 0 )
10496 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10501 TNodeNodeMap anOldNodeToNewNode;
10502 // duplicate elements and nodes
10503 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10504 // replce nodes by duplications
10505 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10509 //================================================================================
10511 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10512 \param theMeshDS - mesh instance
10513 \param theElems - the elements replicated or modified (nodes should be changed)
10514 \param theNodesNot - nodes to NOT replicate
10515 \param theNodeNodeMap - relation of old node to new created node
10516 \param theIsDoubleElem - flag os to replicate element or modify
10517 \return TRUE if operation has been completed successfully, FALSE otherwise
10519 //================================================================================
10521 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10522 const TIDSortedElemSet& theElems,
10523 const TIDSortedElemSet& theNodesNot,
10524 TNodeNodeMap& theNodeNodeMap,
10525 const bool theIsDoubleElem )
10527 // iterate through element and duplicate them (by nodes duplication)
10529 std::vector<const SMDS_MeshNode*> newNodes;
10530 ElemFeatures elemType;
10532 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10533 for ( ; elemItr != theElems.end(); ++elemItr )
10535 const SMDS_MeshElement* anElem = *elemItr;
10539 // duplicate nodes to duplicate element
10540 bool isDuplicate = false;
10541 newNodes.resize( anElem->NbNodes() );
10542 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10544 while ( anIter->more() )
10546 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10547 const SMDS_MeshNode* aNewNode = aCurrNode;
10548 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10549 if ( n2n != theNodeNodeMap.end() )
10551 aNewNode = n2n->second;
10553 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10556 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10557 copyPosition( aCurrNode, aNewNode );
10558 theNodeNodeMap[ aCurrNode ] = aNewNode;
10559 myLastCreatedNodes.Append( aNewNode );
10561 isDuplicate |= (aCurrNode != aNewNode);
10562 newNodes[ ind++ ] = aNewNode;
10564 if ( !isDuplicate )
10567 if ( theIsDoubleElem )
10568 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10570 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10577 //================================================================================
10579 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10580 \param theNodes - identifiers of nodes to be doubled
10581 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10582 nodes. If list of element identifiers is empty then nodes are doubled but
10583 they not assigned to elements
10584 \return TRUE if operation has been completed successfully, FALSE otherwise
10586 //================================================================================
10588 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10589 const std::list< int >& theListOfModifiedElems )
10591 myLastCreatedElems.Clear();
10592 myLastCreatedNodes.Clear();
10594 if ( theListOfNodes.size() == 0 )
10597 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10601 // iterate through nodes and duplicate them
10603 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10605 std::list< int >::const_iterator aNodeIter;
10606 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10608 int aCurr = *aNodeIter;
10609 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10615 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10618 copyPosition( aNode, aNewNode );
10619 anOldNodeToNewNode[ aNode ] = aNewNode;
10620 myLastCreatedNodes.Append( aNewNode );
10624 // Create map of new nodes for modified elements
10626 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10628 std::list< int >::const_iterator anElemIter;
10629 for ( anElemIter = theListOfModifiedElems.begin();
10630 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10632 int aCurr = *anElemIter;
10633 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10637 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10639 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10641 while ( anIter->more() )
10643 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10644 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10646 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10647 aNodeArr[ ind++ ] = aNewNode;
10650 aNodeArr[ ind++ ] = aCurrNode;
10652 anElemToNodes[ anElem ] = aNodeArr;
10655 // Change nodes of elements
10657 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10658 anElemToNodesIter = anElemToNodes.begin();
10659 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10661 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10662 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10665 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10674 //================================================================================
10676 \brief Check if element located inside shape
10677 \return TRUE if IN or ON shape, FALSE otherwise
10679 //================================================================================
10681 template<class Classifier>
10682 bool isInside(const SMDS_MeshElement* theElem,
10683 Classifier& theClassifier,
10684 const double theTol)
10686 gp_XYZ centerXYZ (0, 0, 0);
10687 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10688 while (aNodeItr->more())
10689 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10691 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10692 theClassifier.Perform(aPnt, theTol);
10693 TopAbs_State aState = theClassifier.State();
10694 return (aState == TopAbs_IN || aState == TopAbs_ON );
10697 //================================================================================
10699 * \brief Classifier of the 3D point on the TopoDS_Face
10700 * with interaface suitable for isInside()
10702 //================================================================================
10704 struct _FaceClassifier
10706 Extrema_ExtPS _extremum;
10707 BRepAdaptor_Surface _surface;
10708 TopAbs_State _state;
10710 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10712 _extremum.Initialize( _surface,
10713 _surface.FirstUParameter(), _surface.LastUParameter(),
10714 _surface.FirstVParameter(), _surface.LastVParameter(),
10715 _surface.Tolerance(), _surface.Tolerance() );
10717 void Perform(const gp_Pnt& aPnt, double theTol)
10720 _state = TopAbs_OUT;
10721 _extremum.Perform(aPnt);
10722 if ( _extremum.IsDone() )
10723 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10724 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10726 TopAbs_State State() const
10733 //================================================================================
10735 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10736 This method is the first step of DoubleNodeElemGroupsInRegion.
10737 \param theElems - list of groups of elements (edges or faces) to be replicated
10738 \param theNodesNot - list of groups of nodes not to replicated
10739 \param theShape - shape to detect affected elements (element which geometric center
10740 located on or inside shape). If the shape is null, detection is done on faces orientations
10741 (select elements with a gravity center on the side given by faces normals).
10742 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10743 The replicated nodes should be associated to affected elements.
10744 \return groups of affected elements
10745 \sa DoubleNodeElemGroupsInRegion()
10747 //================================================================================
10749 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10750 const TIDSortedElemSet& theNodesNot,
10751 const TopoDS_Shape& theShape,
10752 TIDSortedElemSet& theAffectedElems)
10754 if ( theShape.IsNull() )
10756 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10757 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10758 std::set<const SMDS_MeshElement*> edgesToCheck;
10759 alreadyCheckedNodes.clear();
10760 alreadyCheckedElems.clear();
10761 edgesToCheck.clear();
10763 // --- iterates on elements to be replicated and get elements by back references from their nodes
10765 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10766 for ( ; elemItr != theElems.end(); ++elemItr )
10768 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10769 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10772 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10773 std::set<const SMDS_MeshNode*> nodesElem;
10775 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10776 while ( nodeItr->more() )
10778 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10779 nodesElem.insert(aNode);
10781 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10782 for (; nodit != nodesElem.end(); nodit++)
10784 const SMDS_MeshNode* aNode = *nodit;
10785 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10787 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10789 alreadyCheckedNodes.insert(aNode);
10790 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10791 while ( backElemItr->more() )
10793 const SMDS_MeshElement* curElem = backElemItr->next();
10794 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10796 if (theElems.find(curElem) != theElems.end())
10798 alreadyCheckedElems.insert(curElem);
10799 double x=0, y=0, z=0;
10801 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10802 while ( nodeItr2->more() )
10804 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10805 x += anotherNode->X();
10806 y += anotherNode->Y();
10807 z += anotherNode->Z();
10811 p.SetCoord( x/nb -aNode->X(),
10813 z/nb -aNode->Z() );
10816 theAffectedElems.insert( curElem );
10818 else if (curElem->GetType() == SMDSAbs_Edge)
10819 edgesToCheck.insert(curElem);
10823 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10824 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10825 for( ; eit != edgesToCheck.end(); eit++)
10827 bool onside = true;
10828 const SMDS_MeshElement* anEdge = *eit;
10829 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10830 while ( nodeItr->more() )
10832 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10833 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10841 theAffectedElems.insert(anEdge);
10847 const double aTol = Precision::Confusion();
10848 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10849 auto_ptr<_FaceClassifier> aFaceClassifier;
10850 if ( theShape.ShapeType() == TopAbs_SOLID )
10852 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10853 bsc3d->PerformInfinitePoint(aTol);
10855 else if (theShape.ShapeType() == TopAbs_FACE )
10857 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10860 // iterates on indicated elements and get elements by back references from their nodes
10861 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10862 for ( ; elemItr != theElems.end(); ++elemItr )
10864 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10867 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10868 while ( nodeItr->more() )
10870 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10871 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10873 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10874 while ( backElemItr->more() )
10876 const SMDS_MeshElement* curElem = backElemItr->next();
10877 if ( curElem && theElems.find(curElem) == theElems.end() &&
10879 isInside( curElem, *bsc3d, aTol ) :
10880 isInside( curElem, *aFaceClassifier, aTol )))
10881 theAffectedElems.insert( curElem );
10889 //================================================================================
10891 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10892 \param theElems - group of of elements (edges or faces) to be replicated
10893 \param theNodesNot - group of nodes not to replicate
10894 \param theShape - shape to detect affected elements (element which geometric center
10895 located on or inside shape).
10896 The replicated nodes should be associated to affected elements.
10897 \return TRUE if operation has been completed successfully, FALSE otherwise
10899 //================================================================================
10901 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10902 const TIDSortedElemSet& theNodesNot,
10903 const TopoDS_Shape& theShape )
10905 if ( theShape.IsNull() )
10908 const double aTol = Precision::Confusion();
10909 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10910 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10911 if ( theShape.ShapeType() == TopAbs_SOLID )
10913 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10914 bsc3d->PerformInfinitePoint(aTol);
10916 else if (theShape.ShapeType() == TopAbs_FACE )
10918 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10921 // iterates on indicated elements and get elements by back references from their nodes
10922 TIDSortedElemSet anAffected;
10923 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10924 for ( ; elemItr != theElems.end(); ++elemItr )
10926 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10930 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10931 while ( nodeItr->more() )
10933 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10934 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10936 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10937 while ( backElemItr->more() )
10939 const SMDS_MeshElement* curElem = backElemItr->next();
10940 if ( curElem && theElems.find(curElem) == theElems.end() &&
10942 isInside( curElem, *bsc3d, aTol ) :
10943 isInside( curElem, *aFaceClassifier, aTol )))
10944 anAffected.insert( curElem );
10948 return DoubleNodes( theElems, theNodesNot, anAffected );
10952 * \brief compute an oriented angle between two planes defined by four points.
10953 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10954 * @param p0 base of the rotation axe
10955 * @param p1 extremity of the rotation axe
10956 * @param g1 belongs to the first plane
10957 * @param g2 belongs to the second plane
10959 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10961 gp_Vec vref(p0, p1);
10964 gp_Vec n1 = vref.Crossed(v1);
10965 gp_Vec n2 = vref.Crossed(v2);
10967 return n2.AngleWithRef(n1, vref);
10969 catch ( Standard_Failure ) {
10971 return Max( v1.Magnitude(), v2.Magnitude() );
10975 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10976 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10977 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10978 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10979 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10980 * 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.
10981 * 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.
10982 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10983 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10984 * \param theElems - list of groups of volumes, where a group of volume is a set of
10985 * SMDS_MeshElements sorted by Id.
10986 * \param createJointElems - if TRUE, create the elements
10987 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10988 * the boundary between \a theDomains and the rest mesh
10989 * \return TRUE if operation has been completed successfully, FALSE otherwise
10991 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10992 bool createJointElems,
10993 bool onAllBoundaries)
10995 MESSAGE("----------------------------------------------");
10996 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10997 MESSAGE("----------------------------------------------");
10999 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11000 meshDS->BuildDownWardConnectivity(true);
11002 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11004 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11005 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11006 // build the list of nodes shared by 2 or more domains, with their domain indexes
11008 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11009 std::map<int,int>celldom; // cell vtkId --> domain
11010 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11011 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11012 faceDomains.clear();
11014 cellDomains.clear();
11015 nodeDomains.clear();
11016 std::map<int,int> emptyMap;
11017 std::set<int> emptySet;
11020 MESSAGE(".. Number of domains :"<<theElems.size());
11022 TIDSortedElemSet theRestDomElems;
11023 const int iRestDom = -1;
11024 const int idom0 = onAllBoundaries ? iRestDom : 0;
11025 const int nbDomains = theElems.size();
11027 // Check if the domains do not share an element
11028 for (int idom = 0; idom < nbDomains-1; idom++)
11030 // MESSAGE("... Check of domain #" << idom);
11031 const TIDSortedElemSet& domain = theElems[idom];
11032 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11033 for (; elemItr != domain.end(); ++elemItr)
11035 const SMDS_MeshElement* anElem = *elemItr;
11036 int idombisdeb = idom + 1 ;
11037 // check if the element belongs to a domain further in the list
11038 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11040 const TIDSortedElemSet& domainbis = theElems[idombis];
11041 if ( domainbis.count( anElem ))
11043 MESSAGE(".... Domain #" << idom);
11044 MESSAGE(".... Domain #" << idombis);
11045 throw SALOME_Exception("The domains are not disjoint.");
11052 for (int idom = 0; idom < nbDomains; idom++)
11055 // --- build a map (face to duplicate --> volume to modify)
11056 // with all the faces shared by 2 domains (group of elements)
11057 // and corresponding volume of this domain, for each shared face.
11058 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11060 MESSAGE("... Neighbors of domain #" << idom);
11061 const TIDSortedElemSet& domain = theElems[idom];
11062 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11063 for (; elemItr != domain.end(); ++elemItr)
11065 const SMDS_MeshElement* anElem = *elemItr;
11068 int vtkId = anElem->getVtkId();
11069 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11070 int neighborsVtkIds[NBMAXNEIGHBORS];
11071 int downIds[NBMAXNEIGHBORS];
11072 unsigned char downTypes[NBMAXNEIGHBORS];
11073 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11074 for (int n = 0; n < nbNeighbors; n++)
11076 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11077 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11078 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11081 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11083 // MESSAGE("Domain " << idombis);
11084 const TIDSortedElemSet& domainbis = theElems[idombis];
11085 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11087 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11089 DownIdType face(downIds[n], downTypes[n]);
11090 if (!faceDomains[face].count(idom))
11092 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11093 celldom[vtkId] = idom;
11094 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11098 theRestDomElems.insert( elem );
11099 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11100 celldom[neighborsVtkIds[n]] = iRestDom;
11108 //MESSAGE("Number of shared faces " << faceDomains.size());
11109 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11111 // --- explore the shared faces domain by domain,
11112 // explore the nodes of the face and see if they belong to a cell in the domain,
11113 // which has only a node or an edge on the border (not a shared face)
11115 for (int idomain = idom0; idomain < nbDomains; idomain++)
11117 //MESSAGE("Domain " << idomain);
11118 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11119 itface = faceDomains.begin();
11120 for (; itface != faceDomains.end(); ++itface)
11122 const std::map<int, int>& domvol = itface->second;
11123 if (!domvol.count(idomain))
11125 DownIdType face = itface->first;
11126 //MESSAGE(" --- face " << face.cellId);
11127 std::set<int> oldNodes;
11129 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11130 std::set<int>::iterator itn = oldNodes.begin();
11131 for (; itn != oldNodes.end(); ++itn)
11134 //MESSAGE(" node " << oldId);
11135 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11136 for (int i=0; i<l.ncells; i++)
11138 int vtkId = l.cells[i];
11139 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11140 if (!domain.count(anElem))
11142 int vtkType = grid->GetCellType(vtkId);
11143 int downId = grid->CellIdToDownId(vtkId);
11146 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11147 continue; // not OK at this stage of the algorithm:
11148 //no cells created after BuildDownWardConnectivity
11150 DownIdType aCell(downId, vtkType);
11151 cellDomains[aCell][idomain] = vtkId;
11152 celldom[vtkId] = idomain;
11153 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11159 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11160 // for each shared face, get the nodes
11161 // for each node, for each domain of the face, create a clone of the node
11163 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11164 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11165 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11167 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11168 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11169 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11171 MESSAGE(".. Duplication of the nodes");
11172 for (int idomain = idom0; idomain < nbDomains; idomain++)
11174 itface = faceDomains.begin();
11175 for (; itface != faceDomains.end(); ++itface)
11177 const std::map<int, int>& domvol = itface->second;
11178 if (!domvol.count(idomain))
11180 DownIdType face = itface->first;
11181 //MESSAGE(" --- face " << face.cellId);
11182 std::set<int> oldNodes;
11184 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11185 std::set<int>::iterator itn = oldNodes.begin();
11186 for (; itn != oldNodes.end(); ++itn)
11189 if (nodeDomains[oldId].empty())
11191 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11192 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11194 std::map<int, int>::const_iterator itdom = domvol.begin();
11195 for (; itdom != domvol.end(); ++itdom)
11197 int idom = itdom->first;
11198 //MESSAGE(" domain " << idom);
11199 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11201 if (nodeDomains[oldId].size() >= 2) // a multiple node
11203 vector<int> orderedDoms;
11204 //MESSAGE("multiple node " << oldId);
11205 if (mutipleNodes.count(oldId))
11206 orderedDoms = mutipleNodes[oldId];
11209 map<int,int>::iterator it = nodeDomains[oldId].begin();
11210 for (; it != nodeDomains[oldId].end(); ++it)
11211 orderedDoms.push_back(it->first);
11213 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11214 //stringstream txt;
11215 //for (int i=0; i<orderedDoms.size(); i++)
11216 // txt << orderedDoms[i] << " ";
11217 //MESSAGE("orderedDoms " << txt.str());
11218 mutipleNodes[oldId] = orderedDoms;
11220 double *coords = grid->GetPoint(oldId);
11221 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11222 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11223 int newId = newNode->getVtkId();
11224 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11225 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11232 MESSAGE(".. Creation of elements");
11233 for (int idomain = idom0; idomain < nbDomains; idomain++)
11235 itface = faceDomains.begin();
11236 for (; itface != faceDomains.end(); ++itface)
11238 std::map<int, int> domvol = itface->second;
11239 if (!domvol.count(idomain))
11241 DownIdType face = itface->first;
11242 //MESSAGE(" --- face " << face.cellId);
11243 std::set<int> oldNodes;
11245 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11246 int nbMultipleNodes = 0;
11247 std::set<int>::iterator itn = oldNodes.begin();
11248 for (; itn != oldNodes.end(); ++itn)
11251 if (mutipleNodes.count(oldId))
11254 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11256 //MESSAGE("multiple Nodes detected on a shared face");
11257 int downId = itface->first.cellId;
11258 unsigned char cellType = itface->first.cellType;
11259 // --- shared edge or shared face ?
11260 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11263 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11264 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11265 if (mutipleNodes.count(nodes[i]))
11266 if (!mutipleNodesToFace.count(nodes[i]))
11267 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11269 else // shared face (between two volumes)
11271 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11272 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11273 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11274 for (int ie =0; ie < nbEdges; ie++)
11277 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11278 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11280 vector<int> vn0 = mutipleNodes[nodes[0]];
11281 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11283 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11284 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11285 if ( vn0[i0] == vn1[i1] )
11286 doms.push_back( vn0[ i0 ]);
11287 if ( doms.size() > 2 )
11289 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11290 double *coords = grid->GetPoint(nodes[0]);
11291 gp_Pnt p0(coords[0], coords[1], coords[2]);
11292 coords = grid->GetPoint(nodes[nbNodes - 1]);
11293 gp_Pnt p1(coords[0], coords[1], coords[2]);
11295 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11296 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11297 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11298 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11299 for ( size_t id = 0; id < doms.size(); id++ )
11301 int idom = doms[id];
11302 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11303 for ( int ivol = 0; ivol < nbvol; ivol++ )
11305 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11306 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11307 if (domain.count(elem))
11309 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11310 domvol[idom] = svol;
11311 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11313 vtkIdType npts = 0;
11314 vtkIdType* pts = 0;
11315 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11316 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11319 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11320 angleDom[idom] = 0;
11324 gp_Pnt g(values[0], values[1], values[2]);
11325 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11326 //MESSAGE(" angle=" << angleDom[idom]);
11332 map<double, int> sortedDom; // sort domains by angle
11333 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11334 sortedDom[ia->second] = ia->first;
11335 vector<int> vnodes;
11337 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11339 vdom.push_back(ib->second);
11340 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11342 for (int ino = 0; ino < nbNodes; ino++)
11343 vnodes.push_back(nodes[ino]);
11344 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11353 // --- iterate on shared faces (volumes to modify, face to extrude)
11354 // get node id's of the face (id SMDS = id VTK)
11355 // create flat element with old and new nodes if requested
11357 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11358 // (domain1 X domain2) = domain1 + MAXINT*domain2
11360 std::map<int, std::map<long,int> > nodeQuadDomains;
11361 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11363 MESSAGE(".. Creation of elements: simple junction");
11364 if (createJointElems)
11367 string joints2DName = "joints2D";
11368 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11369 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11370 string joints3DName = "joints3D";
11371 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11372 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11374 itface = faceDomains.begin();
11375 for (; itface != faceDomains.end(); ++itface)
11377 DownIdType face = itface->first;
11378 std::set<int> oldNodes;
11379 std::set<int>::iterator itn;
11381 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11383 std::map<int, int> domvol = itface->second;
11384 std::map<int, int>::iterator itdom = domvol.begin();
11385 int dom1 = itdom->first;
11386 int vtkVolId = itdom->second;
11388 int dom2 = itdom->first;
11389 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11391 stringstream grpname;
11394 grpname << dom1 << "_" << dom2;
11396 grpname << dom2 << "_" << dom1;
11397 string namegrp = grpname.str();
11398 if (!mapOfJunctionGroups.count(namegrp))
11399 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11400 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11402 sgrp->Add(vol->GetID());
11403 if (vol->GetType() == SMDSAbs_Volume)
11404 joints3DGrp->Add(vol->GetID());
11405 else if (vol->GetType() == SMDSAbs_Face)
11406 joints2DGrp->Add(vol->GetID());
11410 // --- create volumes on multiple domain intersection if requested
11411 // iterate on mutipleNodesToFace
11412 // iterate on edgesMultiDomains
11414 MESSAGE(".. Creation of elements: multiple junction");
11415 if (createJointElems)
11417 // --- iterate on mutipleNodesToFace
11419 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11420 for (; itn != mutipleNodesToFace.end(); ++itn)
11422 int node = itn->first;
11423 vector<int> orderDom = itn->second;
11424 vector<vtkIdType> orderedNodes;
11425 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11426 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11427 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11429 stringstream grpname;
11431 grpname << 0 << "_" << 0;
11433 string namegrp = grpname.str();
11434 if (!mapOfJunctionGroups.count(namegrp))
11435 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11436 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11438 sgrp->Add(face->GetID());
11441 // --- iterate on edgesMultiDomains
11443 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11444 for (; ite != edgesMultiDomains.end(); ++ite)
11446 vector<int> nodes = ite->first;
11447 vector<int> orderDom = ite->second;
11448 vector<vtkIdType> orderedNodes;
11449 if (nodes.size() == 2)
11451 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11452 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11453 if ( orderDom.size() == 3 )
11454 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11455 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11457 for (int idom = orderDom.size()-1; idom >=0; idom--)
11458 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11459 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11462 string namegrp = "jointsMultiples";
11463 if (!mapOfJunctionGroups.count(namegrp))
11464 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11465 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11467 sgrp->Add(vol->GetID());
11471 //INFOS("Quadratic multiple joints not implemented");
11472 // TODO quadratic nodes
11477 // --- list the explicit faces and edges of the mesh that need to be modified,
11478 // i.e. faces and edges built with one or more duplicated nodes.
11479 // associate these faces or edges to their corresponding domain.
11480 // only the first domain found is kept when a face or edge is shared
11482 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11483 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11484 faceOrEdgeDom.clear();
11487 MESSAGE(".. Modification of elements");
11488 for (int idomain = idom0; idomain < nbDomains; idomain++)
11490 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11491 for (; itnod != nodeDomains.end(); ++itnod)
11493 int oldId = itnod->first;
11494 //MESSAGE(" node " << oldId);
11495 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11496 for (int i = 0; i < l.ncells; i++)
11498 int vtkId = l.cells[i];
11499 int vtkType = grid->GetCellType(vtkId);
11500 int downId = grid->CellIdToDownId(vtkId);
11502 continue; // new cells: not to be modified
11503 DownIdType aCell(downId, vtkType);
11504 int volParents[1000];
11505 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11506 for (int j = 0; j < nbvol; j++)
11507 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11508 if (!feDom.count(vtkId))
11510 feDom[vtkId] = idomain;
11511 faceOrEdgeDom[aCell] = emptyMap;
11512 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11513 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11514 // << " type " << vtkType << " downId " << downId);
11520 // --- iterate on shared faces (volumes to modify, face to extrude)
11521 // get node id's of the face
11522 // replace old nodes by new nodes in volumes, and update inverse connectivity
11524 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11525 for (int m=0; m<3; m++)
11527 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11528 itface = (*amap).begin();
11529 for (; itface != (*amap).end(); ++itface)
11531 DownIdType face = itface->first;
11532 std::set<int> oldNodes;
11533 std::set<int>::iterator itn;
11535 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11536 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11537 std::map<int, int> localClonedNodeIds;
11539 std::map<int, int> domvol = itface->second;
11540 std::map<int, int>::iterator itdom = domvol.begin();
11541 for (; itdom != domvol.end(); ++itdom)
11543 int idom = itdom->first;
11544 int vtkVolId = itdom->second;
11545 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11546 localClonedNodeIds.clear();
11547 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11550 if (nodeDomains[oldId].count(idom))
11552 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11553 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11556 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11561 // Remove empty groups (issue 0022812)
11562 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11563 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11565 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11566 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11569 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11570 grid->BuildLinks();
11578 * \brief Double nodes on some external faces and create flat elements.
11579 * Flat elements are mainly used by some types of mechanic calculations.
11581 * Each group of the list must be constituted of faces.
11582 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11583 * @param theElems - list of groups of faces, where a group of faces is a set of
11584 * SMDS_MeshElements sorted by Id.
11585 * @return TRUE if operation has been completed successfully, FALSE otherwise
11587 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11589 MESSAGE("-------------------------------------------------");
11590 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11591 MESSAGE("-------------------------------------------------");
11593 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11595 // --- For each group of faces
11596 // duplicate the nodes, create a flat element based on the face
11597 // replace the nodes of the faces by their clones
11599 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11600 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11601 clonedNodes.clear();
11602 intermediateNodes.clear();
11603 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11604 mapOfJunctionGroups.clear();
11606 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11608 const TIDSortedElemSet& domain = theElems[idom];
11609 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11610 for ( ; elemItr != domain.end(); ++elemItr )
11612 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11613 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11616 // MESSAGE("aFace=" << aFace->GetID());
11617 bool isQuad = aFace->IsQuadratic();
11618 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11620 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11622 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11623 while (nodeIt->more())
11625 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11626 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11628 ln2.push_back(node);
11630 ln0.push_back(node);
11632 const SMDS_MeshNode* clone = 0;
11633 if (!clonedNodes.count(node))
11635 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11636 copyPosition( node, clone );
11637 clonedNodes[node] = clone;
11640 clone = clonedNodes[node];
11643 ln3.push_back(clone);
11645 ln1.push_back(clone);
11647 const SMDS_MeshNode* inter = 0;
11648 if (isQuad && (!isMedium))
11650 if (!intermediateNodes.count(node))
11652 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11653 copyPosition( node, inter );
11654 intermediateNodes[node] = inter;
11657 inter = intermediateNodes[node];
11658 ln4.push_back(inter);
11662 // --- extrude the face
11664 vector<const SMDS_MeshNode*> ln;
11665 SMDS_MeshVolume* vol = 0;
11666 vtkIdType aType = aFace->GetVtkType();
11670 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11671 // MESSAGE("vol prism " << vol->GetID());
11672 ln.push_back(ln1[0]);
11673 ln.push_back(ln1[1]);
11674 ln.push_back(ln1[2]);
11677 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11678 // MESSAGE("vol hexa " << vol->GetID());
11679 ln.push_back(ln1[0]);
11680 ln.push_back(ln1[1]);
11681 ln.push_back(ln1[2]);
11682 ln.push_back(ln1[3]);
11684 case VTK_QUADRATIC_TRIANGLE:
11685 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11686 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11687 // MESSAGE("vol quad prism " << vol->GetID());
11688 ln.push_back(ln1[0]);
11689 ln.push_back(ln1[1]);
11690 ln.push_back(ln1[2]);
11691 ln.push_back(ln3[0]);
11692 ln.push_back(ln3[1]);
11693 ln.push_back(ln3[2]);
11695 case VTK_QUADRATIC_QUAD:
11696 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11697 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11698 // ln4[0], ln4[1], ln4[2], ln4[3]);
11699 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11700 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11701 ln4[0], ln4[1], ln4[2], ln4[3]);
11702 // MESSAGE("vol quad hexa " << vol->GetID());
11703 ln.push_back(ln1[0]);
11704 ln.push_back(ln1[1]);
11705 ln.push_back(ln1[2]);
11706 ln.push_back(ln1[3]);
11707 ln.push_back(ln3[0]);
11708 ln.push_back(ln3[1]);
11709 ln.push_back(ln3[2]);
11710 ln.push_back(ln3[3]);
11720 stringstream grpname;
11724 string namegrp = grpname.str();
11725 if (!mapOfJunctionGroups.count(namegrp))
11726 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11727 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11729 sgrp->Add(vol->GetID());
11732 // --- modify the face
11734 aFace->ChangeNodes(&ln[0], ln.size());
11741 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11742 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11743 * groups of faces to remove inside the object, (idem edges).
11744 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11746 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11747 const TopoDS_Shape& theShape,
11748 SMESH_NodeSearcher* theNodeSearcher,
11749 const char* groupName,
11750 std::vector<double>& nodesCoords,
11751 std::vector<std::vector<int> >& listOfListOfNodes)
11753 MESSAGE("--------------------------------");
11754 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11755 MESSAGE("--------------------------------");
11757 // --- zone of volumes to remove is given :
11758 // 1 either by a geom shape (one or more vertices) and a radius,
11759 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11760 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11761 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11762 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11763 // defined by it's name.
11765 SMESHDS_GroupBase* groupDS = 0;
11766 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11767 while ( groupIt->more() )
11770 SMESH_Group * group = groupIt->next();
11771 if ( !group ) continue;
11772 groupDS = group->GetGroupDS();
11773 if ( !groupDS || groupDS->IsEmpty() ) continue;
11774 std::string grpName = group->GetName();
11775 //MESSAGE("grpName=" << grpName);
11776 if (grpName == groupName)
11782 bool isNodeGroup = false;
11783 bool isNodeCoords = false;
11786 if (groupDS->GetType() != SMDSAbs_Node)
11788 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11791 if (nodesCoords.size() > 0)
11792 isNodeCoords = true; // a list o nodes given by their coordinates
11793 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11795 // --- define groups to build
11797 int idg; // --- group of SMDS volumes
11798 string grpvName = groupName;
11799 grpvName += "_vol";
11800 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11803 MESSAGE("group not created " << grpvName);
11806 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11808 int idgs; // --- group of SMDS faces on the skin
11809 string grpsName = groupName;
11810 grpsName += "_skin";
11811 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11814 MESSAGE("group not created " << grpsName);
11817 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11819 int idgi; // --- group of SMDS faces internal (several shapes)
11820 string grpiName = groupName;
11821 grpiName += "_internalFaces";
11822 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11825 MESSAGE("group not created " << grpiName);
11828 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11830 int idgei; // --- group of SMDS faces internal (several shapes)
11831 string grpeiName = groupName;
11832 grpeiName += "_internalEdges";
11833 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11836 MESSAGE("group not created " << grpeiName);
11839 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11841 // --- build downward connectivity
11843 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11844 meshDS->BuildDownWardConnectivity(true);
11845 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11847 // --- set of volumes detected inside
11849 std::set<int> setOfInsideVol;
11850 std::set<int> setOfVolToCheck;
11852 std::vector<gp_Pnt> gpnts;
11855 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11857 MESSAGE("group of nodes provided");
11858 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11859 while ( elemIt->more() )
11861 const SMDS_MeshElement* elem = elemIt->next();
11864 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11867 SMDS_MeshElement* vol = 0;
11868 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11869 while (volItr->more())
11871 vol = (SMDS_MeshElement*)volItr->next();
11872 setOfInsideVol.insert(vol->getVtkId());
11873 sgrp->Add(vol->GetID());
11877 else if (isNodeCoords)
11879 MESSAGE("list of nodes coordinates provided");
11882 while ( i < nodesCoords.size()-2 )
11884 double x = nodesCoords[i++];
11885 double y = nodesCoords[i++];
11886 double z = nodesCoords[i++];
11887 gp_Pnt p = gp_Pnt(x, y ,z);
11888 gpnts.push_back(p);
11889 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11893 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11895 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11896 TopTools_IndexedMapOfShape vertexMap;
11897 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11898 gp_Pnt p = gp_Pnt(0,0,0);
11899 if (vertexMap.Extent() < 1)
11902 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11904 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11905 p = BRep_Tool::Pnt(vertex);
11906 gpnts.push_back(p);
11907 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11911 if (gpnts.size() > 0)
11914 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11916 nodeId = startNode->GetID();
11917 MESSAGE("nodeId " << nodeId);
11919 double radius2 = radius*radius;
11920 MESSAGE("radius2 " << radius2);
11922 // --- volumes on start node
11924 setOfVolToCheck.clear();
11925 SMDS_MeshElement* startVol = 0;
11926 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11927 while (volItr->more())
11929 startVol = (SMDS_MeshElement*)volItr->next();
11930 setOfVolToCheck.insert(startVol->getVtkId());
11932 if (setOfVolToCheck.empty())
11934 MESSAGE("No volumes found");
11938 // --- starting with central volumes then their neighbors, check if they are inside
11939 // or outside the domain, until no more new neighbor volume is inside.
11940 // Fill the group of inside volumes
11942 std::map<int, double> mapOfNodeDistance2;
11943 mapOfNodeDistance2.clear();
11944 std::set<int> setOfOutsideVol;
11945 while (!setOfVolToCheck.empty())
11947 std::set<int>::iterator it = setOfVolToCheck.begin();
11949 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11950 bool volInside = false;
11951 vtkIdType npts = 0;
11952 vtkIdType* pts = 0;
11953 grid->GetCellPoints(vtkId, npts, pts);
11954 for (int i=0; i<npts; i++)
11956 double distance2 = 0;
11957 if (mapOfNodeDistance2.count(pts[i]))
11959 distance2 = mapOfNodeDistance2[pts[i]];
11960 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11964 double *coords = grid->GetPoint(pts[i]);
11965 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11967 for ( size_t j = 0; j < gpnts.size(); j++ )
11969 double d2 = aPoint.SquareDistance( gpnts[ j ]);
11970 if (d2 < distance2)
11973 if (distance2 < radius2)
11977 mapOfNodeDistance2[pts[i]] = distance2;
11978 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11980 if (distance2 < radius2)
11982 volInside = true; // one or more nodes inside the domain
11983 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11989 setOfInsideVol.insert(vtkId);
11990 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11991 int neighborsVtkIds[NBMAXNEIGHBORS];
11992 int downIds[NBMAXNEIGHBORS];
11993 unsigned char downTypes[NBMAXNEIGHBORS];
11994 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11995 for (int n = 0; n < nbNeighbors; n++)
11996 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11997 setOfVolToCheck.insert(neighborsVtkIds[n]);
12001 setOfOutsideVol.insert(vtkId);
12002 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12004 setOfVolToCheck.erase(vtkId);
12008 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12009 // If yes, add the volume to the inside set
12011 bool addedInside = true;
12012 std::set<int> setOfVolToReCheck;
12013 while (addedInside)
12015 MESSAGE(" --------------------------- re check");
12016 addedInside = false;
12017 std::set<int>::iterator itv = setOfInsideVol.begin();
12018 for (; itv != setOfInsideVol.end(); ++itv)
12021 int neighborsVtkIds[NBMAXNEIGHBORS];
12022 int downIds[NBMAXNEIGHBORS];
12023 unsigned char downTypes[NBMAXNEIGHBORS];
12024 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12025 for (int n = 0; n < nbNeighbors; n++)
12026 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12027 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12029 setOfVolToCheck = setOfVolToReCheck;
12030 setOfVolToReCheck.clear();
12031 while (!setOfVolToCheck.empty())
12033 std::set<int>::iterator it = setOfVolToCheck.begin();
12035 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12037 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12038 int countInside = 0;
12039 int neighborsVtkIds[NBMAXNEIGHBORS];
12040 int downIds[NBMAXNEIGHBORS];
12041 unsigned char downTypes[NBMAXNEIGHBORS];
12042 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12043 for (int n = 0; n < nbNeighbors; n++)
12044 if (setOfInsideVol.count(neighborsVtkIds[n]))
12046 MESSAGE("countInside " << countInside);
12047 if (countInside > 1)
12049 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12050 setOfInsideVol.insert(vtkId);
12051 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12052 addedInside = true;
12055 setOfVolToReCheck.insert(vtkId);
12057 setOfVolToCheck.erase(vtkId);
12061 // --- map of Downward faces at the boundary, inside the global volume
12062 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12063 // fill group of SMDS faces inside the volume (when several volume shapes)
12064 // fill group of SMDS faces on the skin of the global volume (if skin)
12066 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12067 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12068 std::set<int>::iterator it = setOfInsideVol.begin();
12069 for (; it != setOfInsideVol.end(); ++it)
12072 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12073 int neighborsVtkIds[NBMAXNEIGHBORS];
12074 int downIds[NBMAXNEIGHBORS];
12075 unsigned char downTypes[NBMAXNEIGHBORS];
12076 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12077 for (int n = 0; n < nbNeighbors; n++)
12079 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12080 if (neighborDim == 3)
12082 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12084 DownIdType face(downIds[n], downTypes[n]);
12085 boundaryFaces[face] = vtkId;
12087 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12088 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12089 if (vtkFaceId >= 0)
12091 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12092 // find also the smds edges on this face
12093 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12094 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12095 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12096 for (int i = 0; i < nbEdges; i++)
12098 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12099 if (vtkEdgeId >= 0)
12100 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12104 else if (neighborDim == 2) // skin of the volume
12106 DownIdType face(downIds[n], downTypes[n]);
12107 skinFaces[face] = vtkId;
12108 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12109 if (vtkFaceId >= 0)
12110 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12115 // --- identify the edges constituting the wire of each subshape on the skin
12116 // define polylines with the nodes of edges, equivalent to wires
12117 // project polylines on subshapes, and partition, to get geom faces
12119 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12120 std::set<int> emptySet;
12122 std::set<int> shapeIds;
12124 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12125 while (itelem->more())
12127 const SMDS_MeshElement *elem = itelem->next();
12128 int shapeId = elem->getshapeId();
12129 int vtkId = elem->getVtkId();
12130 if (!shapeIdToVtkIdSet.count(shapeId))
12132 shapeIdToVtkIdSet[shapeId] = emptySet;
12133 shapeIds.insert(shapeId);
12135 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12138 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12139 std::set<DownIdType, DownIdCompare> emptyEdges;
12140 emptyEdges.clear();
12142 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12143 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12145 int shapeId = itShape->first;
12146 MESSAGE(" --- Shape ID --- "<< shapeId);
12147 shapeIdToEdges[shapeId] = emptyEdges;
12149 std::vector<int> nodesEdges;
12151 std::set<int>::iterator its = itShape->second.begin();
12152 for (; its != itShape->second.end(); ++its)
12155 MESSAGE(" " << vtkId);
12156 int neighborsVtkIds[NBMAXNEIGHBORS];
12157 int downIds[NBMAXNEIGHBORS];
12158 unsigned char downTypes[NBMAXNEIGHBORS];
12159 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12160 for (int n = 0; n < nbNeighbors; n++)
12162 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12164 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12165 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12166 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12168 DownIdType edge(downIds[n], downTypes[n]);
12169 if (!shapeIdToEdges[shapeId].count(edge))
12171 shapeIdToEdges[shapeId].insert(edge);
12173 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12174 nodesEdges.push_back(vtkNodeId[0]);
12175 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12176 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12182 std::list<int> order;
12184 if (nodesEdges.size() > 0)
12186 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12187 nodesEdges[0] = -1;
12188 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12189 nodesEdges[1] = -1; // do not reuse this edge
12193 int nodeTofind = order.back(); // try first to push back
12195 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12196 if (nodesEdges[i] == nodeTofind)
12198 if ( i == (int) nodesEdges.size() )
12199 found = false; // no follower found on back
12202 if (i%2) // odd ==> use the previous one
12203 if (nodesEdges[i-1] < 0)
12207 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12208 nodesEdges[i-1] = -1;
12210 else // even ==> use the next one
12211 if (nodesEdges[i+1] < 0)
12215 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12216 nodesEdges[i+1] = -1;
12221 // try to push front
12223 nodeTofind = order.front(); // try to push front
12224 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12225 if ( nodesEdges[i] == nodeTofind )
12227 if ( i == (int)nodesEdges.size() )
12229 found = false; // no predecessor found on front
12232 if (i%2) // odd ==> use the previous one
12233 if (nodesEdges[i-1] < 0)
12237 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12238 nodesEdges[i-1] = -1;
12240 else // even ==> use the next one
12241 if (nodesEdges[i+1] < 0)
12245 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12246 nodesEdges[i+1] = -1;
12252 std::vector<int> nodes;
12253 nodes.push_back(shapeId);
12254 std::list<int>::iterator itl = order.begin();
12255 for (; itl != order.end(); itl++)
12257 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12258 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12260 listOfListOfNodes.push_back(nodes);
12263 // partition geom faces with blocFissure
12264 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12265 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12271 //================================================================================
12273 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12274 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12275 * \return TRUE if operation has been completed successfully, FALSE otherwise
12277 //================================================================================
12279 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12281 // iterates on volume elements and detect all free faces on them
12282 SMESHDS_Mesh* aMesh = GetMeshDS();
12286 ElemFeatures faceType( SMDSAbs_Face );
12287 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12288 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12291 const SMDS_MeshVolume* volume = vIt->next();
12292 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12293 vTool.SetExternalNormal();
12294 const int iQuad = volume->IsQuadratic();
12295 faceType.SetQuad( iQuad );
12296 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12298 if (!vTool.IsFreeFace(iface))
12301 vector<const SMDS_MeshNode *> nodes;
12302 int nbFaceNodes = vTool.NbFaceNodes(iface);
12303 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12305 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12306 nodes.push_back(faceNodes[inode]);
12308 if (iQuad) // add medium nodes
12310 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12311 nodes.push_back(faceNodes[inode]);
12312 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12313 nodes.push_back(faceNodes[8]);
12315 // add new face based on volume nodes
12316 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12318 nbExisted++; // face already exsist
12322 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12327 return ( nbFree == ( nbExisted + nbCreated ));
12332 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12334 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12336 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12339 //================================================================================
12341 * \brief Creates missing boundary elements
12342 * \param elements - elements whose boundary is to be checked
12343 * \param dimension - defines type of boundary elements to create
12344 * \param group - a group to store created boundary elements in
12345 * \param targetMesh - a mesh to store created boundary elements in
12346 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12347 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12348 * boundary elements will be copied into the targetMesh
12349 * \param toAddExistingBondary - if true, not only new but also pre-existing
12350 * boundary elements will be added into the new group
12351 * \param aroundElements - if true, elements will be created on boundary of given
12352 * elements else, on boundary of the whole mesh.
12353 * \return nb of added boundary elements
12355 //================================================================================
12357 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12358 Bnd_Dimension dimension,
12359 SMESH_Group* group/*=0*/,
12360 SMESH_Mesh* targetMesh/*=0*/,
12361 bool toCopyElements/*=false*/,
12362 bool toCopyExistingBoundary/*=false*/,
12363 bool toAddExistingBondary/*= false*/,
12364 bool aroundElements/*= false*/)
12366 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12367 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12368 // hope that all elements are of the same type, do not check them all
12369 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12370 throw SALOME_Exception(LOCALIZED("wrong element type"));
12373 toCopyElements = toCopyExistingBoundary = false;
12375 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12376 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12377 int nbAddedBnd = 0;
12379 // editor adding present bnd elements and optionally holding elements to add to the group
12380 SMESH_MeshEditor* presentEditor;
12381 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12382 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12384 SMESH_MesherHelper helper( *myMesh );
12385 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12386 SMDS_VolumeTool vTool;
12387 TIDSortedElemSet avoidSet;
12388 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12391 typedef vector<const SMDS_MeshNode*> TConnectivity;
12392 TConnectivity tgtNodes;
12393 ElemFeatures elemKind( missType ), elemToCopy;
12395 vector<const SMDS_MeshElement*> presentBndElems;
12396 vector<TConnectivity> missingBndElems;
12397 vector<int> freeFacets;
12398 TConnectivity nodes, elemNodes;
12400 SMDS_ElemIteratorPtr eIt;
12401 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12402 else eIt = elemSetIterator( elements );
12404 while (eIt->more())
12406 const SMDS_MeshElement* elem = eIt->next();
12407 const int iQuad = elem->IsQuadratic();
12408 elemKind.SetQuad( iQuad );
12410 // ------------------------------------------------------------------------------------
12411 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12412 // ------------------------------------------------------------------------------------
12413 presentBndElems.clear();
12414 missingBndElems.clear();
12415 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12416 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12418 const SMDS_MeshElement* otherVol = 0;
12419 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12421 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12422 ( !aroundElements || elements.count( otherVol )))
12424 freeFacets.push_back( iface );
12426 if ( missType == SMDSAbs_Face )
12427 vTool.SetExternalNormal();
12428 for ( size_t i = 0; i < freeFacets.size(); ++i )
12430 int iface = freeFacets[i];
12431 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12432 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12433 if ( missType == SMDSAbs_Edge ) // boundary edges
12435 nodes.resize( 2+iQuad );
12436 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12438 for ( size_t j = 0; j < nodes.size(); ++j )
12439 nodes[ j ] = nn[ i+j ];
12440 if ( const SMDS_MeshElement* edge =
12441 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12442 presentBndElems.push_back( edge );
12444 missingBndElems.push_back( nodes );
12447 else // boundary face
12450 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12451 nodes.push_back( nn[inode] ); // add corner nodes
12453 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12454 nodes.push_back( nn[inode] ); // add medium nodes
12455 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12457 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12459 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12460 SMDSAbs_Face, /*noMedium=*/false ))
12461 presentBndElems.push_back( f );
12463 missingBndElems.push_back( nodes );
12465 if ( targetMesh != myMesh )
12467 // add 1D elements on face boundary to be added to a new mesh
12468 const SMDS_MeshElement* edge;
12469 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12472 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12474 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12475 if ( edge && avoidSet.insert( edge ).second )
12476 presentBndElems.push_back( edge );
12482 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12484 avoidSet.clear(), avoidSet.insert( elem );
12485 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12486 SMDS_MeshElement::iterator() );
12487 elemNodes.push_back( elemNodes[0] );
12488 nodes.resize( 2 + iQuad );
12489 const int nbLinks = elem->NbCornerNodes();
12490 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12492 nodes[0] = elemNodes[iN];
12493 nodes[1] = elemNodes[iN+1+iQuad];
12494 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12495 continue; // not free link
12497 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12498 if ( const SMDS_MeshElement* edge =
12499 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12500 presentBndElems.push_back( edge );
12502 missingBndElems.push_back( nodes );
12506 // ---------------------------------
12507 // 2. Add missing boundary elements
12508 // ---------------------------------
12509 if ( targetMesh != myMesh )
12510 // instead of making a map of nodes in this mesh and targetMesh,
12511 // we create nodes with same IDs.
12512 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12514 TConnectivity& srcNodes = missingBndElems[i];
12515 tgtNodes.resize( srcNodes.size() );
12516 for ( inode = 0; inode < srcNodes.size(); ++inode )
12517 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12518 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12520 /*noMedium=*/false))
12522 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12526 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12528 TConnectivity& nodes = missingBndElems[ i ];
12529 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12531 /*noMedium=*/false))
12533 SMDS_MeshElement* newElem =
12534 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12535 nbAddedBnd += bool( newElem );
12537 // try to set a new element to a shape
12538 if ( myMesh->HasShapeToMesh() )
12541 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12542 const size_t nbN = nodes.size() / (iQuad+1 );
12543 for ( inode = 0; inode < nbN && ok; ++inode )
12545 pair<int, TopAbs_ShapeEnum> i_stype =
12546 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12547 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12548 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12550 if ( ok && mediumShapes.size() > 1 )
12552 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12553 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12554 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12556 if (( ok = ( stype_i->first != stype_i_0.first )))
12557 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12558 aMesh->IndexToShape( stype_i_0.second ));
12561 if ( ok && mediumShapes.begin()->first == missShapeType )
12562 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12566 // ----------------------------------
12567 // 3. Copy present boundary elements
12568 // ----------------------------------
12569 if ( toCopyExistingBoundary )
12570 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12572 const SMDS_MeshElement* e = presentBndElems[i];
12573 tgtNodes.resize( e->NbNodes() );
12574 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12575 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12576 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12578 else // store present elements to add them to a group
12579 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12581 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12584 } // loop on given elements
12586 // ---------------------------------------------
12587 // 4. Fill group with boundary elements
12588 // ---------------------------------------------
12591 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12592 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12593 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12595 tgtEditor.myLastCreatedElems.Clear();
12596 tgtEditor2.myLastCreatedElems.Clear();
12598 // -----------------------
12599 // 5. Copy given elements
12600 // -----------------------
12601 if ( toCopyElements && targetMesh != myMesh )
12603 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12604 else eIt = elemSetIterator( elements );
12605 while (eIt->more())
12607 const SMDS_MeshElement* elem = eIt->next();
12608 tgtNodes.resize( elem->NbNodes() );
12609 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12610 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12611 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12613 tgtEditor.myLastCreatedElems.Clear();
12619 //================================================================================
12621 * \brief Copy node position and set \a to node on the same geometry
12623 //================================================================================
12625 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12626 const SMDS_MeshNode* to )
12628 if ( !from || !to ) return;
12630 SMDS_PositionPtr pos = from->GetPosition();
12631 if ( !pos || from->getshapeId() < 1 ) return;
12633 switch ( pos->GetTypeOfPosition() )
12635 case SMDS_TOP_3DSPACE: break;
12637 case SMDS_TOP_FACE:
12639 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12640 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12641 fPos->GetUParameter(), fPos->GetVParameter() );
12644 case SMDS_TOP_EDGE:
12646 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12647 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12648 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12651 case SMDS_TOP_VERTEX:
12653 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12656 case SMDS_TOP_UNSPEC: