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.
478 * \param elements - Elements on whose nodes to create 0D elements; if empty,
479 * the all mesh is treated
480 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481 * \param duplicateElements - to add one more 0D element to a node or not
483 //================================================================================
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486 TIDSortedElemSet& all0DElems,
487 const bool duplicateElements )
489 SMDS_ElemIteratorPtr elemIt;
490 if ( elements.empty() )
492 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
496 elemIt = elemSetIterator( elements );
499 while ( elemIt->more() )
501 const SMDS_MeshElement* e = elemIt->next();
502 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503 while ( nodeIt->more() )
505 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507 if ( duplicateElements || !it0D->more() )
509 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
510 all0DElems.insert( myLastCreatedElems.Last() );
512 while ( it0D->more() )
513 all0DElems.insert( it0D->next() );
518 //=======================================================================
519 //function : FindShape
520 //purpose : Return an index of the shape theElem is on
521 // or zero if a shape not found
522 //=======================================================================
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
526 myLastCreatedElems.Clear();
527 myLastCreatedNodes.Clear();
529 SMESHDS_Mesh * aMesh = GetMeshDS();
530 if ( aMesh->ShapeToMesh().IsNull() )
533 int aShapeID = theElem->getshapeId();
537 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
538 if ( sm->Contains( theElem ))
541 if ( theElem->GetType() == SMDSAbs_Node ) {
542 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
545 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
548 TopoDS_Shape aShape; // the shape a node of theElem is on
549 if ( theElem->GetType() != SMDSAbs_Node )
551 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
552 while ( nodeIt->more() ) {
553 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
554 if ((aShapeID = node->getshapeId()) > 0) {
555 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
556 if ( sm->Contains( theElem ))
558 if ( aShape.IsNull() )
559 aShape = aMesh->IndexToShape( aShapeID );
565 // None of nodes is on a proper shape,
566 // find the shape among ancestors of aShape on which a node is
567 if ( !aShape.IsNull() ) {
568 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
569 for ( ; ancIt.More(); ancIt.Next() ) {
570 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
571 if ( sm && sm->Contains( theElem ))
572 return aMesh->ShapeToIndex( ancIt.Value() );
577 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
578 while ( const SMESHDS_SubMesh* sm = smIt->next() )
579 if ( sm->Contains( theElem ))
586 //=======================================================================
587 //function : IsMedium
589 //=======================================================================
591 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
592 const SMDSAbs_ElementType typeToCheck)
594 bool isMedium = false;
595 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
596 while (it->more() && !isMedium ) {
597 const SMDS_MeshElement* elem = it->next();
598 isMedium = elem->IsMediumNode(node);
603 //=======================================================================
604 //function : shiftNodesQuadTria
605 //purpose : Shift nodes in the array corresponded to quadratic triangle
606 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
607 //=======================================================================
609 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
611 const SMDS_MeshNode* nd1 = aNodes[0];
612 aNodes[0] = aNodes[1];
613 aNodes[1] = aNodes[2];
615 const SMDS_MeshNode* nd2 = aNodes[3];
616 aNodes[3] = aNodes[4];
617 aNodes[4] = aNodes[5];
621 //=======================================================================
622 //function : nbEdgeConnectivity
623 //purpose : return number of the edges connected with the theNode.
624 // if theEdges has connections with the other type of the
625 // elements, return -1
626 //=======================================================================
628 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
630 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
632 // while(elemIt->more()) {
637 return theNode->NbInverseElements();
640 //=======================================================================
641 //function : getNodesFromTwoTria
643 //=======================================================================
645 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
646 const SMDS_MeshElement * theTria2,
647 vector< const SMDS_MeshNode*>& N1,
648 vector< const SMDS_MeshNode*>& N2)
650 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
651 if ( N1.size() < 6 ) return false;
652 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
653 if ( N2.size() < 6 ) return false;
655 int sames[3] = {-1,-1,-1};
667 if(nbsames!=2) return false;
669 shiftNodesQuadTria(N1);
671 shiftNodesQuadTria(N1);
674 i = sames[0] + sames[1] + sames[2];
676 shiftNodesQuadTria(N2);
678 // now we receive following N1 and N2 (using numeration as in the image below)
679 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
680 // i.e. first nodes from both arrays form a new diagonal
684 //=======================================================================
685 //function : InverseDiag
686 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
687 // but having other common link.
688 // Return False if args are improper
689 //=======================================================================
691 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
692 const SMDS_MeshElement * theTria2 )
694 myLastCreatedElems.Clear();
695 myLastCreatedNodes.Clear();
697 if (!theTria1 || !theTria2)
700 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
701 if (!F1) return false;
702 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
703 if (!F2) return false;
704 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
705 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
707 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
708 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
712 // put nodes in array and find out indices of the same ones
713 const SMDS_MeshNode* aNodes [6];
714 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
716 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
717 while ( it->more() ) {
718 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
720 if ( i > 2 ) // theTria2
721 // find same node of theTria1
722 for ( int j = 0; j < 3; j++ )
723 if ( aNodes[ i ] == aNodes[ j ]) {
732 return false; // theTria1 is not a triangle
733 it = theTria2->nodesIterator();
735 if ( i == 6 && it->more() )
736 return false; // theTria2 is not a triangle
739 // find indices of 1,2 and of A,B in theTria1
740 int iA = -1, iB = 0, i1 = 0, i2 = 0;
741 for ( i = 0; i < 6; i++ ) {
742 if ( sameInd [ i ] == -1 ) {
747 if ( iA >= 0) iB = i;
751 // nodes 1 and 2 should not be the same
752 if ( aNodes[ i1 ] == aNodes[ i2 ] )
756 aNodes[ iA ] = aNodes[ i2 ];
758 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
760 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
761 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
765 } // end if(F1 && F2)
767 // check case of quadratic faces
768 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
769 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
771 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
772 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
776 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
777 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
785 vector< const SMDS_MeshNode* > N1;
786 vector< const SMDS_MeshNode* > N2;
787 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
789 // now we receive following N1 and N2 (using numeration as above image)
790 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
791 // i.e. first nodes from both arrays determ new diagonal
793 vector< const SMDS_MeshNode*> N1new( N1.size() );
794 vector< const SMDS_MeshNode*> N2new( N2.size() );
795 N1new.back() = N1.back(); // central node of biquadratic
796 N2new.back() = N2.back();
797 N1new[0] = N1[0]; N2new[0] = N1[0];
798 N1new[1] = N2[0]; N2new[1] = N1[1];
799 N1new[2] = N2[1]; N2new[2] = N2[0];
800 N1new[3] = N1[4]; N2new[3] = N1[3];
801 N1new[4] = N2[3]; N2new[4] = N2[5];
802 N1new[5] = N1[5]; N2new[5] = N1[4];
803 // change nodes in faces
804 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
805 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
807 // move the central node of biquadratic triangle
808 SMESH_MesherHelper helper( *GetMesh() );
809 for ( int is2nd = 0; is2nd < 2; ++is2nd )
811 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
812 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
813 if ( nodes.size() < 7 )
815 helper.SetSubShape( tria->getshapeId() );
816 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
820 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
821 SMESH_TNodeXYZ( nodes[4] ) +
822 SMESH_TNodeXYZ( nodes[5] )) / 3.;
827 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
828 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
829 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
831 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
832 xyz = S->Value( uv.X(), uv.Y() );
833 xyz.Transform( loc );
834 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
835 nodes[6]->getshapeId() > 0 )
836 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
838 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
843 //=======================================================================
844 //function : findTriangles
845 //purpose : find triangles sharing theNode1-theNode2 link
846 //=======================================================================
848 static bool findTriangles(const SMDS_MeshNode * theNode1,
849 const SMDS_MeshNode * theNode2,
850 const SMDS_MeshElement*& theTria1,
851 const SMDS_MeshElement*& theTria2)
853 if ( !theNode1 || !theNode2 ) return false;
855 theTria1 = theTria2 = 0;
857 set< const SMDS_MeshElement* > emap;
858 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
860 const SMDS_MeshElement* elem = it->next();
861 if ( elem->NbCornerNodes() == 3 )
864 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
866 const SMDS_MeshElement* elem = it->next();
867 if ( emap.count( elem )) {
875 // theTria1 must be element with minimum ID
876 if ( theTria2->GetID() < theTria1->GetID() )
877 std::swap( theTria2, theTria1 );
885 //=======================================================================
886 //function : InverseDiag
887 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
888 // with ones built on the same 4 nodes but having other common link.
889 // Return false if proper faces not found
890 //=======================================================================
892 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
893 const SMDS_MeshNode * theNode2)
895 myLastCreatedElems.Clear();
896 myLastCreatedNodes.Clear();
898 const SMDS_MeshElement *tr1, *tr2;
899 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
902 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
903 if (!F1) return false;
904 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
905 if (!F2) return false;
906 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
907 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
909 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
910 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
914 // put nodes in array
915 // and find indices of 1,2 and of A in tr1 and of B in tr2
916 int i, iA1 = 0, i1 = 0;
917 const SMDS_MeshNode* aNodes1 [3];
918 SMDS_ElemIteratorPtr it;
919 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
920 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
921 if ( aNodes1[ i ] == theNode1 )
922 iA1 = i; // node A in tr1
923 else if ( aNodes1[ i ] != theNode2 )
927 const SMDS_MeshNode* aNodes2 [3];
928 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
929 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
930 if ( aNodes2[ i ] == theNode2 )
931 iB2 = i; // node B in tr2
932 else if ( aNodes2[ i ] != theNode1 )
936 // nodes 1 and 2 should not be the same
937 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
941 aNodes1[ iA1 ] = aNodes2[ i2 ];
943 aNodes2[ iB2 ] = aNodes1[ i1 ];
945 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
946 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
951 // check case of quadratic faces
952 return InverseDiag(tr1,tr2);
955 //=======================================================================
956 //function : getQuadrangleNodes
957 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
958 // fusion of triangles tr1 and tr2 having shared link on
959 // theNode1 and theNode2
960 //=======================================================================
962 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
963 const SMDS_MeshNode * theNode1,
964 const SMDS_MeshNode * theNode2,
965 const SMDS_MeshElement * tr1,
966 const SMDS_MeshElement * tr2 )
968 if( tr1->NbNodes() != tr2->NbNodes() )
970 // find the 4-th node to insert into tr1
971 const SMDS_MeshNode* n4 = 0;
972 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
974 while ( !n4 && i<3 ) {
975 const SMDS_MeshNode * n = cast2Node( it->next() );
977 bool isDiag = ( n == theNode1 || n == theNode2 );
981 // Make an array of nodes to be in a quadrangle
982 int iNode = 0, iFirstDiag = -1;
983 it = tr1->nodesIterator();
986 const SMDS_MeshNode * n = cast2Node( it->next() );
988 bool isDiag = ( n == theNode1 || n == theNode2 );
990 if ( iFirstDiag < 0 )
992 else if ( iNode - iFirstDiag == 1 )
993 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
995 else if ( n == n4 ) {
996 return false; // tr1 and tr2 should not have all the same nodes
998 theQuadNodes[ iNode++ ] = n;
1000 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1001 theQuadNodes[ iNode ] = n4;
1006 //=======================================================================
1007 //function : DeleteDiag
1008 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1009 // with a quadrangle built on the same 4 nodes.
1010 // Return false if proper faces not found
1011 //=======================================================================
1013 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1014 const SMDS_MeshNode * theNode2)
1016 myLastCreatedElems.Clear();
1017 myLastCreatedNodes.Clear();
1019 const SMDS_MeshElement *tr1, *tr2;
1020 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1023 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1024 if (!F1) return false;
1025 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1026 if (!F2) return false;
1027 SMESHDS_Mesh * aMesh = GetMeshDS();
1029 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1030 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1032 const SMDS_MeshNode* aNodes [ 4 ];
1033 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1036 const SMDS_MeshElement* newElem = 0;
1037 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1038 myLastCreatedElems.Append(newElem);
1039 AddToSameGroups( newElem, tr1, aMesh );
1040 int aShapeId = tr1->getshapeId();
1043 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045 aMesh->RemoveElement( tr1 );
1046 aMesh->RemoveElement( tr2 );
1051 // check case of quadratic faces
1052 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1054 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1058 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1059 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1067 vector< const SMDS_MeshNode* > N1;
1068 vector< const SMDS_MeshNode* > N2;
1069 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1071 // now we receive following N1 and N2 (using numeration as above image)
1072 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1073 // i.e. first nodes from both arrays determ new diagonal
1075 const SMDS_MeshNode* aNodes[8];
1085 const SMDS_MeshElement* newElem = 0;
1086 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1087 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1088 myLastCreatedElems.Append(newElem);
1089 AddToSameGroups( newElem, tr1, aMesh );
1090 int aShapeId = tr1->getshapeId();
1093 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1095 aMesh->RemoveElement( tr1 );
1096 aMesh->RemoveElement( tr2 );
1098 // remove middle node (9)
1099 GetMeshDS()->RemoveNode( N1[4] );
1104 //=======================================================================
1105 //function : Reorient
1106 //purpose : Reverse theElement orientation
1107 //=======================================================================
1109 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1111 myLastCreatedElems.Clear();
1112 myLastCreatedNodes.Clear();
1116 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1117 if ( !it || !it->more() )
1120 const SMDSAbs_ElementType type = theElem->GetType();
1121 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1124 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1125 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1127 const SMDS_VtkVolume* aPolyedre =
1128 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1130 MESSAGE("Warning: bad volumic element");
1133 const int nbFaces = aPolyedre->NbFaces();
1134 vector<const SMDS_MeshNode *> poly_nodes;
1135 vector<int> quantities (nbFaces);
1137 // reverse each face of the polyedre
1138 for (int iface = 1; iface <= nbFaces; iface++) {
1139 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1140 quantities[iface - 1] = nbFaceNodes;
1142 for (inode = nbFaceNodes; inode >= 1; inode--) {
1143 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1144 poly_nodes.push_back(curNode);
1147 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1149 else // other elements
1151 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1152 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1153 if ( interlace.empty() )
1155 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1159 SMDS_MeshCell::applyInterlace( interlace, nodes );
1161 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1166 //================================================================================
1168 * \brief Reorient faces.
1169 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1170 * \param theDirection - desired direction of normal of \a theFace
1171 * \param theFace - one of \a theFaces that should be oriented according to
1172 * \a theDirection and whose orientation defines orientation of other faces
1173 * \return number of reoriented faces.
1175 //================================================================================
1177 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1178 const gp_Dir& theDirection,
1179 const SMDS_MeshElement * theFace)
1182 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1184 if ( theFaces.empty() )
1186 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1187 while ( fIt->more() )
1188 theFaces.insert( theFaces.end(), fIt->next() );
1191 // orient theFace according to theDirection
1193 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1194 if ( normal * theDirection.XYZ() < 0 )
1195 nbReori += Reorient( theFace );
1197 // Orient other faces
1199 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1200 TIDSortedElemSet avoidSet;
1201 set< SMESH_TLink > checkedLinks;
1202 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1204 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1205 theFaces.erase( theFace );
1206 startFaces.insert( theFace );
1208 int nodeInd1, nodeInd2;
1209 const SMDS_MeshElement* otherFace;
1210 vector< const SMDS_MeshElement* > facesNearLink;
1211 vector< std::pair< int, int > > nodeIndsOfFace;
1213 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1214 while ( !startFaces.empty() )
1216 startFace = startFaces.begin();
1217 theFace = *startFace;
1218 startFaces.erase( startFace );
1219 if ( !visitedFaces.insert( theFace ).second )
1223 avoidSet.insert(theFace);
1225 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1227 const int nbNodes = theFace->NbCornerNodes();
1228 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1230 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1231 linkIt_isNew = checkedLinks.insert( link );
1232 if ( !linkIt_isNew.second )
1234 // link has already been checked and won't be encountered more
1235 // if the group (theFaces) is manifold
1236 //checkedLinks.erase( linkIt_isNew.first );
1240 facesNearLink.clear();
1241 nodeIndsOfFace.clear();
1242 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1244 &nodeInd1, &nodeInd2 )))
1245 if ( otherFace != theFace)
1247 facesNearLink.push_back( otherFace );
1248 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1249 avoidSet.insert( otherFace );
1251 if ( facesNearLink.size() > 1 )
1253 // NON-MANIFOLD mesh shell !
1254 // select a face most co-directed with theFace,
1255 // other faces won't be visited this time
1257 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1258 double proj, maxProj = -1;
1259 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1260 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1261 if (( proj = Abs( NF * NOF )) > maxProj ) {
1263 otherFace = facesNearLink[i];
1264 nodeInd1 = nodeIndsOfFace[i].first;
1265 nodeInd2 = nodeIndsOfFace[i].second;
1268 // not to visit rejected faces
1269 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1270 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1271 visitedFaces.insert( facesNearLink[i] );
1273 else if ( facesNearLink.size() == 1 )
1275 otherFace = facesNearLink[0];
1276 nodeInd1 = nodeIndsOfFace.back().first;
1277 nodeInd2 = nodeIndsOfFace.back().second;
1279 if ( otherFace && otherFace != theFace)
1281 // link must be reverse in otherFace if orientation ot otherFace
1282 // is same as that of theFace
1283 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1285 nbReori += Reorient( otherFace );
1287 startFaces.insert( otherFace );
1290 std::swap( link.first, link.second ); // reverse the link
1296 //================================================================================
1298 * \brief Reorient faces basing on orientation of adjacent volumes.
1299 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1300 * \param theVolumes - reference volumes.
1301 * \param theOutsideNormal - to orient faces to have their normal
1302 * pointing either \a outside or \a inside the adjacent volumes.
1303 * \return number of reoriented faces.
1305 //================================================================================
1307 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1308 TIDSortedElemSet & theVolumes,
1309 const bool theOutsideNormal)
1313 SMDS_ElemIteratorPtr faceIt;
1314 if ( theFaces.empty() )
1315 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1317 faceIt = elemSetIterator( theFaces );
1319 vector< const SMDS_MeshNode* > faceNodes;
1320 TIDSortedElemSet checkedVolumes;
1321 set< const SMDS_MeshNode* > faceNodesSet;
1322 SMDS_VolumeTool volumeTool;
1324 while ( faceIt->more() ) // loop on given faces
1326 const SMDS_MeshElement* face = faceIt->next();
1327 if ( face->GetType() != SMDSAbs_Face )
1330 const size_t nbCornersNodes = face->NbCornerNodes();
1331 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1333 checkedVolumes.clear();
1334 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1335 while ( vIt->more() )
1337 const SMDS_MeshElement* volume = vIt->next();
1339 if ( !checkedVolumes.insert( volume ).second )
1341 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1344 // is volume adjacent?
1345 bool allNodesCommon = true;
1346 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1347 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1348 if ( !allNodesCommon )
1351 // get nodes of a corresponding volume facet
1352 faceNodesSet.clear();
1353 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1354 volumeTool.Set( volume );
1355 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1356 if ( facetID < 0 ) continue;
1357 volumeTool.SetExternalNormal();
1358 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1360 // compare order of faceNodes and facetNodes
1361 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1363 for ( int i = 0; i < 2; ++i )
1365 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1366 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1367 if ( faceNodes[ iN ] == n )
1373 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1374 if ( isOutside != theOutsideNormal )
1375 nbReori += Reorient( face );
1377 } // loop on given faces
1382 //=======================================================================
1383 //function : getBadRate
1385 //=======================================================================
1387 static double getBadRate (const SMDS_MeshElement* theElem,
1388 SMESH::Controls::NumericalFunctorPtr& theCrit)
1390 SMESH::Controls::TSequenceOfXYZ P;
1391 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1393 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1394 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1397 //=======================================================================
1398 //function : QuadToTri
1399 //purpose : Cut quadrangles into triangles.
1400 // theCrit is used to select a diagonal to cut
1401 //=======================================================================
1403 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1404 SMESH::Controls::NumericalFunctorPtr theCrit)
1406 myLastCreatedElems.Clear();
1407 myLastCreatedNodes.Clear();
1409 if ( !theCrit.get() )
1412 SMESHDS_Mesh * aMesh = GetMeshDS();
1414 Handle(Geom_Surface) surface;
1415 SMESH_MesherHelper helper( *GetMesh() );
1417 TIDSortedElemSet::iterator itElem;
1418 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1420 const SMDS_MeshElement* elem = *itElem;
1421 if ( !elem || elem->GetType() != SMDSAbs_Face )
1423 if ( elem->NbCornerNodes() != 4 )
1426 // retrieve element nodes
1427 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1429 // compare two sets of possible triangles
1430 double aBadRate1, aBadRate2; // to what extent a set is bad
1431 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1432 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1433 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1435 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1436 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1437 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1439 const int aShapeId = FindShape( elem );
1440 const SMDS_MeshElement* newElem1 = 0;
1441 const SMDS_MeshElement* newElem2 = 0;
1443 if ( !elem->IsQuadratic() ) // split liner quadrangle
1445 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447 if ( aBadRate1 <= aBadRate2 ) {
1448 // tr1 + tr2 is better
1449 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1450 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1453 // tr3 + tr4 is better
1454 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1455 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1458 else // split quadratic quadrangle
1460 helper.SetIsQuadratic( true );
1461 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1463 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1464 if ( aNodes.size() == 9 )
1466 helper.SetIsBiQuadratic( true );
1467 if ( aBadRate1 <= aBadRate2 )
1468 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1470 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1472 // create a new element
1473 if ( aBadRate1 <= aBadRate2 ) {
1474 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1475 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1478 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1479 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1483 // care of a new element
1485 myLastCreatedElems.Append(newElem1);
1486 myLastCreatedElems.Append(newElem2);
1487 AddToSameGroups( newElem1, elem, aMesh );
1488 AddToSameGroups( newElem2, elem, aMesh );
1490 // put a new triangle on the same shape
1492 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1493 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1495 aMesh->RemoveElement( elem );
1500 //=======================================================================
1502 * \brief Split each of given quadrangles into 4 triangles.
1503 * \param theElems - The faces to be splitted. If empty all faces are split.
1505 //=======================================================================
1507 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1509 myLastCreatedElems.Clear();
1510 myLastCreatedNodes.Clear();
1512 SMESH_MesherHelper helper( *GetMesh() );
1513 helper.SetElementsOnShape( true );
1515 SMDS_ElemIteratorPtr faceIt;
1516 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1517 else faceIt = elemSetIterator( theElems );
1520 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1522 vector< const SMDS_MeshNode* > nodes;
1523 SMESHDS_SubMesh* subMeshDS = 0;
1525 Handle(Geom_Surface) surface;
1526 TopLoc_Location loc;
1528 while ( faceIt->more() )
1530 const SMDS_MeshElement* quad = faceIt->next();
1531 if ( !quad || quad->NbCornerNodes() != 4 )
1534 // get a surface the quad is on
1536 if ( quad->getshapeId() < 1 )
1539 helper.SetSubShape( 0 );
1542 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1544 helper.SetSubShape( quad->getshapeId() );
1545 if ( !helper.GetSubShape().IsNull() &&
1546 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1548 F = TopoDS::Face( helper.GetSubShape() );
1549 surface = BRep_Tool::Surface( F, loc );
1550 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1554 helper.SetSubShape( 0 );
1559 // create a central node
1561 const SMDS_MeshNode* nCentral;
1562 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1564 if ( nodes.size() == 9 )
1566 nCentral = nodes.back();
1573 for ( ; iN < nodes.size(); ++iN )
1574 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1576 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1577 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1579 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1580 xyz[0], xyz[1], xyz[2], xyz[3],
1581 xyz[4], xyz[5], xyz[6], xyz[7] );
1585 for ( ; iN < nodes.size(); ++iN )
1586 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1588 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1589 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1591 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1592 uv[0], uv[1], uv[2], uv[3],
1593 uv[4], uv[5], uv[6], uv[7] );
1595 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1599 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1600 uv[8].X(), uv[8].Y() );
1601 myLastCreatedNodes.Append( nCentral );
1604 // create 4 triangles
1606 helper.SetIsQuadratic ( nodes.size() > 4 );
1607 helper.SetIsBiQuadratic( nodes.size() == 9 );
1608 if ( helper.GetIsQuadratic() )
1609 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1611 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1613 for ( int i = 0; i < 4; ++i )
1615 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1618 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1619 myLastCreatedElems.Append( tria );
1624 //=======================================================================
1625 //function : BestSplit
1626 //purpose : Find better diagonal for cutting.
1627 //=======================================================================
1629 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1630 SMESH::Controls::NumericalFunctorPtr theCrit)
1632 myLastCreatedElems.Clear();
1633 myLastCreatedNodes.Clear();
1638 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1641 if( theQuad->NbNodes()==4 ||
1642 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1644 // retrieve element nodes
1645 const SMDS_MeshNode* aNodes [4];
1646 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1648 //while (itN->more())
1650 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1652 // compare two sets of possible triangles
1653 double aBadRate1, aBadRate2; // to what extent a set is bad
1654 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1655 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1656 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1658 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1659 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1660 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1661 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1662 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1663 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1664 return 1; // diagonal 1-3
1666 return 2; // diagonal 2-4
1673 // Methods of splitting volumes into tetra
1675 const int theHexTo5_1[5*4+1] =
1677 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1679 const int theHexTo5_2[5*4+1] =
1681 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1683 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1685 const int theHexTo6_1[6*4+1] =
1687 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
1689 const int theHexTo6_2[6*4+1] =
1691 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
1693 const int theHexTo6_3[6*4+1] =
1695 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
1697 const int theHexTo6_4[6*4+1] =
1699 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
1701 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1703 const int thePyraTo2_1[2*4+1] =
1705 0, 1, 2, 4, 0, 2, 3, 4, -1
1707 const int thePyraTo2_2[2*4+1] =
1709 1, 2, 3, 4, 1, 3, 0, 4, -1
1711 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1713 const int thePentaTo3_1[3*4+1] =
1715 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1717 const int thePentaTo3_2[3*4+1] =
1719 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1721 const int thePentaTo3_3[3*4+1] =
1723 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1725 const int thePentaTo3_4[3*4+1] =
1727 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1729 const int thePentaTo3_5[3*4+1] =
1731 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1733 const int thePentaTo3_6[3*4+1] =
1735 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1737 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1738 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1740 // Methods of splitting hexahedron into prisms
1742 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1744 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
1746 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1748 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
1750 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1752 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
1755 const int theHexTo2Prisms_BT_1[6*2+1] =
1757 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1759 const int theHexTo2Prisms_BT_2[6*2+1] =
1761 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1763 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1765 const int theHexTo2Prisms_LR_1[6*2+1] =
1767 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1769 const int theHexTo2Prisms_LR_2[6*2+1] =
1771 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1773 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1775 const int theHexTo2Prisms_FB_1[6*2+1] =
1777 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1779 const int theHexTo2Prisms_FB_2[6*2+1] =
1781 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1783 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1786 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1789 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1790 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1791 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1792 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1798 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1799 bool _baryNode; //!< additional node is to be created at cell barycenter
1800 bool _ownConn; //!< to delete _connectivity in destructor
1801 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1803 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1804 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1805 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1806 bool hasFacet( const TTriangleFacet& facet ) const
1808 if ( _nbCorners == 4 )
1810 const int* tetConn = _connectivity;
1811 for ( ; tetConn[0] >= 0; tetConn += 4 )
1812 if (( facet.contains( tetConn[0] ) +
1813 facet.contains( tetConn[1] ) +
1814 facet.contains( tetConn[2] ) +
1815 facet.contains( tetConn[3] )) == 3 )
1818 else // prism, _nbCorners == 6
1820 const int* prismConn = _connectivity;
1821 for ( ; prismConn[0] >= 0; prismConn += 6 )
1823 if (( facet.contains( prismConn[0] ) &&
1824 facet.contains( prismConn[1] ) &&
1825 facet.contains( prismConn[2] ))
1827 ( facet.contains( prismConn[3] ) &&
1828 facet.contains( prismConn[4] ) &&
1829 facet.contains( prismConn[5] )))
1837 //=======================================================================
1839 * \brief return TSplitMethod for the given element to split into tetrahedra
1841 //=======================================================================
1843 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1845 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1847 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1848 // an edge and a face barycenter; tertaherdons are based on triangles and
1849 // a volume barycenter
1850 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1852 // Find out how adjacent volumes are split
1854 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1855 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1856 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1858 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1859 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1860 if ( nbNodes < 4 ) continue;
1862 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1863 const int* nInd = vol.GetFaceNodesIndices( iF );
1866 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1867 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1868 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1869 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1873 int iCom = 0; // common node of triangle faces to split into
1874 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1876 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1877 nInd[ iQ * ( (iCom+1)%nbNodes )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1879 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1880 nInd[ iQ * ( (iCom+2)%nbNodes )],
1881 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1882 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1884 triaSplits.push_back( t012 );
1885 triaSplits.push_back( t023 );
1890 if ( !triaSplits.empty() )
1891 hasAdjacentSplits = true;
1894 // Among variants of split method select one compliant with adjacent volumes
1896 TSplitMethod method;
1897 if ( !vol.Element()->IsPoly() && !is24TetMode )
1899 int nbVariants = 2, nbTet = 0;
1900 const int** connVariants = 0;
1901 switch ( vol.Element()->GetEntityType() )
1903 case SMDSEntity_Hexa:
1904 case SMDSEntity_Quad_Hexa:
1905 case SMDSEntity_TriQuad_Hexa:
1906 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1907 connVariants = theHexTo5, nbTet = 5;
1909 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1911 case SMDSEntity_Pyramid:
1912 case SMDSEntity_Quad_Pyramid:
1913 connVariants = thePyraTo2; nbTet = 2;
1915 case SMDSEntity_Penta:
1916 case SMDSEntity_Quad_Penta:
1917 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1922 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1924 // check method compliancy with adjacent tetras,
1925 // all found splits must be among facets of tetras described by this method
1926 method = TSplitMethod( nbTet, connVariants[variant] );
1927 if ( hasAdjacentSplits && method._nbSplits > 0 )
1929 bool facetCreated = true;
1930 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1932 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1933 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1934 facetCreated = method.hasFacet( *facet );
1936 if ( !facetCreated )
1937 method = TSplitMethod(0); // incompatible method
1941 if ( method._nbSplits < 1 )
1943 // No standard method is applicable, use a generic solution:
1944 // each facet of a volume is split into triangles and
1945 // each of triangles and a volume barycenter form a tetrahedron.
1947 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1949 int* connectivity = new int[ maxTetConnSize + 1 ];
1950 method._connectivity = connectivity;
1951 method._ownConn = true;
1952 method._baryNode = !isHex27; // to create central node or not
1955 int baryCenInd = vol.NbNodes() - int( isHex27 );
1956 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1958 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1959 const int* nInd = vol.GetFaceNodesIndices( iF );
1960 // find common node of triangle facets of tetra to create
1961 int iCommon = 0; // index in linear numeration
1962 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1963 if ( !triaSplits.empty() )
1966 const TTriangleFacet* facet = &triaSplits.front();
1967 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1968 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1969 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1972 else if ( nbNodes > 3 && !is24TetMode )
1974 // find the best method of splitting into triangles by aspect ratio
1975 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1976 map< double, int > badness2iCommon;
1977 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1978 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1979 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1982 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1984 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1985 nodes[ iQ*((iLast-1)%nbNodes)],
1986 nodes[ iQ*((iLast )%nbNodes)]);
1987 badness += getBadRate( &tria, aspectRatio );
1989 badness2iCommon.insert( make_pair( badness, iCommon ));
1991 // use iCommon with lowest badness
1992 iCommon = badness2iCommon.begin()->second;
1994 if ( iCommon >= nbNodes )
1995 iCommon = 0; // something wrong
1997 // fill connectivity of tetrahedra based on a current face
1998 int nbTet = nbNodes - 2;
1999 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2004 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2005 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2009 method._faceBaryNode[ iF ] = 0;
2010 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2013 for ( int i = 0; i < nbTet; ++i )
2015 int i1 = i, i2 = (i+1) % nbNodes;
2016 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2017 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2018 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2019 connectivity[ connSize++ ] = faceBaryCenInd;
2020 connectivity[ connSize++ ] = baryCenInd;
2025 for ( int i = 0; i < nbTet; ++i )
2027 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2028 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2029 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2030 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2031 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2032 connectivity[ connSize++ ] = baryCenInd;
2035 method._nbSplits += nbTet;
2037 } // loop on volume faces
2039 connectivity[ connSize++ ] = -1;
2041 } // end of generic solution
2045 //=======================================================================
2047 * \brief return TSplitMethod to split haxhedron into prisms
2049 //=======================================================================
2051 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2052 const int methodFlags,
2053 const int facetToSplit)
2055 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2057 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2059 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2061 static TSplitMethod to4methods[4]; // order BT, LR, FB
2062 if ( to4methods[iF]._nbSplits == 0 )
2066 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2067 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2068 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2071 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2072 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2073 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2076 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2077 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2078 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2080 default: return to4methods[3];
2082 to4methods[iF]._nbSplits = 4;
2083 to4methods[iF]._nbCorners = 6;
2085 return to4methods[iF];
2087 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2089 TSplitMethod method;
2091 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2093 const int nbVariants = 2, nbSplits = 2;
2094 const int** connVariants = 0;
2096 case 0: connVariants = theHexTo2Prisms_BT; break;
2097 case 1: connVariants = theHexTo2Prisms_LR; break;
2098 case 2: connVariants = theHexTo2Prisms_FB; break;
2099 default: return method;
2102 // look for prisms adjacent via facetToSplit and an opposite one
2103 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2105 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2106 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2107 if ( nbNodes != 4 ) return method;
2109 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2110 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2111 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2113 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2115 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2120 // there are adjacent prism
2121 for ( int variant = 0; variant < nbVariants; ++variant )
2123 // check method compliancy with adjacent prisms,
2124 // the found prism facets must be among facets of prisms described by current method
2125 method._nbSplits = nbSplits;
2126 method._nbCorners = 6;
2127 method._connectivity = connVariants[ variant ];
2128 if ( method.hasFacet( *t ))
2133 // No adjacent prisms. Select a variant with a best aspect ratio.
2135 double badness[2] = { 0., 0. };
2136 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2137 const SMDS_MeshNode** nodes = vol.GetNodes();
2138 for ( int variant = 0; variant < nbVariants; ++variant )
2139 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2141 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2142 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2144 method._connectivity = connVariants[ variant ];
2145 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2146 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2147 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2149 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2152 badness[ variant ] += getBadRate( &tria, aspectRatio );
2154 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2156 method._nbSplits = nbSplits;
2157 method._nbCorners = 6;
2158 method._connectivity = connVariants[ iBetter ];
2163 //================================================================================
2165 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2167 //================================================================================
2169 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2170 const SMDSAbs_GeometryType geom ) const
2172 // find the tetrahedron including the three nodes of facet
2173 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2174 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2175 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2176 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2177 while ( volIt1->more() )
2179 const SMDS_MeshElement* v = volIt1->next();
2180 if ( v->GetGeomType() != geom )
2182 const int lastCornerInd = v->NbCornerNodes() - 1;
2183 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2184 continue; // medium node not allowed
2185 const int ind2 = v->GetNodeIndex( n2 );
2186 if ( ind2 < 0 || lastCornerInd < ind2 )
2188 const int ind3 = v->GetNodeIndex( n3 );
2189 if ( ind3 < 0 || lastCornerInd < ind3 )
2196 //=======================================================================
2198 * \brief A key of a face of volume
2200 //=======================================================================
2202 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2204 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2206 TIDSortedNodeSet sortedNodes;
2207 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2208 int nbNodes = vol.NbFaceNodes( iF );
2209 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2210 for ( int i = 0; i < nbNodes; i += iQ )
2211 sortedNodes.insert( fNodes[i] );
2212 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2213 first.first = (*(n++))->GetID();
2214 first.second = (*(n++))->GetID();
2215 second.first = (*(n++))->GetID();
2216 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2221 //=======================================================================
2222 //function : SplitVolumes
2223 //purpose : Split volume elements into tetrahedra or prisms.
2224 // If facet ID < 0, element is split into tetrahedra,
2225 // else a hexahedron is split into prisms so that the given facet is
2226 // split into triangles
2227 //=======================================================================
2229 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2230 const int theMethodFlags)
2232 SMDS_VolumeTool volTool;
2233 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2234 fHelper.ToFixNodeParameters( true );
2236 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2237 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2239 SMESH_SequenceOfElemPtr newNodes, newElems;
2241 // map face of volume to it's baricenrtic node
2242 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2244 vector<const SMDS_MeshElement* > splitVols;
2246 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2247 for ( ; elem2facet != theElems.end(); ++elem2facet )
2249 const SMDS_MeshElement* elem = elem2facet->first;
2250 const int facetToSplit = elem2facet->second;
2251 if ( elem->GetType() != SMDSAbs_Volume )
2253 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2254 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2257 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2259 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2260 getTetraSplitMethod( volTool, theMethodFlags ) :
2261 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2262 if ( splitMethod._nbSplits < 1 ) continue;
2264 // find submesh to add new tetras to
2265 if ( !subMesh || !subMesh->Contains( elem ))
2267 int shapeID = FindShape( elem );
2268 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2269 subMesh = GetMeshDS()->MeshElements( shapeID );
2272 if ( elem->IsQuadratic() )
2275 // add quadratic links to the helper
2276 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2278 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2279 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2280 for ( int iN = 0; iN < nbN; iN += iQ )
2281 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2283 helper.SetIsQuadratic( true );
2288 helper.SetIsQuadratic( false );
2290 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2291 volTool.GetNodes() + elem->NbNodes() );
2292 helper.SetElementsOnShape( true );
2293 if ( splitMethod._baryNode )
2295 // make a node at barycenter
2296 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2297 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2298 nodes.push_back( gcNode );
2299 newNodes.Append( gcNode );
2301 if ( !splitMethod._faceBaryNode.empty() )
2303 // make or find baricentric nodes of faces
2304 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2305 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2307 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2308 volFace2BaryNode.insert
2309 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2312 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2313 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2315 nodes.push_back( iF_n->second = f_n->second );
2320 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2321 const int* volConn = splitMethod._connectivity;
2322 if ( splitMethod._nbCorners == 4 ) // tetra
2323 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2324 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2325 nodes[ volConn[1] ],
2326 nodes[ volConn[2] ],
2327 nodes[ volConn[3] ]));
2329 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331 nodes[ volConn[1] ],
2332 nodes[ volConn[2] ],
2333 nodes[ volConn[3] ],
2334 nodes[ volConn[4] ],
2335 nodes[ volConn[5] ]));
2337 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2339 // Split faces on sides of the split volume
2341 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2342 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2344 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2345 if ( nbNodes < 4 ) continue;
2347 // find an existing face
2348 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2349 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2350 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2351 /*noMedium=*/false))
2354 helper.SetElementsOnShape( false );
2355 vector< const SMDS_MeshElement* > triangles;
2357 // find submesh to add new triangles in
2358 if ( !fSubMesh || !fSubMesh->Contains( face ))
2360 int shapeID = FindShape( face );
2361 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2363 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2364 if ( iF_n != splitMethod._faceBaryNode.end() )
2366 const SMDS_MeshNode *baryNode = iF_n->second;
2367 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2369 const SMDS_MeshNode* n1 = fNodes[iN];
2370 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2371 const SMDS_MeshNode *n3 = baryNode;
2372 if ( !volTool.IsFaceExternal( iF ))
2374 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2376 if ( fSubMesh ) // update position of the bary node on geometry
2379 subMesh->RemoveNode( baryNode, false );
2380 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2381 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2382 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2384 fHelper.SetSubShape( s );
2385 gp_XY uv( 1e100, 1e100 );
2387 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2388 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2391 // node is too far from the surface
2392 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2393 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2394 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2401 // among possible triangles create ones described by split method
2402 const int* nInd = volTool.GetFaceNodesIndices( iF );
2403 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2404 int iCom = 0; // common node of triangle faces to split into
2405 list< TTriangleFacet > facets;
2406 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2408 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2409 nInd[ iQ * ( (iCom+1)%nbNodes )],
2410 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2411 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2412 nInd[ iQ * ( (iCom+2)%nbNodes )],
2413 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2414 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2416 facets.push_back( t012 );
2417 facets.push_back( t023 );
2418 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2419 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2420 nInd[ iQ * ((iLast-1)%nbNodes )],
2421 nInd[ iQ * ((iLast )%nbNodes )]));
2425 list< TTriangleFacet >::iterator facet = facets.begin();
2426 if ( facet == facets.end() )
2428 for ( ; facet != facets.end(); ++facet )
2430 if ( !volTool.IsFaceExternal( iF ))
2431 swap( facet->_n2, facet->_n3 );
2432 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2433 volNodes[ facet->_n2 ],
2434 volNodes[ facet->_n3 ]));
2437 for ( size_t i = 0; i < triangles.size(); ++i )
2439 if ( !triangles[ i ]) continue;
2441 fSubMesh->AddElement( triangles[ i ]);
2442 newElems.Append( triangles[ i ]);
2444 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2445 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2447 } // while a face based on facet nodes exists
2448 } // loop on volume faces to split them into triangles
2450 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2452 if ( geomType == SMDSEntity_TriQuad_Hexa )
2454 // remove medium nodes that could become free
2455 for ( int i = 20; i < volTool.NbNodes(); ++i )
2456 if ( volNodes[i]->NbInverseElements() == 0 )
2457 GetMeshDS()->RemoveNode( volNodes[i] );
2459 } // loop on volumes to split
2461 myLastCreatedNodes = newNodes;
2462 myLastCreatedElems = newElems;
2465 //=======================================================================
2466 //function : GetHexaFacetsToSplit
2467 //purpose : For hexahedra that will be split into prisms, finds facets to
2468 // split into triangles. Only hexahedra adjacent to the one closest
2469 // to theFacetNormal.Location() are returned.
2470 //param [in,out] theHexas - the hexahedra
2471 //param [in] theFacetNormal - facet normal
2472 //param [out] theFacets - the hexahedra and found facet IDs
2473 //=======================================================================
2475 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2476 const gp_Ax1& theFacetNormal,
2477 TFacetOfElem & theFacets)
2479 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2481 // Find a hexa closest to the location of theFacetNormal
2483 const SMDS_MeshElement* startHex;
2485 // get SMDS_ElemIteratorPtr on theHexas
2486 typedef const SMDS_MeshElement* TValue;
2487 typedef TIDSortedElemSet::iterator TSetIterator;
2488 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2489 typedef SMDS_MeshElement::GeomFilter TFilter;
2490 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2491 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2492 ( new TElemSetIter( theHexas.begin(),
2494 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2496 SMESH_ElementSearcher* searcher =
2497 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2499 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2504 throw SALOME_Exception( THIS_METHOD "startHex not found");
2507 // Select a facet of startHex by theFacetNormal
2509 SMDS_VolumeTool vTool( startHex );
2510 double norm[3], dot, maxDot = 0;
2512 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2513 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2515 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2523 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2525 // Fill theFacets starting from facetID of startHex
2527 // facets used for searching of volumes adjacent to already treated ones
2528 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2529 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2530 TFacetMap facetsToCheck;
2532 set<const SMDS_MeshNode*> facetNodes;
2533 const SMDS_MeshElement* curHex;
2535 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2539 // move in two directions from startHex via facetID
2540 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2543 int curFacet = facetID;
2544 if ( is2nd ) // do not treat startHex twice
2546 vTool.Set( curHex );
2547 if ( vTool.IsFreeFace( curFacet, &curHex ))
2553 vTool.GetFaceNodes( curFacet, facetNodes );
2554 vTool.Set( curHex );
2555 curFacet = vTool.GetFaceIndex( facetNodes );
2560 // store a facet to split
2561 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2563 theFacets.insert( make_pair( curHex, -1 ));
2566 if ( !allHex && !theHexas.count( curHex ))
2569 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2570 theFacets.insert( make_pair( curHex, curFacet ));
2571 if ( !facetIt2isNew.second )
2574 // remember not-to-split facets in facetsToCheck
2575 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2576 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2578 if ( iF == curFacet && iF == oppFacet )
2580 TVolumeFaceKey facetKey ( vTool, iF );
2581 TElemFacets elemFacet( facetIt2isNew.first, iF );
2582 pair< TFacetMap::iterator, bool > it2isnew =
2583 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2584 if ( !it2isnew.second )
2585 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2587 // pass to a volume adjacent via oppFacet
2588 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2594 // get a new curFacet
2595 vTool.GetFaceNodes( oppFacet, facetNodes );
2596 vTool.Set( curHex );
2597 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2600 } // move in two directions from startHex via facetID
2602 // Find a new startHex by facetsToCheck
2606 TFacetMap::iterator fIt = facetsToCheck.begin();
2607 while ( !startHex && fIt != facetsToCheck.end() )
2609 const TElemFacets& elemFacets = fIt->second;
2610 const SMDS_MeshElement* hex = elemFacets.first->first;
2611 int splitFacet = elemFacets.first->second;
2612 int lateralFacet = elemFacets.second;
2613 facetsToCheck.erase( fIt );
2614 fIt = facetsToCheck.begin();
2617 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2618 curHex->GetGeomType() != SMDSGeom_HEXA )
2620 if ( !allHex && !theHexas.count( curHex ))
2625 // find a facet of startHex to split
2627 set<const SMDS_MeshNode*> lateralNodes;
2628 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2629 vTool.GetFaceNodes( splitFacet, facetNodes );
2630 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2631 vTool.Set( startHex );
2632 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2634 // look for a facet of startHex having common nodes with facetNodes
2635 // but not lateralFacet
2636 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2638 if ( iF == lateralFacet )
2640 int nbCommonNodes = 0;
2641 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2642 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2643 nbCommonNodes += facetNodes.count( nn[ iN ]);
2645 if ( nbCommonNodes >= 2 )
2652 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2654 } // while ( startHex )
2661 //================================================================================
2663 * \brief Selects nodes of several elements according to a given interlace
2664 * \param [in] srcNodes - nodes to select from
2665 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2666 * \param [in] interlace - indices of nodes for all elements
2667 * \param [in] nbElems - nb of elements
2668 * \param [in] nbNodes - nb of nodes in each element
2669 * \param [in] mesh - the mesh
2670 * \param [out] elemQueue - a list to push elements found by the selected nodes
2671 * \param [in] type - type of elements to look for
2673 //================================================================================
2675 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2676 vector< const SMDS_MeshNode* >* tgtNodesVec,
2677 const int* interlace,
2680 SMESHDS_Mesh* mesh = 0,
2681 list< const SMDS_MeshElement* >* elemQueue=0,
2682 SMDSAbs_ElementType type=SMDSAbs_All)
2684 for ( int iE = 0; iE < nbElems; ++iE )
2686 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2687 const int* select = & interlace[iE*nbNodes];
2688 elemNodes.resize( nbNodes );
2689 for ( int iN = 0; iN < nbNodes; ++iN )
2690 elemNodes[iN] = srcNodes[ select[ iN ]];
2692 const SMDS_MeshElement* e;
2694 for ( int iE = 0; iE < nbElems; ++iE )
2695 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2696 elemQueue->push_back( e );
2700 //=======================================================================
2702 * Split bi-quadratic elements into linear ones without creation of additional nodes
2703 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2704 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2705 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2706 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2707 * will be split in order to keep the mesh conformal.
2708 * \param elems - elements to split
2710 //=======================================================================
2712 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2714 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2715 vector<const SMDS_MeshElement* > splitElems;
2716 list< const SMDS_MeshElement* > elemQueue;
2717 list< const SMDS_MeshElement* >::iterator elemIt;
2719 SMESHDS_Mesh * mesh = GetMeshDS();
2720 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2721 int nbElems, nbNodes;
2723 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2724 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2727 elemQueue.push_back( *elemSetIt );
2728 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2730 const SMDS_MeshElement* elem = *elemIt;
2731 switch( elem->GetEntityType() )
2733 case SMDSEntity_TriQuad_Hexa: // HEX27
2735 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2736 nbElems = nbNodes = 8;
2737 elemType = & hexaType;
2739 // get nodes for new elements
2740 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2741 { 1,9,20,8, 17,22,26,21 },
2742 { 2,10,20,9, 18,23,26,22 },
2743 { 3,11,20,10, 19,24,26,23 },
2744 { 16,21,26,24, 4,12,25,15 },
2745 { 17,22,26,21, 5,13,25,12 },
2746 { 18,23,26,22, 6,14,25,13 },
2747 { 19,24,26,23, 7,15,25,14 }};
2748 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2750 // add boundary faces to elemQueue
2751 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2752 { 4,5,6,7, 12,13,14,15, 25 },
2753 { 0,1,5,4, 8,17,12,16, 21 },
2754 { 1,2,6,5, 9,18,13,17, 22 },
2755 { 2,3,7,6, 10,19,14,18, 23 },
2756 { 3,0,4,7, 11,16,15,19, 24 }};
2757 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2759 // add boundary segments to elemQueue
2760 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2761 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2762 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2763 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2766 case SMDSEntity_BiQuad_Triangle: // TRIA7
2768 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2771 elemType = & quadType;
2773 // get nodes for new elements
2774 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2775 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2777 // add boundary segments to elemQueue
2778 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2779 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2782 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2784 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2787 elemType = & quadType;
2789 // get nodes for new elements
2790 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2791 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2793 // add boundary segments to elemQueue
2794 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2795 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2798 case SMDSEntity_Quad_Edge:
2800 if ( elemIt == elemQueue.begin() )
2801 continue; // an elem is in theElems
2802 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2805 elemType = & segType;
2807 // get nodes for new elements
2808 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2809 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2813 } // switch( elem->GetEntityType() )
2815 // Create new elements
2817 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2821 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2822 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2823 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2824 //elemType->SetID( -1 );
2826 for ( int iE = 0; iE < nbElems; ++iE )
2827 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2830 ReplaceElemInGroups( elem, splitElems, mesh );
2833 for ( size_t i = 0; i < splitElems.size(); ++i )
2834 subMesh->AddElement( splitElems[i] );
2839 //=======================================================================
2840 //function : AddToSameGroups
2841 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2842 //=======================================================================
2844 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2845 const SMDS_MeshElement* elemInGroups,
2846 SMESHDS_Mesh * aMesh)
2848 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849 if (!groups.empty()) {
2850 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2851 for ( ; grIt != groups.end(); grIt++ ) {
2852 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2853 if ( group && group->Contains( elemInGroups ))
2854 group->SMDSGroup().Add( elemToAdd );
2860 //=======================================================================
2861 //function : RemoveElemFromGroups
2862 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2863 //=======================================================================
2864 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2865 SMESHDS_Mesh * aMesh)
2867 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2868 if (!groups.empty())
2870 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2871 for (; GrIt != groups.end(); GrIt++)
2873 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2874 if (!grp || grp->IsEmpty()) continue;
2875 grp->SMDSGroup().Remove(removeelem);
2880 //================================================================================
2882 * \brief Replace elemToRm by elemToAdd in the all groups
2884 //================================================================================
2886 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2887 const SMDS_MeshElement* elemToAdd,
2888 SMESHDS_Mesh * aMesh)
2890 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2891 if (!groups.empty()) {
2892 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2893 for ( ; grIt != groups.end(); grIt++ ) {
2894 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2895 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2896 group->SMDSGroup().Add( elemToAdd );
2901 //================================================================================
2903 * \brief Replace elemToRm by elemToAdd in the all groups
2905 //================================================================================
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2908 const vector<const SMDS_MeshElement*>& elemToAdd,
2909 SMESHDS_Mesh * aMesh)
2911 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912 if (!groups.empty())
2914 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2915 for ( ; grIt != groups.end(); grIt++ ) {
2916 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2917 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2918 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2919 group->SMDSGroup().Add( elemToAdd[ i ] );
2924 //=======================================================================
2925 //function : QuadToTri
2926 //purpose : Cut quadrangles into triangles.
2927 // theCrit is used to select a diagonal to cut
2928 //=======================================================================
2930 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2931 const bool the13Diag)
2933 myLastCreatedElems.Clear();
2934 myLastCreatedNodes.Clear();
2936 SMESHDS_Mesh * aMesh = GetMeshDS();
2938 Handle(Geom_Surface) surface;
2939 SMESH_MesherHelper helper( *GetMesh() );
2941 TIDSortedElemSet::iterator itElem;
2942 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2944 const SMDS_MeshElement* elem = *itElem;
2945 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2948 if ( elem->NbNodes() == 4 ) {
2949 // retrieve element nodes
2950 const SMDS_MeshNode* aNodes [4];
2951 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953 while ( itN->more() )
2954 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956 int aShapeId = FindShape( elem );
2957 const SMDS_MeshElement* newElem1 = 0;
2958 const SMDS_MeshElement* newElem2 = 0;
2960 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2964 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967 myLastCreatedElems.Append(newElem1);
2968 myLastCreatedElems.Append(newElem2);
2969 // put a new triangle on the same shape and add to the same groups
2972 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975 AddToSameGroups( newElem1, elem, aMesh );
2976 AddToSameGroups( newElem2, elem, aMesh );
2977 aMesh->RemoveElement( elem );
2980 // Quadratic quadrangle
2982 else if ( elem->NbNodes() >= 8 )
2984 // get surface elem is on
2985 int aShapeId = FindShape( elem );
2986 if ( aShapeId != helper.GetSubShapeID() ) {
2990 shape = aMesh->IndexToShape( aShapeId );
2991 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992 TopoDS_Face face = TopoDS::Face( shape );
2993 surface = BRep_Tool::Surface( face );
2994 if ( !surface.IsNull() )
2995 helper.SetSubShape( shape );
2999 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3000 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001 for ( int i = 0; itN->more(); ++i )
3002 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3004 const SMDS_MeshNode* centrNode = aNodes[8];
3005 if ( centrNode == 0 )
3007 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3008 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3010 myLastCreatedNodes.Append(centrNode);
3013 // create a new element
3014 const SMDS_MeshElement* newElem1 = 0;
3015 const SMDS_MeshElement* newElem2 = 0;
3017 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3018 aNodes[6], aNodes[7], centrNode );
3019 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3020 centrNode, aNodes[4], aNodes[5] );
3023 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3024 aNodes[7], aNodes[4], centrNode );
3025 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3026 centrNode, aNodes[5], aNodes[6] );
3028 myLastCreatedElems.Append(newElem1);
3029 myLastCreatedElems.Append(newElem2);
3030 // put a new triangle on the same shape and add to the same groups
3033 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3034 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3036 AddToSameGroups( newElem1, elem, aMesh );
3037 AddToSameGroups( newElem2, elem, aMesh );
3038 aMesh->RemoveElement( elem );
3045 //=======================================================================
3046 //function : getAngle
3048 //=======================================================================
3050 double getAngle(const SMDS_MeshElement * tr1,
3051 const SMDS_MeshElement * tr2,
3052 const SMDS_MeshNode * n1,
3053 const SMDS_MeshNode * n2)
3055 double angle = 2. * M_PI; // bad angle
3058 SMESH::Controls::TSequenceOfXYZ P1, P2;
3059 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3060 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3063 if(!tr1->IsQuadratic())
3064 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3066 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3067 if ( N1.SquareMagnitude() <= gp::Resolution() )
3069 if(!tr2->IsQuadratic())
3070 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3072 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3073 if ( N2.SquareMagnitude() <= gp::Resolution() )
3076 // find the first diagonal node n1 in the triangles:
3077 // take in account a diagonal link orientation
3078 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3079 for ( int t = 0; t < 2; t++ ) {
3080 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3081 int i = 0, iDiag = -1;
3082 while ( it->more()) {
3083 const SMDS_MeshElement *n = it->next();
3084 if ( n == n1 || n == n2 ) {
3088 if ( i - iDiag == 1 )
3089 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3098 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3101 angle = N1.Angle( N2 );
3106 // =================================================
3107 // class generating a unique ID for a pair of nodes
3108 // and able to return nodes by that ID
3109 // =================================================
3113 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3114 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3117 long GetLinkID (const SMDS_MeshNode * n1,
3118 const SMDS_MeshNode * n2) const
3120 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3123 bool GetNodes (const long theLinkID,
3124 const SMDS_MeshNode* & theNode1,
3125 const SMDS_MeshNode* & theNode2) const
3127 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3128 if ( !theNode1 ) return false;
3129 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3130 if ( !theNode2 ) return false;
3136 const SMESHDS_Mesh* myMesh;
3141 //=======================================================================
3142 //function : TriToQuad
3143 //purpose : Fuse neighbour triangles into quadrangles.
3144 // theCrit is used to select a neighbour to fuse with.
3145 // theMaxAngle is a max angle between element normals at which
3146 // fusion is still performed.
3147 //=======================================================================
3149 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3150 SMESH::Controls::NumericalFunctorPtr theCrit,
3151 const double theMaxAngle)
3153 myLastCreatedElems.Clear();
3154 myLastCreatedNodes.Clear();
3156 if ( !theCrit.get() )
3159 SMESHDS_Mesh * aMesh = GetMeshDS();
3161 // Prepare data for algo: build
3162 // 1. map of elements with their linkIDs
3163 // 2. map of linkIDs with their elements
3165 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3166 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3167 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3168 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3170 TIDSortedElemSet::iterator itElem;
3171 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3173 const SMDS_MeshElement* elem = *itElem;
3174 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3175 bool IsTria = ( elem->NbCornerNodes()==3 );
3176 if (!IsTria) continue;
3178 // retrieve element nodes
3179 const SMDS_MeshNode* aNodes [4];
3180 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3183 aNodes[ i++ ] = itN->next();
3184 aNodes[ 3 ] = aNodes[ 0 ];
3187 for ( i = 0; i < 3; i++ ) {
3188 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3189 // check if elements sharing a link can be fused
3190 itLE = mapLi_listEl.find( link );
3191 if ( itLE != mapLi_listEl.end() ) {
3192 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3194 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3195 //if ( FindShape( elem ) != FindShape( elem2 ))
3196 // continue; // do not fuse triangles laying on different shapes
3197 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3198 continue; // avoid making badly shaped quads
3199 (*itLE).second.push_back( elem );
3202 mapLi_listEl[ link ].push_back( elem );
3204 mapEl_setLi [ elem ].insert( link );
3207 // Clean the maps from the links shared by a sole element, ie
3208 // links to which only one element is bound in mapLi_listEl
3210 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3211 int nbElems = (*itLE).second.size();
3212 if ( nbElems < 2 ) {
3213 const SMDS_MeshElement* elem = (*itLE).second.front();
3214 SMESH_TLink link = (*itLE).first;
3215 mapEl_setLi[ elem ].erase( link );
3216 if ( mapEl_setLi[ elem ].empty() )
3217 mapEl_setLi.erase( elem );
3221 // Algo: fuse triangles into quadrangles
3223 while ( ! mapEl_setLi.empty() ) {
3224 // Look for the start element:
3225 // the element having the least nb of shared links
3226 const SMDS_MeshElement* startElem = 0;
3228 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3229 int nbLinks = (*itEL).second.size();
3230 if ( nbLinks < minNbLinks ) {
3231 startElem = (*itEL).first;
3232 minNbLinks = nbLinks;
3233 if ( minNbLinks == 1 )
3238 // search elements to fuse starting from startElem or links of elements
3239 // fused earlyer - startLinks
3240 list< SMESH_TLink > startLinks;
3241 while ( startElem || !startLinks.empty() ) {
3242 while ( !startElem && !startLinks.empty() ) {
3243 // Get an element to start, by a link
3244 SMESH_TLink linkId = startLinks.front();
3245 startLinks.pop_front();
3246 itLE = mapLi_listEl.find( linkId );
3247 if ( itLE != mapLi_listEl.end() ) {
3248 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3249 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3250 for ( ; itE != listElem.end() ; itE++ )
3251 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3253 mapLi_listEl.erase( itLE );
3258 // Get candidates to be fused
3259 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3260 const SMESH_TLink *link12 = 0, *link13 = 0;
3262 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3263 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3264 ASSERT( !setLi.empty() );
3265 set< SMESH_TLink >::iterator itLi;
3266 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3268 const SMESH_TLink & link = (*itLi);
3269 itLE = mapLi_listEl.find( link );
3270 if ( itLE == mapLi_listEl.end() )
3273 const SMDS_MeshElement* elem = (*itLE).second.front();
3275 elem = (*itLE).second.back();
3276 mapLi_listEl.erase( itLE );
3277 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3288 // add other links of elem to list of links to re-start from
3289 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3290 set< SMESH_TLink >::iterator it;
3291 for ( it = links.begin(); it != links.end(); it++ ) {
3292 const SMESH_TLink& link2 = (*it);
3293 if ( link2 != link )
3294 startLinks.push_back( link2 );
3298 // Get nodes of possible quadrangles
3299 const SMDS_MeshNode *n12 [4], *n13 [4];
3300 bool Ok12 = false, Ok13 = false;
3301 const SMDS_MeshNode *linkNode1, *linkNode2;
3303 linkNode1 = link12->first;
3304 linkNode2 = link12->second;
3305 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3309 linkNode1 = link13->first;
3310 linkNode2 = link13->second;
3311 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3315 // Choose a pair to fuse
3316 if ( Ok12 && Ok13 ) {
3317 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3318 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3319 double aBadRate12 = getBadRate( &quad12, theCrit );
3320 double aBadRate13 = getBadRate( &quad13, theCrit );
3321 if ( aBadRate13 < aBadRate12 )
3328 // and remove fused elems and remove links from the maps
3329 mapEl_setLi.erase( tr1 );
3332 mapEl_setLi.erase( tr2 );
3333 mapLi_listEl.erase( *link12 );
3334 if ( tr1->NbNodes() == 3 )
3336 const SMDS_MeshElement* newElem = 0;
3337 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3338 myLastCreatedElems.Append(newElem);
3339 AddToSameGroups( newElem, tr1, aMesh );
3340 int aShapeId = tr1->getshapeId();
3342 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3343 aMesh->RemoveElement( tr1 );
3344 aMesh->RemoveElement( tr2 );
3347 vector< const SMDS_MeshNode* > N1;
3348 vector< const SMDS_MeshNode* > N2;
3349 getNodesFromTwoTria(tr1,tr2,N1,N2);
3350 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3351 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3352 // i.e. first nodes from both arrays form a new diagonal
3353 const SMDS_MeshNode* aNodes[8];
3362 const SMDS_MeshElement* newElem = 0;
3363 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3364 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3365 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3367 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3369 myLastCreatedElems.Append(newElem);
3370 AddToSameGroups( newElem, tr1, aMesh );
3371 int aShapeId = tr1->getshapeId();
3373 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3374 aMesh->RemoveElement( tr1 );
3375 aMesh->RemoveElement( tr2 );
3376 // remove middle node (9)
3377 if ( N1[4]->NbInverseElements() == 0 )
3378 aMesh->RemoveNode( N1[4] );
3379 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3380 aMesh->RemoveNode( N1[6] );
3381 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3382 aMesh->RemoveNode( N2[6] );
3387 mapEl_setLi.erase( tr3 );
3388 mapLi_listEl.erase( *link13 );
3389 if ( tr1->NbNodes() == 3 ) {
3390 const SMDS_MeshElement* newElem = 0;
3391 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3392 myLastCreatedElems.Append(newElem);
3393 AddToSameGroups( newElem, tr1, aMesh );
3394 int aShapeId = tr1->getshapeId();
3396 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3397 aMesh->RemoveElement( tr1 );
3398 aMesh->RemoveElement( tr3 );
3401 vector< const SMDS_MeshNode* > N1;
3402 vector< const SMDS_MeshNode* > N2;
3403 getNodesFromTwoTria(tr1,tr3,N1,N2);
3404 // now we receive following N1 and N2 (using numeration as above image)
3405 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3406 // i.e. first nodes from both arrays form a new diagonal
3407 const SMDS_MeshNode* aNodes[8];
3416 const SMDS_MeshElement* newElem = 0;
3417 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3418 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3419 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3421 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3423 myLastCreatedElems.Append(newElem);
3424 AddToSameGroups( newElem, tr1, aMesh );
3425 int aShapeId = tr1->getshapeId();
3427 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3428 aMesh->RemoveElement( tr1 );
3429 aMesh->RemoveElement( tr3 );
3430 // remove middle node (9)
3431 if ( N1[4]->NbInverseElements() == 0 )
3432 aMesh->RemoveNode( N1[4] );
3433 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3434 aMesh->RemoveNode( N1[6] );
3435 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3436 aMesh->RemoveNode( N2[6] );
3440 // Next element to fuse: the rejected one
3442 startElem = Ok12 ? tr3 : tr2;
3444 } // if ( startElem )
3445 } // while ( startElem || !startLinks.empty() )
3446 } // while ( ! mapEl_setLi.empty() )
3452 /*#define DUMPSO(txt) \
3453 // cout << txt << endl;
3454 //=============================================================================
3458 //=============================================================================
3459 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3463 int tmp = idNodes[ i1 ];
3464 idNodes[ i1 ] = idNodes[ i2 ];
3465 idNodes[ i2 ] = tmp;
3466 gp_Pnt Ptmp = P[ i1 ];
3469 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3472 //=======================================================================
3473 //function : SortQuadNodes
3474 //purpose : Set 4 nodes of a quadrangle face in a good order.
3475 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3477 //=======================================================================
3479 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3484 for ( i = 0; i < 4; i++ ) {
3485 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3487 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3490 gp_Vec V1(P[0], P[1]);
3491 gp_Vec V2(P[0], P[2]);
3492 gp_Vec V3(P[0], P[3]);
3494 gp_Vec Cross1 = V1 ^ V2;
3495 gp_Vec Cross2 = V2 ^ V3;
3498 if (Cross1.Dot(Cross2) < 0)
3503 if (Cross1.Dot(Cross2) < 0)
3507 swap ( i, i + 1, idNodes, P );
3509 // for ( int ii = 0; ii < 4; ii++ ) {
3510 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3511 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3517 //=======================================================================
3518 //function : SortHexaNodes
3519 //purpose : Set 8 nodes of a hexahedron in a good order.
3520 // Return success status
3521 //=======================================================================
3523 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3528 DUMPSO( "INPUT: ========================================");
3529 for ( i = 0; i < 8; i++ ) {
3530 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3531 if ( !n ) return false;
3532 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3533 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3535 DUMPSO( "========================================");
3538 set<int> faceNodes; // ids of bottom face nodes, to be found
3539 set<int> checkedId1; // ids of tried 2-nd nodes
3540 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3541 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3542 int iMin, iLoop1 = 0;
3544 // Loop to try the 2-nd nodes
3546 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3548 // Find not checked 2-nd node
3549 for ( i = 1; i < 8; i++ )
3550 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3551 int id1 = idNodes[i];
3552 swap ( 1, i, idNodes, P );
3553 checkedId1.insert ( id1 );
3557 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3558 // ie that all but meybe one (id3 which is on the same face) nodes
3559 // lay on the same side from the triangle plane.
3561 bool manyInPlane = false; // more than 4 nodes lay in plane
3563 while ( ++iLoop2 < 6 ) {
3565 // get 1-2-3 plane coeffs
3566 Standard_Real A, B, C, D;
3567 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3568 if ( N.SquareMagnitude() > gp::Resolution() )
3570 gp_Pln pln ( P[0], N );
3571 pln.Coefficients( A, B, C, D );
3573 // find the node (iMin) closest to pln
3574 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3576 for ( i = 3; i < 8; i++ ) {
3577 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3578 if ( fabs( dist[i] ) < minDist ) {
3579 minDist = fabs( dist[i] );
3582 if ( fabs( dist[i] ) <= tol )
3583 idInPln.insert( idNodes[i] );
3586 // there should not be more than 4 nodes in bottom plane
3587 if ( idInPln.size() > 1 )
3589 DUMPSO( "### idInPln.size() = " << idInPln.size());
3590 // idInPlane does not contain the first 3 nodes
3591 if ( manyInPlane || idInPln.size() == 5)
3592 return false; // all nodes in one plane
3595 // set the 1-st node to be not in plane
3596 for ( i = 3; i < 8; i++ ) {
3597 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3598 DUMPSO( "### Reset 0-th node");
3599 swap( 0, i, idNodes, P );
3604 // reset to re-check second nodes
3605 leastDist = DBL_MAX;
3609 break; // from iLoop2;
3612 // check that the other 4 nodes are on the same side
3613 bool sameSide = true;
3614 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3615 for ( i = 3; sameSide && i < 8; i++ ) {
3617 sameSide = ( isNeg == dist[i] <= 0.);
3620 // keep best solution
3621 if ( sameSide && minDist < leastDist ) {
3622 leastDist = minDist;
3624 faceNodes.insert( idNodes[ 1 ] );
3625 faceNodes.insert( idNodes[ 2 ] );
3626 faceNodes.insert( idNodes[ iMin ] );
3627 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3628 << " leastDist = " << leastDist);
3629 if ( leastDist <= DBL_MIN )
3634 // set next 3-d node to check
3635 int iNext = 2 + iLoop2;
3637 DUMPSO( "Try 2-nd");
3638 swap ( 2, iNext, idNodes, P );
3640 } // while ( iLoop2 < 6 )
3643 if ( faceNodes.empty() ) return false;
3645 // Put the faceNodes in proper places
3646 for ( i = 4; i < 8; i++ ) {
3647 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3648 // find a place to put
3650 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3652 DUMPSO( "Set faceNodes");
3653 swap ( iTo, i, idNodes, P );
3658 // Set nodes of the found bottom face in good order
3659 DUMPSO( " Found bottom face: ");
3660 i = SortQuadNodes( theMesh, idNodes );
3662 gp_Pnt Ptmp = P[ i ];
3667 // for ( int ii = 0; ii < 4; ii++ ) {
3668 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3669 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3672 // Gravity center of the top and bottom faces
3673 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3674 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3676 // Get direction from the bottom to the top face
3677 gp_Vec upDir ( aGCb, aGCt );
3678 Standard_Real upDirSize = upDir.Magnitude();
3679 if ( upDirSize <= gp::Resolution() ) return false;
3682 // Assure that the bottom face normal points up
3683 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3684 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3685 if ( Nb.Dot( upDir ) < 0 ) {
3686 DUMPSO( "Reverse bottom face");
3687 swap( 1, 3, idNodes, P );
3690 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3691 Standard_Real minDist = DBL_MAX;
3692 for ( i = 4; i < 8; i++ ) {
3693 // projection of P[i] to the plane defined by P[0] and upDir
3694 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3695 Standard_Real sqDist = P[0].SquareDistance( Pp );
3696 if ( sqDist < minDist ) {
3701 DUMPSO( "Set 4-th");
3702 swap ( 4, iMin, idNodes, P );
3704 // Set nodes of the top face in good order
3705 DUMPSO( "Sort top face");
3706 i = SortQuadNodes( theMesh, &idNodes[4] );
3709 gp_Pnt Ptmp = P[ i ];
3714 // Assure that direction of the top face normal is from the bottom face
3715 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3716 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3717 if ( Nt.Dot( upDir ) < 0 ) {
3718 DUMPSO( "Reverse top face");
3719 swap( 5, 7, idNodes, P );
3722 // DUMPSO( "OUTPUT: ========================================");
3723 // for ( i = 0; i < 8; i++ ) {
3724 // float *p = ugrid->GetPoint(idNodes[i]);
3725 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3731 //================================================================================
3733 * \brief Return nodes linked to the given one
3734 * \param theNode - the node
3735 * \param linkedNodes - the found nodes
3736 * \param type - the type of elements to check
3738 * Medium nodes are ignored
3740 //================================================================================
3742 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3743 TIDSortedElemSet & linkedNodes,
3744 SMDSAbs_ElementType type )
3746 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3747 while ( elemIt->more() )
3749 const SMDS_MeshElement* elem = elemIt->next();
3750 if(elem->GetType() == SMDSAbs_0DElement)
3753 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3754 if ( elem->GetType() == SMDSAbs_Volume )
3756 SMDS_VolumeTool vol( elem );
3757 while ( nodeIt->more() ) {
3758 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3759 if ( theNode != n && vol.IsLinked( theNode, n ))
3760 linkedNodes.insert( n );
3765 for ( int i = 0; nodeIt->more(); ++i ) {
3766 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3767 if ( n == theNode ) {
3768 int iBefore = i - 1;
3770 if ( elem->IsQuadratic() ) {
3771 int nb = elem->NbNodes() / 2;
3772 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3773 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3775 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3776 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3783 //=======================================================================
3784 //function : laplacianSmooth
3785 //purpose : pulls theNode toward the center of surrounding nodes directly
3786 // connected to that node along an element edge
3787 //=======================================================================
3789 void laplacianSmooth(const SMDS_MeshNode* theNode,
3790 const Handle(Geom_Surface)& theSurface,
3791 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3793 // find surrounding nodes
3795 TIDSortedElemSet nodeSet;
3796 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3798 // compute new coodrs
3800 double coord[] = { 0., 0., 0. };
3801 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3802 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3803 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3804 if ( theSurface.IsNull() ) { // smooth in 3D
3805 coord[0] += node->X();
3806 coord[1] += node->Y();
3807 coord[2] += node->Z();
3809 else { // smooth in 2D
3810 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3811 gp_XY* uv = theUVMap[ node ];
3812 coord[0] += uv->X();
3813 coord[1] += uv->Y();
3816 int nbNodes = nodeSet.size();
3819 coord[0] /= nbNodes;
3820 coord[1] /= nbNodes;
3822 if ( !theSurface.IsNull() ) {
3823 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3824 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3825 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3831 coord[2] /= nbNodes;
3835 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3838 //=======================================================================
3839 //function : centroidalSmooth
3840 //purpose : pulls theNode toward the element-area-weighted centroid of the
3841 // surrounding elements
3842 //=======================================================================
3844 void centroidalSmooth(const SMDS_MeshNode* theNode,
3845 const Handle(Geom_Surface)& theSurface,
3846 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3848 gp_XYZ aNewXYZ(0.,0.,0.);
3849 SMESH::Controls::Area anAreaFunc;
3850 double totalArea = 0.;
3855 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3856 while ( elemIt->more() )
3858 const SMDS_MeshElement* elem = elemIt->next();
3861 gp_XYZ elemCenter(0.,0.,0.);
3862 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3863 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3864 int nn = elem->NbNodes();
3865 if(elem->IsQuadratic()) nn = nn/2;
3867 //while ( itN->more() ) {
3869 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3871 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3872 aNodePoints.push_back( aP );
3873 if ( !theSurface.IsNull() ) { // smooth in 2D
3874 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3875 gp_XY* uv = theUVMap[ aNode ];
3876 aP.SetCoord( uv->X(), uv->Y(), 0. );
3880 double elemArea = anAreaFunc.GetValue( aNodePoints );
3881 totalArea += elemArea;
3883 aNewXYZ += elemCenter * elemArea;
3885 aNewXYZ /= totalArea;
3886 if ( !theSurface.IsNull() ) {
3887 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3888 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3893 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3896 //=======================================================================
3897 //function : getClosestUV
3898 //purpose : return UV of closest projection
3899 //=======================================================================
3901 static bool getClosestUV (Extrema_GenExtPS& projector,
3902 const gp_Pnt& point,
3905 projector.Perform( point );
3906 if ( projector.IsDone() ) {
3907 double u, v, minVal = DBL_MAX;
3908 for ( int i = projector.NbExt(); i > 0; i-- )
3909 if ( projector.SquareDistance( i ) < minVal ) {
3910 minVal = projector.SquareDistance( i );
3911 projector.Point( i ).Parameter( u, v );
3913 result.SetCoord( u, v );
3919 //=======================================================================
3921 //purpose : Smooth theElements during theNbIterations or until a worst
3922 // element has aspect ratio <= theTgtAspectRatio.
3923 // Aspect Ratio varies in range [1.0, inf].
3924 // If theElements is empty, the whole mesh is smoothed.
3925 // theFixedNodes contains additionally fixed nodes. Nodes built
3926 // on edges and boundary nodes are always fixed.
3927 //=======================================================================
3929 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3930 set<const SMDS_MeshNode*> & theFixedNodes,
3931 const SmoothMethod theSmoothMethod,
3932 const int theNbIterations,
3933 double theTgtAspectRatio,
3936 myLastCreatedElems.Clear();
3937 myLastCreatedNodes.Clear();
3939 if ( theTgtAspectRatio < 1.0 )
3940 theTgtAspectRatio = 1.0;
3942 const double disttol = 1.e-16;
3944 SMESH::Controls::AspectRatio aQualityFunc;
3946 SMESHDS_Mesh* aMesh = GetMeshDS();
3948 if ( theElems.empty() ) {
3949 // add all faces to theElems
3950 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3951 while ( fIt->more() ) {
3952 const SMDS_MeshElement* face = fIt->next();
3953 theElems.insert( theElems.end(), face );
3956 // get all face ids theElems are on
3957 set< int > faceIdSet;
3958 TIDSortedElemSet::iterator itElem;
3960 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3961 int fId = FindShape( *itElem );
3962 // check that corresponding submesh exists and a shape is face
3964 faceIdSet.find( fId ) == faceIdSet.end() &&
3965 aMesh->MeshElements( fId )) {
3966 TopoDS_Shape F = aMesh->IndexToShape( fId );
3967 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3968 faceIdSet.insert( fId );
3971 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3973 // ===============================================
3974 // smooth elements on each TopoDS_Face separately
3975 // ===============================================
3977 SMESH_MesherHelper helper( *GetMesh() );
3979 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3980 for ( ; fId != faceIdSet.rend(); ++fId )
3982 // get face surface and submesh
3983 Handle(Geom_Surface) surface;
3984 SMESHDS_SubMesh* faceSubMesh = 0;
3987 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3988 bool isUPeriodic = false, isVPeriodic = false;
3991 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3992 surface = BRep_Tool::Surface( face );
3993 faceSubMesh = aMesh->MeshElements( *fId );
3994 fToler2 = BRep_Tool::Tolerance( face );
3995 fToler2 *= fToler2 * 10.;
3996 isUPeriodic = surface->IsUPeriodic();
3997 // if ( isUPeriodic )
3998 // surface->UPeriod();
3999 isVPeriodic = surface->IsVPeriodic();
4000 // if ( isVPeriodic )
4001 // surface->VPeriod();
4002 surface->Bounds( u1, u2, v1, v2 );
4003 helper.SetSubShape( face );
4005 // ---------------------------------------------------------
4006 // for elements on a face, find movable and fixed nodes and
4007 // compute UV for them
4008 // ---------------------------------------------------------
4009 bool checkBoundaryNodes = false;
4010 bool isQuadratic = false;
4011 set<const SMDS_MeshNode*> setMovableNodes;
4012 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4013 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4014 list< const SMDS_MeshElement* > elemsOnFace;
4016 Extrema_GenExtPS projector;
4017 GeomAdaptor_Surface surfAdaptor;
4018 if ( !surface.IsNull() ) {
4019 surfAdaptor.Load( surface );
4020 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4022 int nbElemOnFace = 0;
4023 itElem = theElems.begin();
4024 // loop on not yet smoothed elements: look for elems on a face
4025 while ( itElem != theElems.end() )
4027 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4028 break; // all elements found
4030 const SMDS_MeshElement* elem = *itElem;
4031 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4032 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4036 elemsOnFace.push_back( elem );
4037 theElems.erase( itElem++ );
4041 isQuadratic = elem->IsQuadratic();
4043 // get movable nodes of elem
4044 const SMDS_MeshNode* node;
4045 SMDS_TypeOfPosition posType;
4046 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4047 int nn = 0, nbn = elem->NbNodes();
4048 if(elem->IsQuadratic())
4050 while ( nn++ < nbn ) {
4051 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4052 const SMDS_PositionPtr& pos = node->GetPosition();
4053 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4054 if (posType != SMDS_TOP_EDGE &&
4055 posType != SMDS_TOP_VERTEX &&
4056 theFixedNodes.find( node ) == theFixedNodes.end())
4058 // check if all faces around the node are on faceSubMesh
4059 // because a node on edge may be bound to face
4061 if ( faceSubMesh ) {
4062 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4063 while ( eIt->more() && all ) {
4064 const SMDS_MeshElement* e = eIt->next();
4065 all = faceSubMesh->Contains( e );
4069 setMovableNodes.insert( node );
4071 checkBoundaryNodes = true;
4073 if ( posType == SMDS_TOP_3DSPACE )
4074 checkBoundaryNodes = true;
4077 if ( surface.IsNull() )
4080 // get nodes to check UV
4081 list< const SMDS_MeshNode* > uvCheckNodes;
4082 const SMDS_MeshNode* nodeInFace = 0;
4083 itN = elem->nodesIterator();
4084 nn = 0; nbn = elem->NbNodes();
4085 if(elem->IsQuadratic())
4087 while ( nn++ < nbn ) {
4088 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4089 if ( node->GetPosition()->GetDim() == 2 )
4091 if ( uvMap.find( node ) == uvMap.end() )
4092 uvCheckNodes.push_back( node );
4093 // add nodes of elems sharing node
4094 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4095 // while ( eIt->more() ) {
4096 // const SMDS_MeshElement* e = eIt->next();
4097 // if ( e != elem ) {
4098 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4099 // while ( nIt->more() ) {
4100 // const SMDS_MeshNode* n =
4101 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4102 // if ( uvMap.find( n ) == uvMap.end() )
4103 // uvCheckNodes.push_back( n );
4109 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4110 for ( ; n != uvCheckNodes.end(); ++n ) {
4113 const SMDS_PositionPtr& pos = node->GetPosition();
4114 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4118 bool toCheck = true;
4119 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4121 // compute not existing UV
4122 bool project = ( posType == SMDS_TOP_3DSPACE );
4123 // double dist1 = DBL_MAX, dist2 = 0;
4124 // if ( posType != SMDS_TOP_3DSPACE ) {
4125 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4126 // project = dist1 > fToler2;
4128 if ( project ) { // compute new UV
4130 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4131 if ( !getClosestUV( projector, pNode, newUV )) {
4132 MESSAGE("Node Projection Failed " << node);
4136 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4138 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4140 // if ( posType != SMDS_TOP_3DSPACE )
4141 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4142 // if ( dist2 < dist1 )
4146 // store UV in the map
4147 listUV.push_back( uv );
4148 uvMap.insert( make_pair( node, &listUV.back() ));
4150 } // loop on not yet smoothed elements
4152 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4153 checkBoundaryNodes = true;
4155 // fix nodes on mesh boundary
4157 if ( checkBoundaryNodes ) {
4158 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4159 map< SMESH_TLink, int >::iterator link_nb;
4160 // put all elements links to linkNbMap
4161 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4162 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4163 const SMDS_MeshElement* elem = (*elemIt);
4164 int nbn = elem->NbCornerNodes();
4165 // loop on elem links: insert them in linkNbMap
4166 for ( int iN = 0; iN < nbn; ++iN ) {
4167 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4168 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4169 SMESH_TLink link( n1, n2 );
4170 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4174 // remove nodes that are in links encountered only once from setMovableNodes
4175 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4176 if ( link_nb->second == 1 ) {
4177 setMovableNodes.erase( link_nb->first.node1() );
4178 setMovableNodes.erase( link_nb->first.node2() );
4183 // -----------------------------------------------------
4184 // for nodes on seam edge, compute one more UV ( uvMap2 );
4185 // find movable nodes linked to nodes on seam and which
4186 // are to be smoothed using the second UV ( uvMap2 )
4187 // -----------------------------------------------------
4189 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4190 if ( !surface.IsNull() ) {
4191 TopExp_Explorer eExp( face, TopAbs_EDGE );
4192 for ( ; eExp.More(); eExp.Next() ) {
4193 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4194 if ( !BRep_Tool::IsClosed( edge, face ))
4196 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4197 if ( !sm ) continue;
4198 // find out which parameter varies for a node on seam
4201 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4202 if ( pcurve.IsNull() ) continue;
4203 uv1 = pcurve->Value( f );
4205 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206 if ( pcurve.IsNull() ) continue;
4207 uv2 = pcurve->Value( f );
4208 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4210 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4211 std::swap( uv1, uv2 );
4212 // get nodes on seam and its vertices
4213 list< const SMDS_MeshNode* > seamNodes;
4214 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4215 while ( nSeamIt->more() ) {
4216 const SMDS_MeshNode* node = nSeamIt->next();
4217 if ( !isQuadratic || !IsMedium( node ))
4218 seamNodes.push_back( node );
4220 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4221 for ( ; vExp.More(); vExp.Next() ) {
4222 sm = aMesh->MeshElements( vExp.Current() );
4224 nSeamIt = sm->GetNodes();
4225 while ( nSeamIt->more() )
4226 seamNodes.push_back( nSeamIt->next() );
4229 // loop on nodes on seam
4230 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4231 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4232 const SMDS_MeshNode* nSeam = *noSeIt;
4233 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4234 if ( n_uv == uvMap.end() )
4237 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4238 // set the second UV
4239 listUV.push_back( *n_uv->second );
4240 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4241 if ( uvMap2.empty() )
4242 uvMap2 = uvMap; // copy the uvMap contents
4243 uvMap2[ nSeam ] = &listUV.back();
4245 // collect movable nodes linked to ones on seam in nodesNearSeam
4246 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4247 while ( eIt->more() ) {
4248 const SMDS_MeshElement* e = eIt->next();
4249 int nbUseMap1 = 0, nbUseMap2 = 0;
4250 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4251 int nn = 0, nbn = e->NbNodes();
4252 if(e->IsQuadratic()) nbn = nbn/2;
4253 while ( nn++ < nbn )
4255 const SMDS_MeshNode* n =
4256 static_cast<const SMDS_MeshNode*>( nIt->next() );
4258 setMovableNodes.find( n ) == setMovableNodes.end() )
4260 // add only nodes being closer to uv2 than to uv1
4261 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4262 // 0.5 * ( n->Y() + nSeam->Y() ),
4263 // 0.5 * ( n->Z() + nSeam->Z() ));
4265 // getClosestUV( projector, pMid, uv );
4266 double x = uvMap[ n ]->Coord( iPar );
4267 if ( Abs( uv1.Coord( iPar ) - x ) >
4268 Abs( uv2.Coord( iPar ) - x )) {
4269 nodesNearSeam.insert( n );
4275 // for centroidalSmooth all element nodes must
4276 // be on one side of a seam
4277 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4278 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4280 while ( nn++ < nbn ) {
4281 const SMDS_MeshNode* n =
4282 static_cast<const SMDS_MeshNode*>( nIt->next() );
4283 setMovableNodes.erase( n );
4287 } // loop on nodes on seam
4288 } // loop on edge of a face
4289 } // if ( !face.IsNull() )
4291 if ( setMovableNodes.empty() ) {
4292 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4293 continue; // goto next face
4301 double maxRatio = -1., maxDisplacement = -1.;
4302 set<const SMDS_MeshNode*>::iterator nodeToMove;
4303 for ( it = 0; it < theNbIterations; it++ ) {
4304 maxDisplacement = 0.;
4305 nodeToMove = setMovableNodes.begin();
4306 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4307 const SMDS_MeshNode* node = (*nodeToMove);
4308 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4311 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4312 if ( theSmoothMethod == LAPLACIAN )
4313 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4315 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4317 // node displacement
4318 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4319 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4320 if ( aDispl > maxDisplacement )
4321 maxDisplacement = aDispl;
4323 // no node movement => exit
4324 //if ( maxDisplacement < 1.e-16 ) {
4325 if ( maxDisplacement < disttol ) {
4326 MESSAGE("-- no node movement --");
4330 // check elements quality
4332 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4333 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4334 const SMDS_MeshElement* elem = (*elemIt);
4335 if ( !elem || elem->GetType() != SMDSAbs_Face )
4337 SMESH::Controls::TSequenceOfXYZ aPoints;
4338 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4339 double aValue = aQualityFunc.GetValue( aPoints );
4340 if ( aValue > maxRatio )
4344 if ( maxRatio <= theTgtAspectRatio ) {
4345 //MESSAGE("-- quality achieved --");
4348 if (it+1 == theNbIterations) {
4349 //MESSAGE("-- Iteration limit exceeded --");
4351 } // smoothing iterations
4353 // MESSAGE(" Face id: " << *fId <<
4354 // " Nb iterstions: " << it <<
4355 // " Displacement: " << maxDisplacement <<
4356 // " Aspect Ratio " << maxRatio);
4358 // ---------------------------------------
4359 // new nodes positions are computed,
4360 // record movement in DS and set new UV
4361 // ---------------------------------------
4362 nodeToMove = setMovableNodes.begin();
4363 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4364 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4365 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4366 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4367 if ( node_uv != uvMap.end() ) {
4368 gp_XY* uv = node_uv->second;
4370 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4374 // move medium nodes of quadratic elements
4377 vector<const SMDS_MeshNode*> nodes;
4379 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4380 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4382 const SMDS_MeshElement* QF = *elemIt;
4383 if ( QF->IsQuadratic() )
4385 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4386 SMDS_MeshElement::iterator() );
4387 nodes.push_back( nodes[0] );
4389 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4391 if ( !surface.IsNull() )
4393 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4394 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4395 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4396 xyz = surface->Value( uv.X(), uv.Y() );
4399 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4401 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4402 // we have to move a medium node
4403 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4409 } // loop on face ids
4415 //=======================================================================
4416 //function : isReverse
4417 //purpose : Return true if normal of prevNodes is not co-directied with
4418 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4419 // iNotSame is where prevNodes and nextNodes are different.
4420 // If result is true then future volume orientation is OK
4421 //=======================================================================
4423 bool isReverse(const SMDS_MeshElement* face,
4424 const vector<const SMDS_MeshNode*>& prevNodes,
4425 const vector<const SMDS_MeshNode*>& nextNodes,
4429 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4430 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4431 gp_XYZ extrDir( pN - pP ), faceNorm;
4432 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4434 return faceNorm * extrDir < 0.0;
4437 //================================================================================
4439 * \brief Assure that theElemSets[0] holds elements, not nodes
4441 //================================================================================
4443 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4445 if ( !theElemSets[0].empty() &&
4446 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4448 std::swap( theElemSets[0], theElemSets[1] );
4450 else if ( !theElemSets[1].empty() &&
4451 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4453 std::swap( theElemSets[0], theElemSets[1] );
4458 //=======================================================================
4460 * \brief Create elements by sweeping an element
4461 * \param elem - element to sweep
4462 * \param newNodesItVec - nodes generated from each node of the element
4463 * \param newElems - generated elements
4464 * \param nbSteps - number of sweeping steps
4465 * \param srcElements - to append elem for each generated element
4467 //=======================================================================
4469 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4470 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4471 list<const SMDS_MeshElement*>& newElems,
4472 const size_t nbSteps,
4473 SMESH_SequenceOfElemPtr& srcElements)
4475 SMESHDS_Mesh* aMesh = GetMeshDS();
4477 const int nbNodes = elem->NbNodes();
4478 const int nbCorners = elem->NbCornerNodes();
4479 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4480 polyhedron creation !!! */
4481 // Loop on elem nodes:
4482 // find new nodes and detect same nodes indices
4483 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4484 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4485 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4486 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4488 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4489 vector<int> sames(nbNodes);
4490 vector<bool> isSingleNode(nbNodes);
4492 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4493 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4494 const SMDS_MeshNode* node = nnIt->first;
4495 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4496 if ( listNewNodes.empty() )
4499 itNN [ iNode ] = listNewNodes.begin();
4500 prevNod[ iNode ] = node;
4501 nextNod[ iNode ] = listNewNodes.front();
4503 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4504 corner node of linear */
4505 if ( prevNod[ iNode ] != nextNod [ iNode ])
4506 nbDouble += !isSingleNode[iNode];
4508 if( iNode < nbCorners ) { // check corners only
4509 if ( prevNod[ iNode ] == nextNod [ iNode ])
4510 sames[nbSame++] = iNode;
4512 iNotSameNode = iNode;
4516 if ( nbSame == nbNodes || nbSame > 2) {
4517 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4521 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4523 // fix nodes order to have bottom normal external
4524 if ( baseType == SMDSEntity_Polygon )
4526 std::reverse( itNN.begin(), itNN.end() );
4527 std::reverse( prevNod.begin(), prevNod.end() );
4528 std::reverse( midlNod.begin(), midlNod.end() );
4529 std::reverse( nextNod.begin(), nextNod.end() );
4530 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4534 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4535 SMDS_MeshCell::applyInterlace( ind, itNN );
4536 SMDS_MeshCell::applyInterlace( ind, prevNod );
4537 SMDS_MeshCell::applyInterlace( ind, nextNod );
4538 SMDS_MeshCell::applyInterlace( ind, midlNod );
4539 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4542 sames[nbSame] = iNotSameNode;
4543 for ( int j = 0; j <= nbSame; ++j )
4544 for ( size_t i = 0; i < ind.size(); ++i )
4545 if ( ind[i] == sames[j] )
4550 iNotSameNode = sames[nbSame];
4554 else if ( elem->GetType() == SMDSAbs_Edge )
4556 // orient a new face same as adjacent one
4558 const SMDS_MeshElement* e;
4559 TIDSortedElemSet dummy;
4560 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4561 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4562 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4564 // there is an adjacent face, check order of nodes in it
4565 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4568 std::swap( itNN[0], itNN[1] );
4569 std::swap( prevNod[0], prevNod[1] );
4570 std::swap( nextNod[0], nextNod[1] );
4571 #if defined(__APPLE__)
4572 std::swap( isSingleNode[0], isSingleNode[1] );
4574 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,
5485 const std::list<double>& theScales,
5486 const gp_XYZ* theBasePoint,
5488 const double theTolerance):
5490 myBaseP( Precision::Infinite(), 0, 0 ),
5491 myFlags( theFlags ),
5492 myTolerance( theTolerance ),
5493 myElemsToUse( NULL )
5495 mySteps = new TColStd_HSequenceOfReal;
5496 const double stepSize = theStep.Magnitude();
5497 for (int i=1; i<=theNbSteps; i++ )
5498 mySteps->Append( stepSize );
5500 int nbScales = theScales.size();
5503 if ( IsLinearVariation() && nbScales < theNbSteps )
5505 myScales.reserve( theNbSteps );
5506 std::list<double>::const_iterator scale = theScales.begin();
5507 double prevScale = 1.0;
5508 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5510 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5511 int stDelta = Max( 1, iStep - myScales.size());
5512 double scDelta = ( *scale - prevScale ) / stDelta;
5513 for ( int iStep = 0; iStep < stDelta; ++iStep )
5515 myScales.push_back( prevScale + scDelta );
5516 prevScale = myScales.back();
5523 myScales.assign( theScales.begin(), theScales.end() );
5528 myBaseP = *theBasePoint;
5531 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5532 ( theTolerance > 0 ))
5534 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5538 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5542 //=======================================================================
5543 //function : ExtrusParam
5544 //purpose : steps are given explicitly
5545 //=======================================================================
5547 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5548 Handle(TColStd_HSequenceOfReal) theSteps,
5550 const double theTolerance):
5552 mySteps( theSteps ),
5553 myFlags( theFlags ),
5554 myTolerance( theTolerance ),
5555 myElemsToUse( NULL )
5557 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5558 ( theTolerance > 0 ))
5560 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5564 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5568 //=======================================================================
5569 //function : ExtrusParam
5570 //purpose : for extrusion by normal
5571 //=======================================================================
5573 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5574 const int theNbSteps,
5578 mySteps( new TColStd_HSequenceOfReal ),
5579 myFlags( theFlags ),
5581 myElemsToUse( NULL )
5583 for (int i = 0; i < theNbSteps; i++ )
5584 mySteps->Append( theStepSize );
5588 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5592 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5596 //=======================================================================
5597 //function : ExtrusParam::SetElementsToUse
5598 //purpose : stores elements to use for extrusion by normal, depending on
5599 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5600 // define myBaseP for scaling
5601 //=======================================================================
5603 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5604 const TIDSortedElemSet& nodes )
5606 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5608 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5610 myBaseP.SetCoord( 0.,0.,0. );
5611 TIDSortedElemSet newNodes;
5613 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5614 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5616 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5617 TIDSortedElemSet::const_iterator itElem = elements.begin();
5618 for ( ; itElem != elements.end(); itElem++ )
5620 const SMDS_MeshElement* elem = *itElem;
5621 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5622 while ( itN->more() ) {
5623 const SMDS_MeshElement* node = itN->next();
5624 if ( newNodes.insert( node ).second )
5625 myBaseP += SMESH_TNodeXYZ( node );
5629 myBaseP /= newNodes.size();
5633 //=======================================================================
5634 //function : ExtrusParam::beginStepIter
5635 //purpose : prepare iteration on steps
5636 //=======================================================================
5638 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5640 myWithMediumNodes = withMediumNodes;
5644 //=======================================================================
5645 //function : ExtrusParam::moreSteps
5646 //purpose : are there more steps?
5647 //=======================================================================
5649 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5651 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5653 //=======================================================================
5654 //function : ExtrusParam::nextStep
5655 //purpose : returns the next step
5656 //=======================================================================
5658 double SMESH_MeshEditor::ExtrusParam::nextStep()
5661 if ( !myCurSteps.empty() )
5663 res = myCurSteps.back();
5664 myCurSteps.pop_back();
5666 else if ( myNextStep <= mySteps->Length() )
5668 myCurSteps.push_back( mySteps->Value( myNextStep ));
5670 if ( myWithMediumNodes )
5672 myCurSteps.back() /= 2.;
5673 myCurSteps.push_back( myCurSteps.back() );
5680 //=======================================================================
5681 //function : ExtrusParam::makeNodesByDir
5682 //purpose : create nodes for standard extrusion
5683 //=======================================================================
5685 int SMESH_MeshEditor::ExtrusParam::
5686 makeNodesByDir( SMESHDS_Mesh* mesh,
5687 const SMDS_MeshNode* srcNode,
5688 std::list<const SMDS_MeshNode*> & newNodes,
5689 const bool makeMediumNodes)
5691 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5694 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5696 p += myDir.XYZ() * nextStep();
5697 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5698 newNodes.push_back( newNode );
5701 if ( !myScales.empty() )
5703 if ( makeMediumNodes && myMediumScales.empty() )
5705 myMediumScales.resize( myScales.size() );
5706 double prevFactor = 1.;
5707 for ( size_t i = 0; i < myScales.size(); ++i )
5709 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5710 prevFactor = myScales[i];
5713 typedef std::vector<double>::iterator ScaleIt;
5714 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5716 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5718 gp_XYZ center = myBaseP;
5719 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5721 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5723 center += myDir.XYZ() * nextStep();
5725 iSc += int( makeMediumNodes );
5726 ScaleIt& scale = scales[ iSc % 2 ];
5728 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5729 xyz = ( *scale * ( xyz - center )) + center;
5730 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5738 //=======================================================================
5739 //function : ExtrusParam::makeNodesByDirAndSew
5740 //purpose : create nodes for standard extrusion with sewing
5741 //=======================================================================
5743 int SMESH_MeshEditor::ExtrusParam::
5744 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5745 const SMDS_MeshNode* srcNode,
5746 std::list<const SMDS_MeshNode*> & newNodes,
5747 const bool makeMediumNodes)
5749 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5752 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5754 P1 += myDir.XYZ() * nextStep();
5756 // try to search in sequence of existing nodes
5757 // if myNodes.Length()>0 we 'nave to use given sequence
5758 // else - use all nodes of mesh
5759 const SMDS_MeshNode * node = 0;
5760 if ( myNodes.Length() > 0 ) {
5762 for(i=1; i<=myNodes.Length(); i++) {
5763 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5764 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5766 node = myNodes.Value(i);
5772 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5773 while(itn->more()) {
5774 SMESH_TNodeXYZ P2( itn->next() );
5775 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5784 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5786 newNodes.push_back( node );
5793 //=======================================================================
5794 //function : ExtrusParam::makeNodesByNormal2D
5795 //purpose : create nodes for extrusion using normals of faces
5796 //=======================================================================
5798 int SMESH_MeshEditor::ExtrusParam::
5799 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5800 const SMDS_MeshNode* srcNode,
5801 std::list<const SMDS_MeshNode*> & newNodes,
5802 const bool makeMediumNodes)
5804 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5806 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5808 // get normals to faces sharing srcNode
5809 vector< gp_XYZ > norms, baryCenters;
5810 gp_XYZ norm, avgNorm( 0,0,0 );
5811 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5812 while ( faceIt->more() )
5814 const SMDS_MeshElement* face = faceIt->next();
5815 if ( myElemsToUse && !myElemsToUse->count( face ))
5817 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5819 norms.push_back( norm );
5821 if ( !alongAvgNorm )
5825 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5826 bc += SMESH_TNodeXYZ( nIt->next() );
5827 baryCenters.push_back( bc / nbN );
5832 if ( norms.empty() ) return 0;
5834 double normSize = avgNorm.Modulus();
5835 if ( normSize < std::numeric_limits<double>::min() )
5838 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5841 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5844 avgNorm /= normSize;
5847 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5850 double stepSize = nextStep();
5852 if ( norms.size() > 1 )
5854 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5856 // translate plane of a face
5857 baryCenters[ iF ] += norms[ iF ] * stepSize;
5859 // find point of intersection of the face plane located at baryCenters[ iF ]
5860 // and avgNorm located at pNew
5861 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5862 double dot = ( norms[ iF ] * avgNorm );
5863 if ( dot < std::numeric_limits<double>::min() )
5864 dot = stepSize * 1e-3;
5865 double step = -( norms[ iF ] * pNew + d ) / dot;
5866 pNew += step * avgNorm;
5871 pNew += stepSize * avgNorm;
5875 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5876 newNodes.push_back( newNode );
5881 //=======================================================================
5882 //function : ExtrusParam::makeNodesByNormal1D
5883 //purpose : create nodes for extrusion using normals of edges
5884 //=======================================================================
5886 int SMESH_MeshEditor::ExtrusParam::
5887 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5888 const SMDS_MeshNode* srcNode,
5889 std::list<const SMDS_MeshNode*> & newNodes,
5890 const bool makeMediumNodes)
5892 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5896 //=======================================================================
5897 //function : ExtrusionSweep
5899 //=======================================================================
5901 SMESH_MeshEditor::PGroupIDs
5902 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5903 const gp_Vec& theStep,
5904 const int theNbSteps,
5905 TTElemOfElemListMap& newElemsMap,
5907 const double theTolerance)
5909 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5910 return ExtrusionSweep( theElems, aParams, newElemsMap );
5914 //=======================================================================
5915 //function : ExtrusionSweep
5917 //=======================================================================
5919 SMESH_MeshEditor::PGroupIDs
5920 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5921 ExtrusParam& theParams,
5922 TTElemOfElemListMap& newElemsMap)
5924 myLastCreatedElems.Clear();
5925 myLastCreatedNodes.Clear();
5927 // source elements for each generated one
5928 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5930 setElemsFirst( theElemSets );
5931 const int nbSteps = theParams.NbSteps();
5932 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5934 TNodeOfNodeListMap mapNewNodes;
5935 TElemOfVecOfNnlmiMap mapElemNewNodes;
5937 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5938 myMesh->NbFaces(ORDER_QUADRATIC) +
5939 myMesh->NbVolumes(ORDER_QUADRATIC) );
5941 TIDSortedElemSet::iterator itElem;
5942 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5944 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5945 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5947 // check element type
5948 const SMDS_MeshElement* elem = *itElem;
5949 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5952 const size_t nbNodes = elem->NbNodes();
5953 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5954 newNodesItVec.reserve( nbNodes );
5956 // loop on elem nodes
5957 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5958 while ( itN->more() )
5960 // check if a node has been already sweeped
5961 const SMDS_MeshNode* node = cast2Node( itN->next() );
5962 TNodeOfNodeListMap::iterator nIt =
5963 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5964 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5965 if ( listNewNodes.empty() )
5969 // check if we are to create medium nodes between corner ones
5970 bool needMediumNodes = false;
5971 if ( isQuadraticMesh )
5973 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5974 while (it->more() && !needMediumNodes )
5976 const SMDS_MeshElement* invElem = it->next();
5977 if ( invElem != elem && !theElems.count( invElem )) continue;
5978 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5979 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5980 needMediumNodes = true;
5983 // create nodes for all steps
5984 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5986 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5987 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5989 myLastCreatedNodes.Append( *newNodesIt );
5990 srcNodes.Append( node );
5995 break; // newNodesItVec will be shorter than nbNodes
5998 newNodesItVec.push_back( nIt );
6000 // make new elements
6001 if ( newNodesItVec.size() == nbNodes )
6002 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6006 if ( theParams.ToMakeBoundary() ) {
6007 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6009 PGroupIDs newGroupIDs;
6010 if ( theParams.ToMakeGroups() )
6011 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6016 //=======================================================================
6017 //function : ExtrusionAlongTrack
6019 //=======================================================================
6020 SMESH_MeshEditor::Extrusion_Error
6021 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6022 SMESH_subMesh* theTrack,
6023 const SMDS_MeshNode* theN1,
6024 const bool theHasAngles,
6025 list<double>& theAngles,
6026 const bool theLinearVariation,
6027 const bool theHasRefPoint,
6028 const gp_Pnt& theRefPoint,
6029 const bool theMakeGroups)
6031 myLastCreatedElems.Clear();
6032 myLastCreatedNodes.Clear();
6035 std::list<double> aPrms;
6036 TIDSortedElemSet::iterator itElem;
6039 TopoDS_Edge aTrackEdge;
6040 TopoDS_Vertex aV1, aV2;
6042 SMDS_ElemIteratorPtr aItE;
6043 SMDS_NodeIteratorPtr aItN;
6044 SMDSAbs_ElementType aTypeE;
6046 TNodeOfNodeListMap mapNewNodes;
6049 aNbE = theElements[0].size() + theElements[1].size();
6052 return EXTR_NO_ELEMENTS;
6054 // 1.1 Track Pattern
6057 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6059 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6060 theHasAngles, theAngles, theLinearVariation,
6061 theHasRefPoint, theRefPoint, theMakeGroups );
6063 aItE = pSubMeshDS->GetElements();
6064 while ( aItE->more() ) {
6065 const SMDS_MeshElement* pE = aItE->next();
6066 aTypeE = pE->GetType();
6067 // Pattern must contain links only
6068 if ( aTypeE != SMDSAbs_Edge )
6069 return EXTR_PATH_NOT_EDGE;
6072 list<SMESH_MeshEditor_PathPoint> fullList;
6074 const TopoDS_Shape& aS = theTrack->GetSubShape();
6075 // Sub-shape for the Pattern must be an Edge or Wire
6076 if( aS.ShapeType() == TopAbs_EDGE ) {
6077 aTrackEdge = TopoDS::Edge( aS );
6078 // the Edge must not be degenerated
6079 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6080 return EXTR_BAD_PATH_SHAPE;
6081 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6082 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6083 const SMDS_MeshNode* aN1 = aItN->next();
6084 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6085 const SMDS_MeshNode* aN2 = aItN->next();
6086 // starting node must be aN1 or aN2
6087 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6088 return EXTR_BAD_STARTING_NODE;
6089 aItN = pSubMeshDS->GetNodes();
6090 while ( aItN->more() ) {
6091 const SMDS_MeshNode* pNode = aItN->next();
6092 const SMDS_EdgePosition* pEPos =
6093 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6094 double aT = pEPos->GetUParameter();
6095 aPrms.push_back( aT );
6097 //Extrusion_Error err =
6098 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6099 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6100 list< SMESH_subMesh* > LSM;
6101 TopTools_SequenceOfShape Edges;
6102 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6103 while(itSM->more()) {
6104 SMESH_subMesh* SM = itSM->next();
6106 const TopoDS_Shape& aS = SM->GetSubShape();
6109 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6110 int startNid = theN1->GetID();
6111 TColStd_MapOfInteger UsedNums;
6113 int NbEdges = Edges.Length();
6115 for(; i<=NbEdges; i++) {
6117 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6118 for(; itLSM!=LSM.end(); itLSM++) {
6120 if(UsedNums.Contains(k)) continue;
6121 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6122 SMESH_subMesh* locTrack = *itLSM;
6123 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6124 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6125 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6126 const SMDS_MeshNode* aN1 = aItN->next();
6127 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6128 const SMDS_MeshNode* aN2 = aItN->next();
6129 // starting node must be aN1 or aN2
6130 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6131 // 2. Collect parameters on the track edge
6133 aItN = locMeshDS->GetNodes();
6134 while ( aItN->more() ) {
6135 const SMDS_MeshNode* pNode = aItN->next();
6136 const SMDS_EdgePosition* pEPos =
6137 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6138 double aT = pEPos->GetUParameter();
6139 aPrms.push_back( aT );
6141 list<SMESH_MeshEditor_PathPoint> LPP;
6142 //Extrusion_Error err =
6143 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6144 LLPPs.push_back(LPP);
6146 // update startN for search following egde
6147 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6148 else startNid = aN1->GetID();
6152 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6153 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6154 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6155 for(; itPP!=firstList.end(); itPP++) {
6156 fullList.push_back( *itPP );
6158 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6159 fullList.pop_back();
6161 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6162 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6163 itPP = currList.begin();
6164 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6165 gp_Dir D1 = PP1.Tangent();
6166 gp_Dir D2 = PP2.Tangent();
6167 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6168 (D1.Z()+D2.Z())/2 ) );
6169 PP1.SetTangent(Dnew);
6170 fullList.push_back(PP1);
6172 for(; itPP!=firstList.end(); itPP++) {
6173 fullList.push_back( *itPP );
6175 PP1 = fullList.back();
6176 fullList.pop_back();
6178 // if wire not closed
6179 fullList.push_back(PP1);
6183 return EXTR_BAD_PATH_SHAPE;
6186 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6187 theHasRefPoint, theRefPoint, theMakeGroups);
6191 //=======================================================================
6192 //function : ExtrusionAlongTrack
6194 //=======================================================================
6195 SMESH_MeshEditor::Extrusion_Error
6196 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6197 SMESH_Mesh* theTrack,
6198 const SMDS_MeshNode* theN1,
6199 const bool theHasAngles,
6200 list<double>& theAngles,
6201 const bool theLinearVariation,
6202 const bool theHasRefPoint,
6203 const gp_Pnt& theRefPoint,
6204 const bool theMakeGroups)
6206 myLastCreatedElems.Clear();
6207 myLastCreatedNodes.Clear();
6210 std::list<double> aPrms;
6211 TIDSortedElemSet::iterator itElem;
6214 TopoDS_Edge aTrackEdge;
6215 TopoDS_Vertex aV1, aV2;
6217 SMDS_ElemIteratorPtr aItE;
6218 SMDS_NodeIteratorPtr aItN;
6219 SMDSAbs_ElementType aTypeE;
6221 TNodeOfNodeListMap mapNewNodes;
6224 aNbE = theElements[0].size() + theElements[1].size();
6227 return EXTR_NO_ELEMENTS;
6229 // 1.1 Track Pattern
6232 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6234 aItE = pMeshDS->elementsIterator();
6235 while ( aItE->more() ) {
6236 const SMDS_MeshElement* pE = aItE->next();
6237 aTypeE = pE->GetType();
6238 // Pattern must contain links only
6239 if ( aTypeE != SMDSAbs_Edge )
6240 return EXTR_PATH_NOT_EDGE;
6243 list<SMESH_MeshEditor_PathPoint> fullList;
6245 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6247 if ( !theTrack->HasShapeToMesh() ) {
6248 //Mesh without shape
6249 const SMDS_MeshNode* currentNode = NULL;
6250 const SMDS_MeshNode* prevNode = theN1;
6251 std::vector<const SMDS_MeshNode*> aNodesList;
6252 aNodesList.push_back(theN1);
6253 int nbEdges = 0, conn=0;
6254 const SMDS_MeshElement* prevElem = NULL;
6255 const SMDS_MeshElement* currentElem = NULL;
6256 int totalNbEdges = theTrack->NbEdges();
6257 SMDS_ElemIteratorPtr nIt;
6260 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6261 return EXTR_BAD_STARTING_NODE;
6264 conn = nbEdgeConnectivity(theN1);
6266 return EXTR_PATH_NOT_EDGE;
6268 aItE = theN1->GetInverseElementIterator();
6269 prevElem = aItE->next();
6270 currentElem = prevElem;
6272 if(totalNbEdges == 1 ) {
6273 nIt = currentElem->nodesIterator();
6274 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6275 if(currentNode == prevNode)
6276 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6277 aNodesList.push_back(currentNode);
6279 nIt = currentElem->nodesIterator();
6280 while( nIt->more() ) {
6281 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6282 if(currentNode == prevNode)
6283 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6284 aNodesList.push_back(currentNode);
6286 //case of the closed mesh
6287 if(currentNode == theN1) {
6292 conn = nbEdgeConnectivity(currentNode);
6294 return EXTR_PATH_NOT_EDGE;
6295 }else if( conn == 1 && nbEdges > 0 ) {
6300 prevNode = currentNode;
6301 aItE = currentNode->GetInverseElementIterator();
6302 currentElem = aItE->next();
6303 if( currentElem == prevElem)
6304 currentElem = aItE->next();
6305 nIt = currentElem->nodesIterator();
6306 prevElem = currentElem;
6312 if(nbEdges != totalNbEdges)
6313 return EXTR_PATH_NOT_EDGE;
6315 TopTools_SequenceOfShape Edges;
6316 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6317 int startNid = theN1->GetID();
6318 for ( size_t i = 1; i < aNodesList.size(); i++ )
6320 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6321 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6322 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6323 list<SMESH_MeshEditor_PathPoint> LPP;
6325 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6326 LLPPs.push_back(LPP);
6327 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6328 else startNid = aNodesList[i-1]->GetID();
6331 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6332 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6333 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6334 for(; itPP!=firstList.end(); itPP++) {
6335 fullList.push_back( *itPP );
6338 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6339 SMESH_MeshEditor_PathPoint PP2;
6340 fullList.pop_back();
6342 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6343 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6344 itPP = currList.begin();
6345 PP2 = currList.front();
6346 gp_Dir D1 = PP1.Tangent();
6347 gp_Dir D2 = PP2.Tangent();
6348 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6349 PP1.SetTangent(Dnew);
6350 fullList.push_back(PP1);
6352 for(; itPP!=currList.end(); itPP++) {
6353 fullList.push_back( *itPP );
6355 PP1 = fullList.back();
6356 fullList.pop_back();
6358 fullList.push_back(PP1);
6360 } // Sub-shape for the Pattern must be an Edge or Wire
6361 else if ( aS.ShapeType() == TopAbs_EDGE )
6363 aTrackEdge = TopoDS::Edge( aS );
6364 // the Edge must not be degenerated
6365 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6366 return EXTR_BAD_PATH_SHAPE;
6367 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6368 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6369 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6370 // starting node must be aN1 or aN2
6371 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6372 return EXTR_BAD_STARTING_NODE;
6373 aItN = pMeshDS->nodesIterator();
6374 while ( aItN->more() ) {
6375 const SMDS_MeshNode* pNode = aItN->next();
6376 if( pNode==aN1 || pNode==aN2 ) continue;
6377 const SMDS_EdgePosition* pEPos =
6378 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6379 double aT = pEPos->GetUParameter();
6380 aPrms.push_back( aT );
6382 //Extrusion_Error err =
6383 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6385 else if( aS.ShapeType() == TopAbs_WIRE ) {
6386 list< SMESH_subMesh* > LSM;
6387 TopTools_SequenceOfShape Edges;
6388 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6389 for(; eExp.More(); eExp.Next()) {
6390 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6391 if( SMESH_Algo::isDegenerated(E) ) continue;
6392 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6398 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6399 TopoDS_Vertex aVprev;
6400 TColStd_MapOfInteger UsedNums;
6401 int NbEdges = Edges.Length();
6403 for(; i<=NbEdges; i++) {
6405 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6406 for(; itLSM!=LSM.end(); itLSM++) {
6408 if(UsedNums.Contains(k)) continue;
6409 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6410 SMESH_subMesh* locTrack = *itLSM;
6411 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6412 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6413 bool aN1isOK = false, aN2isOK = false;
6414 if ( aVprev.IsNull() ) {
6415 // if previous vertex is not yet defined, it means that we in the beginning of wire
6416 // and we have to find initial vertex corresponding to starting node theN1
6417 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6418 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6419 // starting node must be aN1 or aN2
6420 aN1isOK = ( aN1 && aN1 == theN1 );
6421 aN2isOK = ( aN2 && aN2 == theN1 );
6424 // we have specified ending vertex of the previous edge on the previous iteration
6425 // and we have just to check that it corresponds to any vertex in current segment
6426 aN1isOK = aVprev.IsSame( aV1 );
6427 aN2isOK = aVprev.IsSame( aV2 );
6429 if ( !aN1isOK && !aN2isOK ) continue;
6430 // 2. Collect parameters on the track edge
6432 aItN = locMeshDS->GetNodes();
6433 while ( aItN->more() ) {
6434 const SMDS_MeshNode* pNode = aItN->next();
6435 const SMDS_EdgePosition* pEPos =
6436 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6437 double aT = pEPos->GetUParameter();
6438 aPrms.push_back( aT );
6440 list<SMESH_MeshEditor_PathPoint> LPP;
6441 //Extrusion_Error err =
6442 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6443 LLPPs.push_back(LPP);
6445 // update startN for search following egde
6446 if ( aN1isOK ) aVprev = aV2;
6451 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6452 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6453 fullList.splice( fullList.end(), firstList );
6455 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6456 fullList.pop_back();
6458 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6459 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6460 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6461 gp_Dir D1 = PP1.Tangent();
6462 gp_Dir D2 = PP2.Tangent();
6463 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6464 PP1.SetTangent(Dnew);
6465 fullList.push_back(PP1);
6466 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6467 PP1 = fullList.back();
6468 fullList.pop_back();
6470 // if wire not closed
6471 fullList.push_back(PP1);
6475 return EXTR_BAD_PATH_SHAPE;
6478 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6479 theHasRefPoint, theRefPoint, theMakeGroups);
6483 //=======================================================================
6484 //function : makeEdgePathPoints
6485 //purpose : auxiliary for ExtrusionAlongTrack
6486 //=======================================================================
6487 SMESH_MeshEditor::Extrusion_Error
6488 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6489 const TopoDS_Edge& aTrackEdge,
6491 list<SMESH_MeshEditor_PathPoint>& LPP)
6493 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6495 aTolVec2=aTolVec*aTolVec;
6497 TopoDS_Vertex aV1, aV2;
6498 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6499 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6500 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6501 // 2. Collect parameters on the track edge
6502 aPrms.push_front( aT1 );
6503 aPrms.push_back( aT2 );
6506 if( FirstIsStart ) {
6517 SMESH_MeshEditor_PathPoint aPP;
6518 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6519 std::list<double>::iterator aItD = aPrms.begin();
6520 for(; aItD != aPrms.end(); ++aItD) {
6524 aC3D->D1( aT, aP3D, aVec );
6525 aL2 = aVec.SquareMagnitude();
6526 if ( aL2 < aTolVec2 )
6527 return EXTR_CANT_GET_TANGENT;
6528 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6530 aPP.SetTangent( aTgt );
6531 aPP.SetParameter( aT );
6538 //=======================================================================
6539 //function : makeExtrElements
6540 //purpose : auxiliary for ExtrusionAlongTrack
6541 //=======================================================================
6542 SMESH_MeshEditor::Extrusion_Error
6543 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6544 list<SMESH_MeshEditor_PathPoint>& fullList,
6545 const bool theHasAngles,
6546 list<double>& theAngles,
6547 const bool theLinearVariation,
6548 const bool theHasRefPoint,
6549 const gp_Pnt& theRefPoint,
6550 const bool theMakeGroups)
6552 const int aNbTP = fullList.size();
6555 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6556 linearAngleVariation(aNbTP-1, theAngles);
6558 // fill vector of path points with angles
6559 vector<SMESH_MeshEditor_PathPoint> aPPs;
6560 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6561 list<double>::iterator itAngles = theAngles.begin();
6562 aPPs.push_back( *itPP++ );
6563 for( ; itPP != fullList.end(); itPP++) {
6564 aPPs.push_back( *itPP );
6565 if ( theHasAngles && itAngles != theAngles.end() )
6566 aPPs.back().SetAngle( *itAngles++ );
6569 TNodeOfNodeListMap mapNewNodes;
6570 TElemOfVecOfNnlmiMap mapElemNewNodes;
6571 TTElemOfElemListMap newElemsMap;
6572 TIDSortedElemSet::iterator itElem;
6573 // source elements for each generated one
6574 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6576 // 3. Center of rotation aV0
6577 gp_Pnt aV0 = theRefPoint;
6578 if ( !theHasRefPoint )
6580 gp_XYZ aGC( 0.,0.,0. );
6581 TIDSortedElemSet newNodes;
6583 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6585 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6586 itElem = theElements.begin();
6587 for ( ; itElem != theElements.end(); itElem++ )
6589 const SMDS_MeshElement* elem = *itElem;
6590 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6591 while ( itN->more() ) {
6592 const SMDS_MeshElement* node = itN->next();
6593 if ( newNodes.insert( node ).second )
6594 aGC += SMESH_TNodeXYZ( node );
6598 aGC /= newNodes.size();
6600 } // if (!theHasRefPoint) {
6602 // 4. Processing the elements
6603 SMESHDS_Mesh* aMesh = GetMeshDS();
6604 list<const SMDS_MeshNode*> emptyList;
6606 setElemsFirst( theElemSets );
6607 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6609 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6610 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6612 const SMDS_MeshElement* elem = *itElem;
6614 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6615 newNodesItVec.reserve( elem->NbNodes() );
6617 // loop on elem nodes
6619 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6620 while ( itN->more() )
6623 // check if a node has been already processed
6624 const SMDS_MeshNode* node = cast2Node( itN->next() );
6625 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6626 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6627 if ( listNewNodes.empty() )
6630 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6631 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6632 gp_Ax1 anAx1, anAxT1T0;
6633 gp_Dir aDT1x, aDT0x, aDT1T0;
6638 aPN0 = SMESH_TNodeXYZ( node );
6640 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6642 aDT0x= aPP0.Tangent();
6644 for ( int j = 1; j < aNbTP; ++j ) {
6645 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6647 aDT1x = aPP1.Tangent();
6648 aAngle1x = aPP1.Angle();
6650 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6652 gp_Vec aV01x( aP0x, aP1x );
6653 aTrsf.SetTranslation( aV01x );
6656 aV1x = aV0x.Transformed( aTrsf );
6657 aPN1 = aPN0.Transformed( aTrsf );
6659 // rotation 1 [ T1,T0 ]
6660 aAngleT1T0=-aDT1x.Angle( aDT0x );
6661 if (fabs(aAngleT1T0) > aTolAng)
6664 anAxT1T0.SetLocation( aV1x );
6665 anAxT1T0.SetDirection( aDT1T0 );
6666 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6668 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6672 if ( theHasAngles ) {
6673 anAx1.SetLocation( aV1x );
6674 anAx1.SetDirection( aDT1x );
6675 aTrsfRot.SetRotation( anAx1, aAngle1x );
6677 aPN1 = aPN1.Transformed( aTrsfRot );
6681 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6683 // create additional node
6684 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6685 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6686 myLastCreatedNodes.Append(newNode);
6687 srcNodes.Append( node );
6688 listNewNodes.push_back( newNode );
6690 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6691 myLastCreatedNodes.Append(newNode);
6692 srcNodes.Append( node );
6693 listNewNodes.push_back( newNode );
6701 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6703 // if current elem is quadratic and current node is not medium
6704 // we have to check - may be it is needed to insert additional nodes
6705 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6706 if ((int) listNewNodes.size() == aNbTP-1 )
6708 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6709 gp_XYZ P(node->X(), node->Y(), node->Z());
6710 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6712 for(i=0; i<aNbTP-1; i++) {
6713 const SMDS_MeshNode* N = *it;
6714 double x = ( N->X() + P.X() )/2.;
6715 double y = ( N->Y() + P.Y() )/2.;
6716 double z = ( N->Z() + P.Z() )/2.;
6717 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6718 srcNodes.Append( node );
6719 myLastCreatedNodes.Append(newN);
6722 P = gp_XYZ(N->X(),N->Y(),N->Z());
6724 listNewNodes.clear();
6725 for(i=0; i<2*(aNbTP-1); i++) {
6726 listNewNodes.push_back(aNodes[i]);
6731 newNodesItVec.push_back( nIt );
6734 // make new elements
6735 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6739 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6741 if ( theMakeGroups )
6742 generateGroups( srcNodes, srcElems, "extruded");
6748 //=======================================================================
6749 //function : linearAngleVariation
6750 //purpose : spread values over nbSteps
6751 //=======================================================================
6753 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6754 list<double>& Angles)
6756 int nbAngles = Angles.size();
6757 if( nbSteps > nbAngles && nbAngles > 0 )
6759 vector<double> theAngles(nbAngles);
6760 theAngles.assign( Angles.begin(), Angles.end() );
6763 double rAn2St = double( nbAngles ) / double( nbSteps );
6764 double angPrev = 0, angle;
6765 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6767 double angCur = rAn2St * ( iSt+1 );
6768 double angCurFloor = floor( angCur );
6769 double angPrevFloor = floor( angPrev );
6770 if ( angPrevFloor == angCurFloor )
6771 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6773 int iP = int( angPrevFloor );
6774 double angPrevCeil = ceil(angPrev);
6775 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6777 int iC = int( angCurFloor );
6778 if ( iC < nbAngles )
6779 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6781 iP = int( angPrevCeil );
6783 angle += theAngles[ iC ];
6785 res.push_back(angle);
6793 //================================================================================
6795 * \brief Move or copy theElements applying theTrsf to their nodes
6796 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6797 * \param theTrsf - transformation to apply
6798 * \param theCopy - if true, create translated copies of theElems
6799 * \param theMakeGroups - if true and theCopy, create translated groups
6800 * \param theTargetMesh - mesh to copy translated elements into
6801 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6803 //================================================================================
6805 SMESH_MeshEditor::PGroupIDs
6806 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6807 const gp_Trsf& theTrsf,
6809 const bool theMakeGroups,
6810 SMESH_Mesh* theTargetMesh)
6812 myLastCreatedElems.Clear();
6813 myLastCreatedNodes.Clear();
6815 bool needReverse = false;
6816 string groupPostfix;
6817 switch ( theTrsf.Form() ) {
6820 groupPostfix = "mirrored";
6823 groupPostfix = "mirrored";
6827 groupPostfix = "mirrored";
6830 groupPostfix = "rotated";
6832 case gp_Translation:
6833 groupPostfix = "translated";
6836 groupPostfix = "scaled";
6838 case gp_CompoundTrsf: // different scale by axis
6839 groupPostfix = "scaled";
6842 needReverse = false;
6843 groupPostfix = "transformed";
6846 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6847 SMESHDS_Mesh* aMesh = GetMeshDS();
6849 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6850 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6851 SMESH_MeshEditor::ElemFeatures elemType;
6853 // map old node to new one
6854 TNodeNodeMap nodeMap;
6856 // elements sharing moved nodes; those of them which have all
6857 // nodes mirrored but are not in theElems are to be reversed
6858 TIDSortedElemSet inverseElemSet;
6860 // source elements for each generated one
6861 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6863 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6864 TIDSortedElemSet orphanNode;
6866 if ( theElems.empty() ) // transform the whole mesh
6869 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6870 while ( eIt->more() ) theElems.insert( eIt->next() );
6872 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6873 while ( nIt->more() )
6875 const SMDS_MeshNode* node = nIt->next();
6876 if ( node->NbInverseElements() == 0)
6877 orphanNode.insert( node );
6881 // loop on elements to transform nodes : first orphan nodes then elems
6882 TIDSortedElemSet::iterator itElem;
6883 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6884 for (int i=0; i<2; i++)
6885 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6887 const SMDS_MeshElement* elem = *itElem;
6891 // loop on elem nodes
6893 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6894 while ( itN->more() )
6896 const SMDS_MeshNode* node = cast2Node( itN->next() );
6897 // check if a node has been already transformed
6898 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6899 nodeMap.insert( make_pair ( node, node ));
6900 if ( !n2n_isnew.second )
6903 node->GetXYZ( coord );
6904 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6905 if ( theTargetMesh ) {
6906 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6907 n2n_isnew.first->second = newNode;
6908 myLastCreatedNodes.Append(newNode);
6909 srcNodes.Append( node );
6911 else if ( theCopy ) {
6912 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6913 n2n_isnew.first->second = newNode;
6914 myLastCreatedNodes.Append(newNode);
6915 srcNodes.Append( node );
6918 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6919 // node position on shape becomes invalid
6920 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6921 ( SMDS_SpacePosition::originSpacePosition() );
6924 // keep inverse elements
6925 if ( !theCopy && !theTargetMesh && needReverse ) {
6926 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6927 while ( invElemIt->more() ) {
6928 const SMDS_MeshElement* iel = invElemIt->next();
6929 inverseElemSet.insert( iel );
6933 } // loop on elems in { &orphanNode, &theElems };
6935 // either create new elements or reverse mirrored ones
6936 if ( !theCopy && !needReverse && !theTargetMesh )
6939 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6941 // Replicate or reverse elements
6943 std::vector<int> iForw;
6944 vector<const SMDS_MeshNode*> nodes;
6945 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6947 const SMDS_MeshElement* elem = *itElem;
6948 if ( !elem ) continue;
6950 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6951 size_t nbNodes = elem->NbNodes();
6952 if ( geomType == SMDSGeom_NONE ) continue; // node
6954 nodes.resize( nbNodes );
6956 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6958 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6962 bool allTransformed = true;
6963 int nbFaces = aPolyedre->NbFaces();
6964 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6966 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6967 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6969 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6970 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6971 if ( nodeMapIt == nodeMap.end() )
6972 allTransformed = false; // not all nodes transformed
6974 nodes.push_back((*nodeMapIt).second);
6976 if ( needReverse && allTransformed )
6977 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6979 if ( !allTransformed )
6980 continue; // not all nodes transformed
6982 else // ----------------------- the rest element types
6984 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6985 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6986 const vector<int>& i = needReverse ? iRev : iForw;
6988 // find transformed nodes
6990 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6991 while ( itN->more() ) {
6992 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6993 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6994 if ( nodeMapIt == nodeMap.end() )
6995 break; // not all nodes transformed
6996 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6998 if ( iNode != nbNodes )
6999 continue; // not all nodes transformed
7003 // copy in this or a new mesh
7004 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7005 srcElems.Append( elem );
7008 // reverse element as it was reversed by transformation
7010 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7013 } // loop on elements
7015 if ( editor && editor != this )
7016 myLastCreatedElems = editor->myLastCreatedElems;
7018 PGroupIDs newGroupIDs;
7020 if ( ( theMakeGroups && theCopy ) ||
7021 ( theMakeGroups && theTargetMesh ) )
7022 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7027 //=======================================================================
7029 * \brief Create groups of elements made during transformation
7030 * \param nodeGens - nodes making corresponding myLastCreatedNodes
7031 * \param elemGens - elements making corresponding myLastCreatedElems
7032 * \param postfix - to append to names of new groups
7033 * \param targetMesh - mesh to create groups in
7034 * \param topPresent - is there "top" elements that are created by sweeping
7036 //=======================================================================
7038 SMESH_MeshEditor::PGroupIDs
7039 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7040 const SMESH_SequenceOfElemPtr& elemGens,
7041 const std::string& postfix,
7042 SMESH_Mesh* targetMesh,
7043 const bool topPresent)
7045 PGroupIDs newGroupIDs( new list<int> );
7046 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7048 // Sort existing groups by types and collect their names
7050 // containers to store an old group and generated new ones;
7051 // 1st new group is for result elems of different type than a source one;
7052 // 2nd new group is for same type result elems ("top" group at extrusion)
7054 using boost::make_tuple;
7055 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7056 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7057 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7059 set< string > groupNames;
7061 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7062 if ( !groupIt->more() ) return newGroupIDs;
7064 int newGroupID = mesh->GetGroupIds().back()+1;
7065 while ( groupIt->more() )
7067 SMESH_Group * group = groupIt->next();
7068 if ( !group ) continue;
7069 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7070 if ( !groupDS || groupDS->IsEmpty() ) continue;
7071 groupNames.insert ( group->GetName() );
7072 groupDS->SetStoreName( group->GetName() );
7073 const SMDSAbs_ElementType type = groupDS->GetType();
7074 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7075 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7076 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7077 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7080 // Loop on nodes and elements to add them in new groups
7082 vector< const SMDS_MeshElement* > resultElems;
7083 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7085 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7086 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7087 if ( gens.Length() != elems.Length() )
7088 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7090 // loop on created elements
7091 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7093 const SMDS_MeshElement* sourceElem = gens( iElem );
7094 if ( !sourceElem ) {
7095 MESSAGE("generateGroups(): NULL source element");
7098 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7099 if ( groupsOldNew.empty() ) { // no groups of this type at all
7100 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7101 ++iElem; // skip all elements made by sourceElem
7104 // collect all elements made by the iElem-th sourceElem
7105 resultElems.clear();
7106 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7107 if ( resElem != sourceElem )
7108 resultElems.push_back( resElem );
7109 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7110 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7111 if ( resElem != sourceElem )
7112 resultElems.push_back( resElem );
7114 const SMDS_MeshElement* topElem = 0;
7115 if ( isNodes ) // there must be a top element
7117 topElem = resultElems.back();
7118 resultElems.pop_back();
7122 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7123 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7124 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7126 topElem = *resElemIt;
7127 *resElemIt = 0; // erase *resElemIt
7131 // add resultElems to groups originted from ones the sourceElem belongs to
7132 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7133 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7135 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7136 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7138 // fill in a new group
7139 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7140 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7141 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7143 newGroup.Add( *resElemIt );
7145 // fill a "top" group
7148 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7149 newTopGroup.Add( topElem );
7153 } // loop on created elements
7154 }// loop on nodes and elements
7156 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7158 list<int> topGrouIds;
7159 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7161 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7162 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7163 orderedOldNewGroups[i]->get<2>() };
7164 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7166 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7167 if ( newGroupDS->IsEmpty() )
7169 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7174 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7177 const bool isTop = ( topPresent &&
7178 newGroupDS->GetType() == oldGroupDS->GetType() &&
7181 string name = oldGroupDS->GetStoreName();
7182 { // remove trailing whitespaces (issue 22599)
7183 size_t size = name.size();
7184 while ( size > 1 && isspace( name[ size-1 ]))
7186 if ( size != name.size() )
7188 name.resize( size );
7189 oldGroupDS->SetStoreName( name.c_str() );
7192 if ( !targetMesh ) {
7193 string suffix = ( isTop ? "top": postfix.c_str() );
7197 while ( !groupNames.insert( name ).second ) // name exists
7198 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7203 newGroupDS->SetStoreName( name.c_str() );
7205 // make a SMESH_Groups
7206 mesh->AddGroup( newGroupDS );
7208 topGrouIds.push_back( newGroupDS->GetID() );
7210 newGroupIDs->push_back( newGroupDS->GetID() );
7214 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7219 //================================================================================
7221 * * \brief Return list of group of nodes close to each other within theTolerance
7222 * * Search among theNodes or in the whole mesh if theNodes is empty using
7223 * * an Octree algorithm
7224 * \param [in,out] theNodes - the nodes to treat
7225 * \param [in] theTolerance - the tolerance
7226 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7227 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7228 * corner and medium nodes in separate groups
7230 //================================================================================
7232 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7233 const double theTolerance,
7234 TListOfListOfNodes & theGroupsOfNodes,
7235 bool theSeparateCornersAndMedium)
7237 myLastCreatedElems.Clear();
7238 myLastCreatedNodes.Clear();
7240 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7241 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7242 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7243 theSeparateCornersAndMedium = false;
7245 TIDSortedNodeSet& corners = theNodes;
7246 TIDSortedNodeSet medium;
7248 if ( theNodes.empty() ) // get all nodes in the mesh
7250 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7251 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7252 if ( theSeparateCornersAndMedium )
7253 while ( nIt->more() )
7255 const SMDS_MeshNode* n = nIt->next();
7256 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7257 nodeSet->insert( nodeSet->end(), n );
7260 while ( nIt->more() )
7261 theNodes.insert( theNodes.end(), nIt->next() );
7263 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7265 TIDSortedNodeSet::iterator nIt = corners.begin();
7266 while ( nIt != corners.end() )
7267 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7269 medium.insert( medium.end(), *nIt );
7270 corners.erase( nIt++ );
7278 if ( !corners.empty() )
7279 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7280 if ( !medium.empty() )
7281 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7284 //=======================================================================
7285 //function : SimplifyFace
7286 //purpose : split a chain of nodes into several closed chains
7287 //=======================================================================
7289 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7290 vector<const SMDS_MeshNode *>& poly_nodes,
7291 vector<int>& quantities) const
7293 int nbNodes = faceNodes.size();
7294 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7298 size_t prevNbQuant = quantities.size();
7300 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7301 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7302 map< const SMDS_MeshNode*, int >::iterator nInd;
7304 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7305 simpleNodes.push_back( faceNodes[0] );
7306 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7308 if ( faceNodes[ iCur ] != simpleNodes.back() )
7310 int index = simpleNodes.size();
7311 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7312 int prevIndex = nInd->second;
7313 if ( prevIndex < index )
7316 int loopLen = index - prevIndex;
7319 // store the sub-loop
7320 quantities.push_back( loopLen );
7321 for ( int i = prevIndex; i < index; i++ )
7322 poly_nodes.push_back( simpleNodes[ i ]);
7324 simpleNodes.resize( prevIndex+1 );
7328 simpleNodes.push_back( faceNodes[ iCur ]);
7333 if ( simpleNodes.size() > 2 )
7335 quantities.push_back( simpleNodes.size() );
7336 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7339 return quantities.size() - prevNbQuant;
7342 //=======================================================================
7343 //function : MergeNodes
7344 //purpose : In each group, the cdr of nodes are substituted by the first one
7346 //=======================================================================
7348 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7349 const bool theAvoidMakingHoles)
7351 myLastCreatedElems.Clear();
7352 myLastCreatedNodes.Clear();
7354 SMESHDS_Mesh* mesh = GetMeshDS();
7356 TNodeNodeMap nodeNodeMap; // node to replace - new node
7357 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7358 list< int > rmElemIds, rmNodeIds;
7359 vector< ElemFeatures > newElemDefs;
7361 // Fill nodeNodeMap and elems
7363 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7364 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7366 list<const SMDS_MeshNode*>& nodes = *grIt;
7367 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7368 const SMDS_MeshNode* nToKeep = *nIt;
7369 for ( ++nIt; nIt != nodes.end(); nIt++ )
7371 const SMDS_MeshNode* nToRemove = *nIt;
7372 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7373 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7374 while ( invElemIt->more() ) {
7375 const SMDS_MeshElement* elem = invElemIt->next();
7381 // Apply recursive replacements (BUG 0020185)
7382 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7383 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7385 const SMDS_MeshNode* nToKeep = nnIt->second;
7386 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7387 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7388 nToKeep = nnIt_i->second;
7389 nnIt->second = nToKeep;
7392 if ( theAvoidMakingHoles )
7394 // find elements whose topology changes
7396 vector<const SMDS_MeshElement*> pbElems;
7397 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7398 for ( ; eIt != elems.end(); ++eIt )
7400 const SMDS_MeshElement* elem = *eIt;
7401 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7402 while ( itN->more() )
7404 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7405 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7406 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7408 // several nodes of elem stick
7409 pbElems.push_back( elem );
7414 // exclude from merge nodes causing spoiling element
7415 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7417 bool nodesExcluded = false;
7418 for ( size_t i = 0; i < pbElems.size(); ++i )
7420 size_t prevNbMergeNodes = nodeNodeMap.size();
7421 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7422 prevNbMergeNodes < nodeNodeMap.size() )
7423 nodesExcluded = true;
7425 if ( !nodesExcluded )
7430 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7432 const SMDS_MeshNode* nToRemove = nnIt->first;
7433 const SMDS_MeshNode* nToKeep = nnIt->second;
7434 if ( nToRemove != nToKeep )
7436 rmNodeIds.push_back( nToRemove->GetID() );
7437 AddToSameGroups( nToKeep, nToRemove, mesh );
7438 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7439 // w/o creating node in place of merged ones.
7440 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7441 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7442 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7443 sm->SetIsAlwaysComputed( true );
7447 // Change element nodes or remove an element
7449 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7450 for ( ; eIt != elems.end(); eIt++ )
7452 const SMDS_MeshElement* elem = *eIt;
7453 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7455 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7457 rmElemIds.push_back( elem->GetID() );
7459 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7461 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7462 & newElemDefs[i].myNodes[0],
7463 newElemDefs[i].myNodes.size() ))
7467 newElemDefs[i].SetID( elem->GetID() );
7468 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7469 if ( !keepElem ) rmElemIds.pop_back();
7473 newElemDefs[i].SetID( -1 );
7475 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7476 if ( sm && newElem )
7477 sm->AddElement( newElem );
7478 if ( elem != newElem )
7479 ReplaceElemInGroups( elem, newElem, mesh );
7484 // Remove bad elements, then equal nodes (order important)
7485 Remove( rmElemIds, false );
7486 Remove( rmNodeIds, true );
7491 //=======================================================================
7492 //function : applyMerge
7493 //purpose : Compute new connectivity of an element after merging nodes
7494 // \param [in] elems - the element
7495 // \param [out] newElemDefs - definition(s) of result element(s)
7496 // \param [inout] nodeNodeMap - nodes to merge
7497 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7498 // after merging (but not degenerated), removes nodes causing
7499 // the invalidity from \a nodeNodeMap.
7500 // \return bool - true if the element should be removed
7501 //=======================================================================
7503 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7504 vector< ElemFeatures >& newElemDefs,
7505 TNodeNodeMap& nodeNodeMap,
7506 const bool avoidMakingHoles )
7508 bool toRemove = false; // to remove elem
7509 int nbResElems = 1; // nb new elements
7511 newElemDefs.resize(nbResElems);
7512 newElemDefs[0].Init( elem );
7513 newElemDefs[0].myNodes.clear();
7515 set<const SMDS_MeshNode*> nodeSet;
7516 vector< const SMDS_MeshNode*> curNodes;
7517 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7520 const int nbNodes = elem->NbNodes();
7521 SMDSAbs_EntityType entity = elem->GetEntityType();
7523 curNodes.resize( nbNodes );
7524 uniqueNodes.resize( nbNodes );
7525 iRepl.resize( nbNodes );
7526 int iUnique = 0, iCur = 0, nbRepl = 0;
7528 // Get new seq of nodes
7530 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7531 while ( itN->more() )
7533 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7535 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7536 if ( nnIt != nodeNodeMap.end() ) {
7539 curNodes[ iCur ] = n;
7540 bool isUnique = nodeSet.insert( n ).second;
7542 uniqueNodes[ iUnique++ ] = n;
7544 iRepl[ nbRepl++ ] = iCur;
7548 // Analyse element topology after replacement
7550 int nbUniqueNodes = nodeSet.size();
7551 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7556 if ( elem->IsQuadratic() && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7558 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7559 int nbCorners = nbNodes / 2;
7560 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7562 int iPrev = ( iCur + 1 ) % nbCorners;
7563 if ( curNodes[ iCur ] == curNodes[ iPrev ] ) // corners stick
7565 int iMedium = iCur + nbCorners;
7566 vector< const SMDS_MeshNode* >::iterator i =
7567 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7569 curNodes[ iMedium ]);
7570 if ( i != uniqueNodes.end() )
7573 for ( ; i+1 != uniqueNodes.end(); ++i )
7582 case SMDSEntity_Polygon:
7583 case SMDSEntity_Quad_Polygon: // Polygon
7585 ElemFeatures* elemType = & newElemDefs[0];
7586 const bool isQuad = elemType->myIsQuad;
7588 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7589 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7591 // a polygon can divide into several elements
7592 vector<const SMDS_MeshNode *> polygons_nodes;
7593 vector<int> quantities;
7594 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7595 newElemDefs.resize( nbResElems );
7596 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7598 ElemFeatures* elemType = & newElemDefs[iface];
7599 if ( iface ) elemType->Init( elem );
7601 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7602 int nbNewNodes = quantities[iface];
7603 face_nodes.assign( polygons_nodes.begin() + inode,
7604 polygons_nodes.begin() + inode + nbNewNodes );
7605 inode += nbNewNodes;
7606 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7608 bool isValid = ( nbNewNodes % 2 == 0 );
7609 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7610 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7611 elemType->SetQuad( isValid );
7612 if ( isValid ) // put medium nodes after corners
7613 SMDS_MeshCell::applyInterlaceRev
7614 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7615 nbNewNodes ), face_nodes );
7617 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7619 nbUniqueNodes = newElemDefs[0].myNodes.size();
7623 case SMDSEntity_Polyhedra: // Polyhedral volume
7625 if ( nbUniqueNodes >= 4 )
7627 // each face has to be analyzed in order to check volume validity
7628 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7630 int nbFaces = aPolyedre->NbFaces();
7632 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7633 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7634 vector<const SMDS_MeshNode *> faceNodes;
7638 for (int iface = 1; iface <= nbFaces; iface++)
7640 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7641 faceNodes.resize( nbFaceNodes );
7642 for (int inode = 1; inode <= nbFaceNodes; inode++)
7644 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7645 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7646 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7647 faceNode = (*nnIt).second;
7648 faceNodes[inode - 1] = faceNode;
7650 SimplifyFace(faceNodes, poly_nodes, quantities);
7653 if ( quantities.size() > 3 )
7655 // TODO: remove coincident faces
7657 nbUniqueNodes = newElemDefs[0].myNodes.size();
7665 // TODO not all the possible cases are solved. Find something more generic?
7666 case SMDSEntity_Edge: //////// EDGE
7667 case SMDSEntity_Triangle: //// TRIANGLE
7668 case SMDSEntity_Quad_Triangle:
7669 case SMDSEntity_Tetra:
7670 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7674 case SMDSEntity_Quad_Edge:
7678 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7680 if ( nbUniqueNodes < 3 )
7682 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7683 toRemove = true; // opposite nodes stick
7688 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7697 if ( nbUniqueNodes == 6 &&
7699 ( nbRepl == 1 || iRepl[1] >= 4 ))
7705 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7714 if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7715 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7716 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7717 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7718 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7724 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7726 if ( nbUniqueNodes == 4 ) {
7727 // ---------------------------------> tetrahedron
7728 if ( curNodes[3] == curNodes[4] &&
7729 curNodes[3] == curNodes[5] ) {
7733 else if ( curNodes[0] == curNodes[1] &&
7734 curNodes[0] == curNodes[2] ) {
7735 // bottom nodes stick: set a top before
7736 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7737 uniqueNodes[ 0 ] = curNodes [ 5 ];
7738 uniqueNodes[ 1 ] = curNodes [ 4 ];
7739 uniqueNodes[ 2 ] = curNodes [ 3 ];
7742 else if (( curNodes[0] == curNodes[3] ) +
7743 ( curNodes[1] == curNodes[4] ) +
7744 ( curNodes[2] == curNodes[5] ) == 2 ) {
7745 // a lateral face turns into a line
7749 else if ( nbUniqueNodes == 5 ) {
7750 // PENTAHEDRON --------------------> pyramid
7751 if ( curNodes[0] == curNodes[3] )
7753 uniqueNodes[ 0 ] = curNodes[ 1 ];
7754 uniqueNodes[ 1 ] = curNodes[ 4 ];
7755 uniqueNodes[ 2 ] = curNodes[ 5 ];
7756 uniqueNodes[ 3 ] = curNodes[ 2 ];
7757 uniqueNodes[ 4 ] = curNodes[ 0 ];
7760 if ( curNodes[1] == curNodes[4] )
7762 uniqueNodes[ 0 ] = curNodes[ 0 ];
7763 uniqueNodes[ 1 ] = curNodes[ 2 ];
7764 uniqueNodes[ 2 ] = curNodes[ 5 ];
7765 uniqueNodes[ 3 ] = curNodes[ 3 ];
7766 uniqueNodes[ 4 ] = curNodes[ 1 ];
7769 if ( curNodes[2] == curNodes[5] )
7771 uniqueNodes[ 0 ] = curNodes[ 0 ];
7772 uniqueNodes[ 1 ] = curNodes[ 3 ];
7773 uniqueNodes[ 2 ] = curNodes[ 4 ];
7774 uniqueNodes[ 3 ] = curNodes[ 1 ];
7775 uniqueNodes[ 4 ] = curNodes[ 2 ];
7781 case SMDSEntity_Hexa:
7783 //////////////////////////////////// HEXAHEDRON
7784 SMDS_VolumeTool hexa (elem);
7785 hexa.SetExternalNormal();
7786 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7787 //////////////////////// HEX ---> tetrahedron
7788 for ( int iFace = 0; iFace < 6; iFace++ ) {
7789 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7790 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7791 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7792 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7793 // one face turns into a point ...
7794 int pickInd = ind[ 0 ];
7795 int iOppFace = hexa.GetOppFaceIndex( iFace );
7796 ind = hexa.GetFaceNodesIndices( iOppFace );
7798 uniqueNodes.clear();
7799 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7800 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7803 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7805 if ( nbStick == 1 ) {
7806 // ... and the opposite one - into a triangle.
7808 uniqueNodes.push_back( curNodes[ pickInd ]);
7815 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7816 //////////////////////// HEX ---> prism
7817 int nbTria = 0, iTria[3];
7818 const int *ind; // indices of face nodes
7819 // look for triangular faces
7820 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7821 ind = hexa.GetFaceNodesIndices( iFace );
7822 TIDSortedNodeSet faceNodes;
7823 for ( iCur = 0; iCur < 4; iCur++ )
7824 faceNodes.insert( curNodes[ind[iCur]] );
7825 if ( faceNodes.size() == 3 )
7826 iTria[ nbTria++ ] = iFace;
7828 // check if triangles are opposite
7829 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7831 // set nodes of the bottom triangle
7832 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7834 for ( iCur = 0; iCur < 4; iCur++ )
7835 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7836 indB.push_back( ind[iCur] );
7837 if ( !hexa.IsForward() )
7838 std::swap( indB[0], indB[2] );
7839 for ( iCur = 0; iCur < 3; iCur++ )
7840 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7841 // set nodes of the top triangle
7842 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7843 for ( iCur = 0; iCur < 3; ++iCur )
7844 for ( int j = 0; j < 4; ++j )
7845 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7847 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7854 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7855 //////////////////// HEXAHEDRON ---> pyramid
7856 for ( int iFace = 0; iFace < 6; iFace++ ) {
7857 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7858 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7859 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7860 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7861 // one face turns into a point ...
7862 int iOppFace = hexa.GetOppFaceIndex( iFace );
7863 ind = hexa.GetFaceNodesIndices( iOppFace );
7864 uniqueNodes.clear();
7865 for ( iCur = 0; iCur < 4; iCur++ ) {
7866 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7869 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7871 if ( uniqueNodes.size() == 4 ) {
7872 // ... and the opposite one is a quadrangle
7874 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7875 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7883 if ( toRemove && nbUniqueNodes > 4 ) {
7884 ////////////////// HEXAHEDRON ---> polyhedron
7885 hexa.SetExternalNormal();
7886 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7887 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7888 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7889 quantities.reserve( 6 ); quantities.clear();
7890 for ( int iFace = 0; iFace < 6; iFace++ )
7892 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7893 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7894 curNodes[ind[1]] == curNodes[ind[3]] )
7897 break; // opposite nodes stick
7900 for ( iCur = 0; iCur < 4; iCur++ )
7902 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7903 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7905 if ( nodeSet.size() < 3 )
7906 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7908 quantities.push_back( nodeSet.size() );
7910 if ( quantities.size() >= 4 )
7913 nbUniqueNodes = poly_nodes.size();
7914 newElemDefs[0].SetPoly(true);
7918 } // case HEXAHEDRON
7923 } // switch ( entity )
7925 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7927 // erase from nodeNodeMap nodes whose merge spoils elem
7928 vector< const SMDS_MeshNode* > noMergeNodes;
7929 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7930 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7931 nodeNodeMap.erase( noMergeNodes[i] );
7934 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7936 uniqueNodes.resize( nbUniqueNodes );
7938 if ( !toRemove && nbResElems == 0 )
7941 newElemDefs.resize( nbResElems );
7947 // ========================================================
7948 // class : SortableElement
7949 // purpose : allow sorting elements basing on their nodes
7950 // ========================================================
7951 class SortableElement : public set <const SMDS_MeshElement*>
7955 SortableElement( const SMDS_MeshElement* theElem )
7958 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7959 while ( nodeIt->more() )
7960 this->insert( nodeIt->next() );
7963 const SMDS_MeshElement* Get() const
7967 mutable const SMDS_MeshElement* myElem;
7970 //=======================================================================
7971 //function : FindEqualElements
7972 //purpose : Return list of group of elements built on the same nodes.
7973 // Search among theElements or in the whole mesh if theElements is empty
7974 //=======================================================================
7976 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7977 TListOfListOfElementsID & theGroupsOfElementsID)
7979 myLastCreatedElems.Clear();
7980 myLastCreatedNodes.Clear();
7982 typedef map< SortableElement, int > TMapOfNodeSet;
7983 typedef list<int> TGroupOfElems;
7985 if ( theElements.empty() )
7986 { // get all elements in the mesh
7987 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7988 while ( eIt->more() )
7989 theElements.insert( theElements.end(), eIt->next() );
7992 vector< TGroupOfElems > arrayOfGroups;
7993 TGroupOfElems groupOfElems;
7994 TMapOfNodeSet mapOfNodeSet;
7996 TIDSortedElemSet::iterator elemIt = theElements.begin();
7997 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7999 const SMDS_MeshElement* curElem = *elemIt;
8000 SortableElement SE(curElem);
8002 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8003 if ( !pp.second ) { // one more coincident elem
8004 TMapOfNodeSet::iterator& itSE = pp.first;
8005 int ind = (*itSE).second;
8006 arrayOfGroups[ind].push_back( curElem->GetID() );
8009 arrayOfGroups.push_back( groupOfElems );
8010 arrayOfGroups.back().push_back( curElem->GetID() );
8015 groupOfElems.clear();
8016 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8017 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8019 if ( groupIt->size() > 1 ) {
8020 //groupOfElems.sort(); -- theElements is sorted already
8021 theGroupsOfElementsID.push_back( groupOfElems );
8022 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8027 //=======================================================================
8028 //function : MergeElements
8029 //purpose : In each given group, substitute all elements by the first one.
8030 //=======================================================================
8032 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8034 myLastCreatedElems.Clear();
8035 myLastCreatedNodes.Clear();
8037 typedef list<int> TListOfIDs;
8038 TListOfIDs rmElemIds; // IDs of elems to remove
8040 SMESHDS_Mesh* aMesh = GetMeshDS();
8042 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8043 while ( groupsIt != theGroupsOfElementsID.end() ) {
8044 TListOfIDs& aGroupOfElemID = *groupsIt;
8045 aGroupOfElemID.sort();
8046 int elemIDToKeep = aGroupOfElemID.front();
8047 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8048 aGroupOfElemID.pop_front();
8049 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8050 while ( idIt != aGroupOfElemID.end() ) {
8051 int elemIDToRemove = *idIt;
8052 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8053 // add the kept element in groups of removed one (PAL15188)
8054 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8055 rmElemIds.push_back( elemIDToRemove );
8061 Remove( rmElemIds, false );
8064 //=======================================================================
8065 //function : MergeEqualElements
8066 //purpose : Remove all but one of elements built on the same nodes.
8067 //=======================================================================
8069 void SMESH_MeshEditor::MergeEqualElements()
8071 TIDSortedElemSet aMeshElements; /* empty input ==
8072 to merge equal elements in the whole mesh */
8073 TListOfListOfElementsID aGroupsOfElementsID;
8074 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8075 MergeElements(aGroupsOfElementsID);
8078 //=======================================================================
8079 //function : findAdjacentFace
8081 //=======================================================================
8083 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8084 const SMDS_MeshNode* n2,
8085 const SMDS_MeshElement* elem)
8087 TIDSortedElemSet elemSet, avoidSet;
8089 avoidSet.insert ( elem );
8090 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8093 //=======================================================================
8094 //function : findSegment
8095 //purpose : Return a mesh segment by two nodes one of which can be medium
8096 //=======================================================================
8098 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8099 const SMDS_MeshNode* n2)
8101 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8102 while ( it->more() )
8104 const SMDS_MeshElement* seg = it->next();
8105 if ( seg->GetNodeIndex( n2 ) >= 0 )
8111 //=======================================================================
8112 //function : FindFreeBorder
8114 //=======================================================================
8116 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8118 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8119 const SMDS_MeshNode* theSecondNode,
8120 const SMDS_MeshNode* theLastNode,
8121 list< const SMDS_MeshNode* > & theNodes,
8122 list< const SMDS_MeshElement* >& theFaces)
8124 if ( !theFirstNode || !theSecondNode )
8126 // find border face between theFirstNode and theSecondNode
8127 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8131 theFaces.push_back( curElem );
8132 theNodes.push_back( theFirstNode );
8133 theNodes.push_back( theSecondNode );
8135 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8136 TIDSortedElemSet foundElems;
8137 bool needTheLast = ( theLastNode != 0 );
8139 while ( nStart != theLastNode ) {
8140 if ( nStart == theFirstNode )
8141 return !needTheLast;
8143 // find all free border faces sharing form nStart
8145 list< const SMDS_MeshElement* > curElemList;
8146 list< const SMDS_MeshNode* > nStartList;
8147 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8148 while ( invElemIt->more() ) {
8149 const SMDS_MeshElement* e = invElemIt->next();
8150 if ( e == curElem || foundElems.insert( e ).second ) {
8152 int iNode = 0, nbNodes = e->NbNodes();
8153 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8155 if ( e->IsQuadratic() ) {
8156 const SMDS_VtkFace* F =
8157 dynamic_cast<const SMDS_VtkFace*>(e);
8158 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8159 // use special nodes iterator
8160 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8161 while( anIter->more() ) {
8162 nodes[ iNode++ ] = cast2Node(anIter->next());
8166 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8167 while ( nIt->more() )
8168 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8170 nodes[ iNode ] = nodes[ 0 ];
8172 for ( iNode = 0; iNode < nbNodes; iNode++ )
8173 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8174 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8175 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8177 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8178 curElemList.push_back( e );
8182 // analyse the found
8184 int nbNewBorders = curElemList.size();
8185 if ( nbNewBorders == 0 ) {
8186 // no free border furthermore
8187 return !needTheLast;
8189 else if ( nbNewBorders == 1 ) {
8190 // one more element found
8192 nStart = nStartList.front();
8193 curElem = curElemList.front();
8194 theFaces.push_back( curElem );
8195 theNodes.push_back( nStart );
8198 // several continuations found
8199 list< const SMDS_MeshElement* >::iterator curElemIt;
8200 list< const SMDS_MeshNode* >::iterator nStartIt;
8201 // check if one of them reached the last node
8202 if ( needTheLast ) {
8203 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8204 curElemIt!= curElemList.end();
8205 curElemIt++, nStartIt++ )
8206 if ( *nStartIt == theLastNode ) {
8207 theFaces.push_back( *curElemIt );
8208 theNodes.push_back( *nStartIt );
8212 // find the best free border by the continuations
8213 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8214 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8215 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8216 curElemIt!= curElemList.end();
8217 curElemIt++, nStartIt++ )
8219 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8220 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8221 // find one more free border
8222 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8226 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8227 // choice: clear a worse one
8228 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8229 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8230 contNodes[ iWorse ].clear();
8231 contFaces[ iWorse ].clear();
8234 if ( contNodes[0].empty() && contNodes[1].empty() )
8237 // append the best free border
8238 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8239 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8240 theNodes.pop_back(); // remove nIgnore
8241 theNodes.pop_back(); // remove nStart
8242 theFaces.pop_back(); // remove curElem
8243 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8244 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8245 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8246 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8249 } // several continuations found
8250 } // while ( nStart != theLastNode )
8255 //=======================================================================
8256 //function : CheckFreeBorderNodes
8257 //purpose : Return true if the tree nodes are on a free border
8258 //=======================================================================
8260 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8261 const SMDS_MeshNode* theNode2,
8262 const SMDS_MeshNode* theNode3)
8264 list< const SMDS_MeshNode* > nodes;
8265 list< const SMDS_MeshElement* > faces;
8266 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8269 //=======================================================================
8270 //function : SewFreeBorder
8272 //warning : for border-to-side sewing theSideSecondNode is considered as
8273 // the last side node and theSideThirdNode is not used
8274 //=======================================================================
8276 SMESH_MeshEditor::Sew_Error
8277 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8278 const SMDS_MeshNode* theBordSecondNode,
8279 const SMDS_MeshNode* theBordLastNode,
8280 const SMDS_MeshNode* theSideFirstNode,
8281 const SMDS_MeshNode* theSideSecondNode,
8282 const SMDS_MeshNode* theSideThirdNode,
8283 const bool theSideIsFreeBorder,
8284 const bool toCreatePolygons,
8285 const bool toCreatePolyedrs)
8287 myLastCreatedElems.Clear();
8288 myLastCreatedNodes.Clear();
8290 Sew_Error aResult = SEW_OK;
8292 // ====================================
8293 // find side nodes and elements
8294 // ====================================
8296 list< const SMDS_MeshNode* > nSide[ 2 ];
8297 list< const SMDS_MeshElement* > eSide[ 2 ];
8298 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8299 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8303 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8304 nSide[0], eSide[0])) {
8305 MESSAGE(" Free Border 1 not found " );
8306 aResult = SEW_BORDER1_NOT_FOUND;
8308 if (theSideIsFreeBorder) {
8311 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8312 nSide[1], eSide[1])) {
8313 MESSAGE(" Free Border 2 not found " );
8314 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8317 if ( aResult != SEW_OK )
8320 if (!theSideIsFreeBorder) {
8324 // -------------------------------------------------------------------------
8326 // 1. If nodes to merge are not coincident, move nodes of the free border
8327 // from the coord sys defined by the direction from the first to last
8328 // nodes of the border to the correspondent sys of the side 2
8329 // 2. On the side 2, find the links most co-directed with the correspondent
8330 // links of the free border
8331 // -------------------------------------------------------------------------
8333 // 1. Since sewing may break if there are volumes to split on the side 2,
8334 // we won't move nodes but just compute new coordinates for them
8335 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8336 TNodeXYZMap nBordXYZ;
8337 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8338 list< const SMDS_MeshNode* >::iterator nBordIt;
8340 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8341 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8342 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8343 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8344 double tol2 = 1.e-8;
8345 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8346 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8347 // Need node movement.
8349 // find X and Z axes to create trsf
8350 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8352 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8354 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8357 gp_Ax3 toBordAx( Pb1, Zb, X );
8358 gp_Ax3 fromSideAx( Ps1, Zs, X );
8359 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8361 gp_Trsf toBordSys, fromSide2Sys;
8362 toBordSys.SetTransformation( toBordAx );
8363 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8364 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8367 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8368 const SMDS_MeshNode* n = *nBordIt;
8369 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8370 toBordSys.Transforms( xyz );
8371 fromSide2Sys.Transforms( xyz );
8372 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8376 // just insert nodes XYZ in the nBordXYZ map
8377 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8378 const SMDS_MeshNode* n = *nBordIt;
8379 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8383 // 2. On the side 2, find the links most co-directed with the correspondent
8384 // links of the free border
8386 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8387 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8388 sideNodes.push_back( theSideFirstNode );
8390 bool hasVolumes = false;
8391 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8392 set<long> foundSideLinkIDs, checkedLinkIDs;
8393 SMDS_VolumeTool volume;
8394 //const SMDS_MeshNode* faceNodes[ 4 ];
8396 const SMDS_MeshNode* sideNode;
8397 const SMDS_MeshElement* sideElem = 0;
8398 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8399 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8400 nBordIt = bordNodes.begin();
8402 // border node position and border link direction to compare with
8403 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8404 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8405 // choose next side node by link direction or by closeness to
8406 // the current border node:
8407 bool searchByDir = ( *nBordIt != theBordLastNode );
8409 // find the next node on the Side 2
8411 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8413 checkedLinkIDs.clear();
8414 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8416 // loop on inverse elements of current node (prevSideNode) on the Side 2
8417 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8418 while ( invElemIt->more() )
8420 const SMDS_MeshElement* elem = invElemIt->next();
8421 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8422 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8423 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8424 bool isVolume = volume.Set( elem );
8425 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8426 if ( isVolume ) // --volume
8428 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8429 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8430 if(elem->IsQuadratic()) {
8431 const SMDS_VtkFace* F =
8432 dynamic_cast<const SMDS_VtkFace*>(elem);
8433 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8434 // use special nodes iterator
8435 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8436 while( anIter->more() ) {
8437 nodes[ iNode ] = cast2Node(anIter->next());
8438 if ( nodes[ iNode++ ] == prevSideNode )
8439 iPrevNode = iNode - 1;
8443 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8444 while ( nIt->more() ) {
8445 nodes[ iNode ] = cast2Node( nIt->next() );
8446 if ( nodes[ iNode++ ] == prevSideNode )
8447 iPrevNode = iNode - 1;
8450 // there are 2 links to check
8455 // loop on links, to be precise, on the second node of links
8456 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8457 const SMDS_MeshNode* n = nodes[ iNode ];
8459 if ( !volume.IsLinked( n, prevSideNode ))
8463 if ( iNode ) // a node before prevSideNode
8464 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8465 else // a node after prevSideNode
8466 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8468 // check if this link was already used
8469 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8470 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8471 if (!isJustChecked &&
8472 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8474 // test a link geometrically
8475 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8476 bool linkIsBetter = false;
8477 double dot = 0.0, dist = 0.0;
8478 if ( searchByDir ) { // choose most co-directed link
8479 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8480 linkIsBetter = ( dot > maxDot );
8482 else { // choose link with the node closest to bordPos
8483 dist = ( nextXYZ - bordPos ).SquareModulus();
8484 linkIsBetter = ( dist < minDist );
8486 if ( linkIsBetter ) {
8495 } // loop on inverse elements of prevSideNode
8498 MESSAGE(" Can't find path by links of the Side 2 ");
8499 return SEW_BAD_SIDE_NODES;
8501 sideNodes.push_back( sideNode );
8502 sideElems.push_back( sideElem );
8503 foundSideLinkIDs.insert ( linkID );
8504 prevSideNode = sideNode;
8506 if ( *nBordIt == theBordLastNode )
8507 searchByDir = false;
8509 // find the next border link to compare with
8510 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8511 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8512 // move to next border node if sideNode is before forward border node (bordPos)
8513 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8514 prevBordNode = *nBordIt;
8516 bordPos = nBordXYZ[ *nBordIt ];
8517 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8518 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8522 while ( sideNode != theSideSecondNode );
8524 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8525 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8526 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8528 } // end nodes search on the side 2
8530 // ============================
8531 // sew the border to the side 2
8532 // ============================
8534 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8535 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8537 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8538 if ( toMergeConformal && toCreatePolygons )
8540 // do not merge quadrangles if polygons are OK (IPAL0052824)
8541 eIt[0] = eSide[0].begin();
8542 eIt[1] = eSide[1].begin();
8543 bool allQuads[2] = { true, true };
8544 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8545 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8546 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8548 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8551 TListOfListOfNodes nodeGroupsToMerge;
8552 if (( toMergeConformal ) ||
8553 ( theSideIsFreeBorder && !theSideThirdNode )) {
8555 // all nodes are to be merged
8557 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8558 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8559 nIt[0]++, nIt[1]++ )
8561 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8562 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8563 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8568 // insert new nodes into the border and the side to get equal nb of segments
8570 // get normalized parameters of nodes on the borders
8571 vector< double > param[ 2 ];
8572 param[0].resize( maxNbNodes );
8573 param[1].resize( maxNbNodes );
8575 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8576 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8577 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8578 const SMDS_MeshNode* nPrev = *nIt;
8579 double bordLength = 0;
8580 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8581 const SMDS_MeshNode* nCur = *nIt;
8582 gp_XYZ segment (nCur->X() - nPrev->X(),
8583 nCur->Y() - nPrev->Y(),
8584 nCur->Z() - nPrev->Z());
8585 double segmentLen = segment.Modulus();
8586 bordLength += segmentLen;
8587 param[ iBord ][ iNode ] = bordLength;
8590 // normalize within [0,1]
8591 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8592 param[ iBord ][ iNode ] /= bordLength;
8596 // loop on border segments
8597 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8598 int i[ 2 ] = { 0, 0 };
8599 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8600 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8602 TElemOfNodeListMap insertMap;
8603 TElemOfNodeListMap::iterator insertMapIt;
8605 // key: elem to insert nodes into
8606 // value: 2 nodes to insert between + nodes to be inserted
8608 bool next[ 2 ] = { false, false };
8610 // find min adjacent segment length after sewing
8611 double nextParam = 10., prevParam = 0;
8612 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8613 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8614 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8615 if ( i[ iBord ] > 0 )
8616 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8618 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8619 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8620 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8622 // choose to insert or to merge nodes
8623 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8624 if ( Abs( du ) <= minSegLen * 0.2 ) {
8627 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8628 const SMDS_MeshNode* n0 = *nIt[0];
8629 const SMDS_MeshNode* n1 = *nIt[1];
8630 nodeGroupsToMerge.back().push_back( n1 );
8631 nodeGroupsToMerge.back().push_back( n0 );
8632 // position of node of the border changes due to merge
8633 param[ 0 ][ i[0] ] += du;
8634 // move n1 for the sake of elem shape evaluation during insertion.
8635 // n1 will be removed by MergeNodes() anyway
8636 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8637 next[0] = next[1] = true;
8642 int intoBord = ( du < 0 ) ? 0 : 1;
8643 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8644 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8645 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8646 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8647 if ( intoBord == 1 ) {
8648 // move node of the border to be on a link of elem of the side
8649 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8650 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8651 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8652 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8653 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8655 insertMapIt = insertMap.find( elem );
8656 bool notFound = ( insertMapIt == insertMap.end() );
8657 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8659 // insert into another link of the same element:
8660 // 1. perform insertion into the other link of the elem
8661 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8662 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8663 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8664 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8665 // 2. perform insertion into the link of adjacent faces
8666 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8667 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8669 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8670 InsertNodesIntoLink( seg, n12, n22, nodeList );
8672 if (toCreatePolyedrs) {
8673 // perform insertion into the links of adjacent volumes
8674 UpdateVolumes(n12, n22, nodeList);
8676 // 3. find an element appeared on n1 and n2 after the insertion
8677 insertMap.erase( elem );
8678 elem = findAdjacentFace( n1, n2, 0 );
8680 if ( notFound || otherLink ) {
8681 // add element and nodes of the side into the insertMap
8682 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8683 (*insertMapIt).second.push_back( n1 );
8684 (*insertMapIt).second.push_back( n2 );
8686 // add node to be inserted into elem
8687 (*insertMapIt).second.push_back( nIns );
8688 next[ 1 - intoBord ] = true;
8691 // go to the next segment
8692 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8693 if ( next[ iBord ] ) {
8694 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8696 nPrev[ iBord ] = *nIt[ iBord ];
8697 nIt[ iBord ]++; i[ iBord ]++;
8701 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8703 // perform insertion of nodes into elements
8705 for (insertMapIt = insertMap.begin();
8706 insertMapIt != insertMap.end();
8709 const SMDS_MeshElement* elem = (*insertMapIt).first;
8710 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8711 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8712 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8714 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8716 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8717 InsertNodesIntoLink( seg, n1, n2, nodeList );
8720 if ( !theSideIsFreeBorder ) {
8721 // look for and insert nodes into the faces adjacent to elem
8722 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8723 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8726 if (toCreatePolyedrs) {
8727 // perform insertion into the links of adjacent volumes
8728 UpdateVolumes(n1, n2, nodeList);
8731 } // end: insert new nodes
8733 MergeNodes ( nodeGroupsToMerge );
8736 // Remove coincident segments
8739 TIDSortedElemSet segments;
8740 SMESH_SequenceOfElemPtr newFaces;
8741 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8743 if ( !myLastCreatedElems(i) ) continue;
8744 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8745 segments.insert( segments.end(), myLastCreatedElems(i) );
8747 newFaces.Append( myLastCreatedElems(i) );
8749 // get segments adjacent to merged nodes
8750 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8751 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8753 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8754 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8755 while ( segIt->more() )
8756 segments.insert( segIt->next() );
8760 TListOfListOfElementsID equalGroups;
8761 if ( !segments.empty() )
8762 FindEqualElements( segments, equalGroups );
8763 if ( !equalGroups.empty() )
8765 // remove from segments those that will be removed
8766 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8767 for ( ; itGroups != equalGroups.end(); ++itGroups )
8769 list< int >& group = *itGroups;
8770 list< int >::iterator id = group.begin();
8771 for ( ++id; id != group.end(); ++id )
8772 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8773 segments.erase( seg );
8775 // remove equal segments
8776 MergeElements( equalGroups );
8778 // restore myLastCreatedElems
8779 myLastCreatedElems = newFaces;
8780 TIDSortedElemSet::iterator seg = segments.begin();
8781 for ( ; seg != segments.end(); ++seg )
8782 myLastCreatedElems.Append( *seg );
8788 //=======================================================================
8789 //function : InsertNodesIntoLink
8790 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8791 // and theBetweenNode2 and split theElement
8792 //=======================================================================
8794 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8795 const SMDS_MeshNode* theBetweenNode1,
8796 const SMDS_MeshNode* theBetweenNode2,
8797 list<const SMDS_MeshNode*>& theNodesToInsert,
8798 const bool toCreatePoly)
8800 if ( !theElement ) return;
8802 SMESHDS_Mesh *aMesh = GetMeshDS();
8803 vector<const SMDS_MeshElement*> newElems;
8805 if ( theElement->GetType() == SMDSAbs_Edge )
8807 theNodesToInsert.push_front( theBetweenNode1 );
8808 theNodesToInsert.push_back ( theBetweenNode2 );
8809 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8810 const SMDS_MeshNode* n1 = *n;
8811 for ( ++n; n != theNodesToInsert.end(); ++n )
8813 const SMDS_MeshNode* n2 = *n;
8814 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8815 AddToSameGroups( seg, theElement, aMesh );
8817 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8820 theNodesToInsert.pop_front();
8821 theNodesToInsert.pop_back();
8823 if ( theElement->IsQuadratic() ) // add a not split part
8825 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8826 theElement->end_nodes() );
8827 int iOther = 0, nbN = nodes.size();
8828 for ( ; iOther < nbN; ++iOther )
8829 if ( nodes[iOther] != theBetweenNode1 &&
8830 nodes[iOther] != theBetweenNode2 )
8834 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8835 AddToSameGroups( seg, theElement, aMesh );
8837 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8839 else if ( iOther == 2 )
8841 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8842 AddToSameGroups( seg, theElement, aMesh );
8844 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8847 // treat new elements
8848 for ( size_t i = 0; i < newElems.size(); ++i )
8851 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8852 myLastCreatedElems.Append( newElems[i] );
8854 ReplaceElemInGroups( theElement, newElems, aMesh );
8855 aMesh->RemoveElement( theElement );
8858 } // if ( theElement->GetType() == SMDSAbs_Edge )
8860 const SMDS_MeshElement* theFace = theElement;
8861 if ( theFace->GetType() != SMDSAbs_Face ) return;
8863 // find indices of 2 link nodes and of the rest nodes
8864 int iNode = 0, il1, il2, i3, i4;
8865 il1 = il2 = i3 = i4 = -1;
8866 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8868 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8869 while ( nodeIt->more() ) {
8870 const SMDS_MeshNode* n = nodeIt->next();
8871 if ( n == theBetweenNode1 )
8873 else if ( n == theBetweenNode2 )
8879 nodes[ iNode++ ] = n;
8881 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8884 // arrange link nodes to go one after another regarding the face orientation
8885 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8886 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8891 aNodesToInsert.reverse();
8893 // check that not link nodes of a quadrangles are in good order
8894 int nbFaceNodes = theFace->NbNodes();
8895 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8901 if (toCreatePoly || theFace->IsPoly()) {
8904 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8906 // add nodes of face up to first node of link
8909 if ( theFace->IsQuadratic() ) {
8910 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8911 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8912 // use special nodes iterator
8913 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8914 while( anIter->more() && !isFLN ) {
8915 const SMDS_MeshNode* n = cast2Node(anIter->next());
8916 poly_nodes[iNode++] = n;
8917 if (n == nodes[il1]) {
8921 // add nodes to insert
8922 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8923 for (; nIt != aNodesToInsert.end(); nIt++) {
8924 poly_nodes[iNode++] = *nIt;
8926 // add nodes of face starting from last node of link
8927 while ( anIter->more() ) {
8928 poly_nodes[iNode++] = cast2Node(anIter->next());
8932 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8933 while ( nodeIt->more() && !isFLN ) {
8934 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8935 poly_nodes[iNode++] = n;
8936 if (n == nodes[il1]) {
8940 // add nodes to insert
8941 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8942 for (; nIt != aNodesToInsert.end(); nIt++) {
8943 poly_nodes[iNode++] = *nIt;
8945 // add nodes of face starting from last node of link
8946 while ( nodeIt->more() ) {
8947 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8948 poly_nodes[iNode++] = n;
8953 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8956 else if ( !theFace->IsQuadratic() )
8958 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8959 int nbLinkNodes = 2 + aNodesToInsert.size();
8960 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8961 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8962 linkNodes[ 0 ] = nodes[ il1 ];
8963 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8964 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8965 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8966 linkNodes[ iNode++ ] = *nIt;
8968 // decide how to split a quadrangle: compare possible variants
8969 // and choose which of splits to be a quadrangle
8970 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8971 if ( nbFaceNodes == 3 ) {
8972 iBestQuad = nbSplits;
8975 else if ( nbFaceNodes == 4 ) {
8976 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8977 double aBestRate = DBL_MAX;
8978 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8980 double aBadRate = 0;
8981 // evaluate elements quality
8982 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8983 if ( iSplit == iQuad ) {
8984 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8988 aBadRate += getBadRate( &quad, aCrit );
8991 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8993 nodes[ iSplit < iQuad ? i4 : i3 ]);
8994 aBadRate += getBadRate( &tria, aCrit );
8998 if ( aBadRate < aBestRate ) {
9000 aBestRate = aBadRate;
9005 // create new elements
9007 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9009 if ( iSplit == iBestQuad )
9010 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9015 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9017 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9020 const SMDS_MeshNode* newNodes[ 4 ];
9021 newNodes[ 0 ] = linkNodes[ i1 ];
9022 newNodes[ 1 ] = linkNodes[ i2 ];
9023 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9024 newNodes[ 3 ] = nodes[ i4 ];
9025 if (iSplit == iBestQuad)
9026 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9028 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9030 } // end if(!theFace->IsQuadratic())
9032 else { // theFace is quadratic
9033 // we have to split theFace on simple triangles and one simple quadrangle
9035 int nbshift = tmp*2;
9036 // shift nodes in nodes[] by nbshift
9038 for(i=0; i<nbshift; i++) {
9039 const SMDS_MeshNode* n = nodes[0];
9040 for(j=0; j<nbFaceNodes-1; j++) {
9041 nodes[j] = nodes[j+1];
9043 nodes[nbFaceNodes-1] = n;
9045 il1 = il1 - nbshift;
9046 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9047 // n0 n1 n2 n0 n1 n2
9048 // +-----+-----+ +-----+-----+
9057 // create new elements
9059 if ( nbFaceNodes == 6 ) { // quadratic triangle
9060 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9061 if ( theFace->IsMediumNode(nodes[il1]) ) {
9062 // create quadrangle
9063 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9069 // create quadrangle
9070 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9076 else { // nbFaceNodes==8 - quadratic quadrangle
9077 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9078 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9079 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9080 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9081 // create quadrangle
9082 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9088 // create quadrangle
9089 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9095 // create needed triangles using n1,n2,n3 and inserted nodes
9096 int nbn = 2 + aNodesToInsert.size();
9097 vector<const SMDS_MeshNode*> aNodes(nbn);
9098 aNodes[0 ] = nodes[n1];
9099 aNodes[nbn-1] = nodes[n2];
9100 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9101 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9102 aNodes[iNode++] = *nIt;
9104 for ( i = 1; i < nbn; i++ )
9105 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9108 // remove the old face
9109 for ( size_t i = 0; i < newElems.size(); ++i )
9112 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9113 myLastCreatedElems.Append( newElems[i] );
9115 ReplaceElemInGroups( theFace, newElems, aMesh );
9116 aMesh->RemoveElement(theFace);
9118 } // InsertNodesIntoLink()
9120 //=======================================================================
9121 //function : UpdateVolumes
9123 //=======================================================================
9125 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9126 const SMDS_MeshNode* theBetweenNode2,
9127 list<const SMDS_MeshNode*>& theNodesToInsert)
9129 myLastCreatedElems.Clear();
9130 myLastCreatedNodes.Clear();
9132 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9133 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9134 const SMDS_MeshElement* elem = invElemIt->next();
9136 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9137 SMDS_VolumeTool aVolume (elem);
9138 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9141 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9142 int iface, nbFaces = aVolume.NbFaces();
9143 vector<const SMDS_MeshNode *> poly_nodes;
9144 vector<int> quantities (nbFaces);
9146 for (iface = 0; iface < nbFaces; iface++) {
9147 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9148 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9149 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9151 for (int inode = 0; inode < nbFaceNodes; inode++) {
9152 poly_nodes.push_back(faceNodes[inode]);
9154 if (nbInserted == 0) {
9155 if (faceNodes[inode] == theBetweenNode1) {
9156 if (faceNodes[inode + 1] == theBetweenNode2) {
9157 nbInserted = theNodesToInsert.size();
9159 // add nodes to insert
9160 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9161 for (; nIt != theNodesToInsert.end(); nIt++) {
9162 poly_nodes.push_back(*nIt);
9166 else if (faceNodes[inode] == theBetweenNode2) {
9167 if (faceNodes[inode + 1] == theBetweenNode1) {
9168 nbInserted = theNodesToInsert.size();
9170 // add nodes to insert in reversed order
9171 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9173 for (; nIt != theNodesToInsert.begin(); nIt--) {
9174 poly_nodes.push_back(*nIt);
9176 poly_nodes.push_back(*nIt);
9183 quantities[iface] = nbFaceNodes + nbInserted;
9186 // Replace the volume
9187 SMESHDS_Mesh *aMesh = GetMeshDS();
9189 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9191 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9192 myLastCreatedElems.Append( newElem );
9193 ReplaceElemInGroups( elem, newElem, aMesh );
9195 aMesh->RemoveElement( elem );
9201 //================================================================================
9203 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9205 //================================================================================
9207 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9208 vector<const SMDS_MeshNode *> & nodes,
9209 vector<int> & nbNodeInFaces )
9212 nbNodeInFaces.clear();
9213 SMDS_VolumeTool vTool ( elem );
9214 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9216 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9217 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9218 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9223 //=======================================================================
9225 * \brief Convert elements contained in a sub-mesh to quadratic
9226 * \return int - nb of checked elements
9228 //=======================================================================
9230 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9231 SMESH_MesherHelper& theHelper,
9232 const bool theForce3d)
9235 if( !theSm ) return nbElem;
9237 vector<int> nbNodeInFaces;
9238 vector<const SMDS_MeshNode *> nodes;
9239 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9240 while(ElemItr->more())
9243 const SMDS_MeshElement* elem = ElemItr->next();
9244 if( !elem ) continue;
9246 // analyse a necessity of conversion
9247 const SMDSAbs_ElementType aType = elem->GetType();
9248 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9250 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9251 bool hasCentralNodes = false;
9252 if ( elem->IsQuadratic() )
9255 switch ( aGeomType ) {
9256 case SMDSEntity_Quad_Triangle:
9257 case SMDSEntity_Quad_Quadrangle:
9258 case SMDSEntity_Quad_Hexa:
9259 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9261 case SMDSEntity_BiQuad_Triangle:
9262 case SMDSEntity_BiQuad_Quadrangle:
9263 case SMDSEntity_TriQuad_Hexa:
9264 alreadyOK = theHelper.GetIsBiQuadratic();
9265 hasCentralNodes = true;
9270 // take into account already present modium nodes
9272 case SMDSAbs_Volume:
9273 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9275 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9277 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9283 // get elem data needed to re-create it
9285 const int id = elem->GetID();
9286 const int nbNodes = elem->NbCornerNodes();
9287 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9288 if ( aGeomType == SMDSEntity_Polyhedra )
9289 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9290 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9291 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9293 // remove a linear element
9294 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9296 // remove central nodes of biquadratic elements (biquad->quad conversion)
9297 if ( hasCentralNodes )
9298 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9299 if ( nodes[i]->NbInverseElements() == 0 )
9300 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9302 const SMDS_MeshElement* NewElem = 0;
9308 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9316 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9319 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9322 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9326 case SMDSAbs_Volume :
9330 case SMDSEntity_Tetra:
9331 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9333 case SMDSEntity_Pyramid:
9334 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9336 case SMDSEntity_Penta:
9337 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9339 case SMDSEntity_Hexa:
9340 case SMDSEntity_Quad_Hexa:
9341 case SMDSEntity_TriQuad_Hexa:
9342 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9343 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9345 case SMDSEntity_Hexagonal_Prism:
9347 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9354 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9355 if( NewElem && NewElem->getshapeId() < 1 )
9356 theSm->AddElement( NewElem );
9360 //=======================================================================
9361 //function : ConvertToQuadratic
9363 //=======================================================================
9365 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9367 SMESHDS_Mesh* meshDS = GetMeshDS();
9369 SMESH_MesherHelper aHelper(*myMesh);
9371 aHelper.SetIsQuadratic( true );
9372 aHelper.SetIsBiQuadratic( theToBiQuad );
9373 aHelper.SetElementsOnShape(true);
9374 aHelper.ToFixNodeParameters( true );
9376 // convert elements assigned to sub-meshes
9377 int nbCheckedElems = 0;
9378 if ( myMesh->HasShapeToMesh() )
9380 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9382 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9383 while ( smIt->more() ) {
9384 SMESH_subMesh* sm = smIt->next();
9385 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9386 aHelper.SetSubShape( sm->GetSubShape() );
9387 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9393 // convert elements NOT assigned to sub-meshes
9394 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9395 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9397 aHelper.SetElementsOnShape(false);
9398 SMESHDS_SubMesh *smDS = 0;
9401 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9402 while( aEdgeItr->more() )
9404 const SMDS_MeshEdge* edge = aEdgeItr->next();
9405 if ( !edge->IsQuadratic() )
9407 int id = edge->GetID();
9408 const SMDS_MeshNode* n1 = edge->GetNode(0);
9409 const SMDS_MeshNode* n2 = edge->GetNode(1);
9411 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9413 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9414 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9418 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9423 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9424 while( aFaceItr->more() )
9426 const SMDS_MeshFace* face = aFaceItr->next();
9427 if ( !face ) continue;
9429 const SMDSAbs_EntityType type = face->GetEntityType();
9433 case SMDSEntity_Quad_Triangle:
9434 case SMDSEntity_Quad_Quadrangle:
9435 alreadyOK = !theToBiQuad;
9436 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9438 case SMDSEntity_BiQuad_Triangle:
9439 case SMDSEntity_BiQuad_Quadrangle:
9440 alreadyOK = theToBiQuad;
9441 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9443 default: alreadyOK = false;
9448 const int id = face->GetID();
9449 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9451 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9453 SMDS_MeshFace * NewFace = 0;
9456 case SMDSEntity_Triangle:
9457 case SMDSEntity_Quad_Triangle:
9458 case SMDSEntity_BiQuad_Triangle:
9459 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9460 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9461 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9464 case SMDSEntity_Quadrangle:
9465 case SMDSEntity_Quad_Quadrangle:
9466 case SMDSEntity_BiQuad_Quadrangle:
9467 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9468 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9469 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9473 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9475 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9479 vector<int> nbNodeInFaces;
9480 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9481 while(aVolumeItr->more())
9483 const SMDS_MeshVolume* volume = aVolumeItr->next();
9484 if ( !volume ) continue;
9486 const SMDSAbs_EntityType type = volume->GetEntityType();
9487 if ( volume->IsQuadratic() )
9492 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9493 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9494 default: alreadyOK = true;
9498 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9502 const int id = volume->GetID();
9503 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9504 if ( type == SMDSEntity_Polyhedra )
9505 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9506 else if ( type == SMDSEntity_Hexagonal_Prism )
9507 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9509 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9511 SMDS_MeshVolume * NewVolume = 0;
9514 case SMDSEntity_Tetra:
9515 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9517 case SMDSEntity_Hexa:
9518 case SMDSEntity_Quad_Hexa:
9519 case SMDSEntity_TriQuad_Hexa:
9520 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9521 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9522 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9523 if ( nodes[i]->NbInverseElements() == 0 )
9524 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9526 case SMDSEntity_Pyramid:
9527 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9528 nodes[3], nodes[4], id, theForce3d);
9530 case SMDSEntity_Penta:
9531 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9532 nodes[3], nodes[4], nodes[5], id, theForce3d);
9534 case SMDSEntity_Hexagonal_Prism:
9536 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9538 ReplaceElemInGroups(volume, NewVolume, meshDS);
9543 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9544 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9545 // aHelper.FixQuadraticElements(myError);
9546 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9550 //================================================================================
9552 * \brief Makes given elements quadratic
9553 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9554 * \param theElements - elements to make quadratic
9556 //================================================================================
9558 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9559 TIDSortedElemSet& theElements,
9560 const bool theToBiQuad)
9562 if ( theElements.empty() ) return;
9564 // we believe that all theElements are of the same type
9565 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9567 // get all nodes shared by theElements
9568 TIDSortedNodeSet allNodes;
9569 TIDSortedElemSet::iterator eIt = theElements.begin();
9570 for ( ; eIt != theElements.end(); ++eIt )
9571 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9573 // complete theElements with elements of lower dim whose all nodes are in allNodes
9575 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9576 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9577 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9578 for ( ; nIt != allNodes.end(); ++nIt )
9580 const SMDS_MeshNode* n = *nIt;
9581 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9582 while ( invIt->more() )
9584 const SMDS_MeshElement* e = invIt->next();
9585 const SMDSAbs_ElementType type = e->GetType();
9586 if ( e->IsQuadratic() )
9588 quadAdjacentElems[ type ].insert( e );
9591 switch ( e->GetEntityType() ) {
9592 case SMDSEntity_Quad_Triangle:
9593 case SMDSEntity_Quad_Quadrangle:
9594 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9595 case SMDSEntity_BiQuad_Triangle:
9596 case SMDSEntity_BiQuad_Quadrangle:
9597 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9598 default: alreadyOK = true;
9603 if ( type >= elemType )
9604 continue; // same type or more complex linear element
9606 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9607 continue; // e is already checked
9611 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9612 while ( nodeIt->more() && allIn )
9613 allIn = allNodes.count( nodeIt->next() );
9615 theElements.insert(e );
9619 SMESH_MesherHelper helper(*myMesh);
9620 helper.SetIsQuadratic( true );
9621 helper.SetIsBiQuadratic( theToBiQuad );
9623 // add links of quadratic adjacent elements to the helper
9625 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9626 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9627 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9629 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9631 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9632 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9633 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9635 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9637 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9638 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9639 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9641 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9644 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9646 SMESHDS_Mesh* meshDS = GetMeshDS();
9647 SMESHDS_SubMesh* smDS = 0;
9648 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9650 const SMDS_MeshElement* elem = *eIt;
9653 int nbCentralNodes = 0;
9654 switch ( elem->GetEntityType() ) {
9655 // linear convertible
9656 case SMDSEntity_Edge:
9657 case SMDSEntity_Triangle:
9658 case SMDSEntity_Quadrangle:
9659 case SMDSEntity_Tetra:
9660 case SMDSEntity_Pyramid:
9661 case SMDSEntity_Hexa:
9662 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9663 // quadratic that can become bi-quadratic
9664 case SMDSEntity_Quad_Triangle:
9665 case SMDSEntity_Quad_Quadrangle:
9666 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9668 case SMDSEntity_BiQuad_Triangle:
9669 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9670 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9672 default: alreadyOK = true;
9674 if ( alreadyOK ) continue;
9676 const SMDSAbs_ElementType type = elem->GetType();
9677 const int id = elem->GetID();
9678 const int nbNodes = elem->NbCornerNodes();
9679 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9681 helper.SetSubShape( elem->getshapeId() );
9683 if ( !smDS || !smDS->Contains( elem ))
9684 smDS = meshDS->MeshElements( elem->getshapeId() );
9685 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9687 SMDS_MeshElement * newElem = 0;
9690 case 4: // cases for most frequently used element types go first (for optimization)
9691 if ( type == SMDSAbs_Volume )
9692 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9694 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9697 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9698 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9701 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9704 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9707 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9708 nodes[4], id, theForce3d);
9711 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9712 nodes[4], nodes[5], id, theForce3d);
9716 ReplaceElemInGroups( elem, newElem, meshDS);
9717 if( newElem && smDS )
9718 smDS->AddElement( newElem );
9720 // remove central nodes
9721 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9722 if ( nodes[i]->NbInverseElements() == 0 )
9723 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9725 } // loop on theElements
9728 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9729 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9730 // helper.FixQuadraticElements( myError );
9731 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9735 //=======================================================================
9737 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9738 * \return int - nb of checked elements
9740 //=======================================================================
9742 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9743 SMDS_ElemIteratorPtr theItr,
9744 const int theShapeID)
9747 SMESHDS_Mesh* meshDS = GetMeshDS();
9748 ElemFeatures elemType;
9749 vector<const SMDS_MeshNode *> nodes;
9751 while( theItr->more() )
9753 const SMDS_MeshElement* elem = theItr->next();
9755 if( elem && elem->IsQuadratic())
9758 int nbCornerNodes = elem->NbCornerNodes();
9759 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9761 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9763 //remove a quadratic element
9764 if ( !theSm || !theSm->Contains( elem ))
9765 theSm = meshDS->MeshElements( elem->getshapeId() );
9766 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9768 // remove medium nodes
9769 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9770 if ( nodes[i]->NbInverseElements() == 0 )
9771 meshDS->RemoveFreeNode( nodes[i], theSm );
9773 // add a linear element
9774 nodes.resize( nbCornerNodes );
9775 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9776 ReplaceElemInGroups(elem, newElem, meshDS);
9777 if( theSm && newElem )
9778 theSm->AddElement( newElem );
9784 //=======================================================================
9785 //function : ConvertFromQuadratic
9787 //=======================================================================
9789 bool SMESH_MeshEditor::ConvertFromQuadratic()
9791 int nbCheckedElems = 0;
9792 if ( myMesh->HasShapeToMesh() )
9794 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9796 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9797 while ( smIt->more() ) {
9798 SMESH_subMesh* sm = smIt->next();
9799 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9800 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9806 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9807 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9809 SMESHDS_SubMesh *aSM = 0;
9810 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9818 //================================================================================
9820 * \brief Return true if all medium nodes of the element are in the node set
9822 //================================================================================
9824 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9826 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9827 if ( !nodeSet.count( elem->GetNode(i) ))
9833 //================================================================================
9835 * \brief Makes given elements linear
9837 //================================================================================
9839 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9841 if ( theElements.empty() ) return;
9843 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9844 set<int> mediumNodeIDs;
9845 TIDSortedElemSet::iterator eIt = theElements.begin();
9846 for ( ; eIt != theElements.end(); ++eIt )
9848 const SMDS_MeshElement* e = *eIt;
9849 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9850 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9853 // replace given elements by linear ones
9854 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9855 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9857 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9858 // except those elements sharing medium nodes of quadratic element whose medium nodes
9859 // are not all in mediumNodeIDs
9861 // get remaining medium nodes
9862 TIDSortedNodeSet mediumNodes;
9863 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9864 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9865 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9866 mediumNodes.insert( mediumNodes.end(), n );
9868 // find more quadratic elements to convert
9869 TIDSortedElemSet moreElemsToConvert;
9870 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9871 for ( ; nIt != mediumNodes.end(); ++nIt )
9873 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9874 while ( invIt->more() )
9876 const SMDS_MeshElement* e = invIt->next();
9877 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9879 // find a more complex element including e and
9880 // whose medium nodes are not in mediumNodes
9881 bool complexFound = false;
9882 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9884 SMDS_ElemIteratorPtr invIt2 =
9885 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9886 while ( invIt2->more() )
9888 const SMDS_MeshElement* eComplex = invIt2->next();
9889 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9891 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9892 if ( nbCommonNodes == e->NbNodes())
9894 complexFound = true;
9895 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9901 if ( !complexFound )
9902 moreElemsToConvert.insert( e );
9906 elemIt = elemSetIterator( moreElemsToConvert );
9907 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9910 //=======================================================================
9911 //function : SewSideElements
9913 //=======================================================================
9915 SMESH_MeshEditor::Sew_Error
9916 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9917 TIDSortedElemSet& theSide2,
9918 const SMDS_MeshNode* theFirstNode1,
9919 const SMDS_MeshNode* theFirstNode2,
9920 const SMDS_MeshNode* theSecondNode1,
9921 const SMDS_MeshNode* theSecondNode2)
9923 myLastCreatedElems.Clear();
9924 myLastCreatedNodes.Clear();
9926 if ( theSide1.size() != theSide2.size() )
9927 return SEW_DIFF_NB_OF_ELEMENTS;
9929 Sew_Error aResult = SEW_OK;
9931 // 1. Build set of faces representing each side
9932 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9933 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9935 // =======================================================================
9936 // 1. Build set of faces representing each side:
9937 // =======================================================================
9938 // a. build set of nodes belonging to faces
9939 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9940 // c. create temporary faces representing side of volumes if correspondent
9941 // face does not exist
9943 SMESHDS_Mesh* aMesh = GetMeshDS();
9944 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9945 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9946 TIDSortedElemSet faceSet1, faceSet2;
9947 set<const SMDS_MeshElement*> volSet1, volSet2;
9948 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9949 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9950 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9951 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9952 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9953 int iSide, iFace, iNode;
9955 list<const SMDS_MeshElement* > tempFaceList;
9956 for ( iSide = 0; iSide < 2; iSide++ ) {
9957 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9958 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9959 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9960 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9961 set<const SMDS_MeshElement*>::iterator vIt;
9962 TIDSortedElemSet::iterator eIt;
9963 set<const SMDS_MeshNode*>::iterator nIt;
9965 // check that given nodes belong to given elements
9966 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9967 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9968 int firstIndex = -1, secondIndex = -1;
9969 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9970 const SMDS_MeshElement* elem = *eIt;
9971 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9972 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9973 if ( firstIndex > -1 && secondIndex > -1 ) break;
9975 if ( firstIndex < 0 || secondIndex < 0 ) {
9976 // we can simply return until temporary faces created
9977 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9980 // -----------------------------------------------------------
9981 // 1a. Collect nodes of existing faces
9982 // and build set of face nodes in order to detect missing
9983 // faces corresponding to sides of volumes
9984 // -----------------------------------------------------------
9986 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9988 // loop on the given element of a side
9989 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9990 //const SMDS_MeshElement* elem = *eIt;
9991 const SMDS_MeshElement* elem = *eIt;
9992 if ( elem->GetType() == SMDSAbs_Face ) {
9993 faceSet->insert( elem );
9994 set <const SMDS_MeshNode*> faceNodeSet;
9995 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9996 while ( nodeIt->more() ) {
9997 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9998 nodeSet->insert( n );
9999 faceNodeSet.insert( n );
10001 setOfFaceNodeSet.insert( faceNodeSet );
10003 else if ( elem->GetType() == SMDSAbs_Volume )
10004 volSet->insert( elem );
10006 // ------------------------------------------------------------------------------
10007 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10008 // ------------------------------------------------------------------------------
10010 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10011 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10012 while ( fIt->more() ) { // loop on faces sharing a node
10013 const SMDS_MeshElement* f = fIt->next();
10014 if ( faceSet->find( f ) == faceSet->end() ) {
10015 // check if all nodes are in nodeSet and
10016 // complete setOfFaceNodeSet if they are
10017 set <const SMDS_MeshNode*> faceNodeSet;
10018 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10019 bool allInSet = true;
10020 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10021 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10022 if ( nodeSet->find( n ) == nodeSet->end() )
10025 faceNodeSet.insert( n );
10028 faceSet->insert( f );
10029 setOfFaceNodeSet.insert( faceNodeSet );
10035 // -------------------------------------------------------------------------
10036 // 1c. Create temporary faces representing sides of volumes if correspondent
10037 // face does not exist
10038 // -------------------------------------------------------------------------
10040 if ( !volSet->empty() ) {
10041 //int nodeSetSize = nodeSet->size();
10043 // loop on given volumes
10044 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10045 SMDS_VolumeTool vol (*vIt);
10046 // loop on volume faces: find free faces
10047 // --------------------------------------
10048 list<const SMDS_MeshElement* > freeFaceList;
10049 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10050 if ( !vol.IsFreeFace( iFace ))
10052 // check if there is already a face with same nodes in a face set
10053 const SMDS_MeshElement* aFreeFace = 0;
10054 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10055 int nbNodes = vol.NbFaceNodes( iFace );
10056 set <const SMDS_MeshNode*> faceNodeSet;
10057 vol.GetFaceNodes( iFace, faceNodeSet );
10058 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10060 // no such a face is given but it still can exist, check it
10061 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10062 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10064 if ( !aFreeFace ) {
10065 // create a temporary face
10066 if ( nbNodes == 3 ) {
10067 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10068 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10070 else if ( nbNodes == 4 ) {
10071 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10072 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10075 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10076 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10077 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10080 tempFaceList.push_back( aFreeFace );
10084 freeFaceList.push_back( aFreeFace );
10086 } // loop on faces of a volume
10088 // choose one of several free faces of a volume
10089 // --------------------------------------------
10090 if ( freeFaceList.size() > 1 ) {
10091 // choose a face having max nb of nodes shared by other elems of a side
10092 int maxNbNodes = -1;
10093 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10094 while ( fIt != freeFaceList.end() ) { // loop on free faces
10095 int nbSharedNodes = 0;
10096 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10097 while ( nodeIt->more() ) { // loop on free face nodes
10098 const SMDS_MeshNode* n =
10099 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10100 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10101 while ( invElemIt->more() ) {
10102 const SMDS_MeshElement* e = invElemIt->next();
10103 nbSharedNodes += faceSet->count( e );
10104 nbSharedNodes += elemSet->count( e );
10107 if ( nbSharedNodes > maxNbNodes ) {
10108 maxNbNodes = nbSharedNodes;
10109 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10111 else if ( nbSharedNodes == maxNbNodes ) {
10115 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10118 if ( freeFaceList.size() > 1 )
10120 // could not choose one face, use another way
10121 // choose a face most close to the bary center of the opposite side
10122 gp_XYZ aBC( 0., 0., 0. );
10123 set <const SMDS_MeshNode*> addedNodes;
10124 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10125 eIt = elemSet2->begin();
10126 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10127 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10128 while ( nodeIt->more() ) { // loop on free face nodes
10129 const SMDS_MeshNode* n =
10130 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10131 if ( addedNodes.insert( n ).second )
10132 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10135 aBC /= addedNodes.size();
10136 double minDist = DBL_MAX;
10137 fIt = freeFaceList.begin();
10138 while ( fIt != freeFaceList.end() ) { // loop on free faces
10140 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10141 while ( nodeIt->more() ) { // loop on free face nodes
10142 const SMDS_MeshNode* n =
10143 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10144 gp_XYZ p( n->X(),n->Y(),n->Z() );
10145 dist += ( aBC - p ).SquareModulus();
10147 if ( dist < minDist ) {
10149 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10152 fIt = freeFaceList.erase( fIt++ );
10155 } // choose one of several free faces of a volume
10157 if ( freeFaceList.size() == 1 ) {
10158 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10159 faceSet->insert( aFreeFace );
10160 // complete a node set with nodes of a found free face
10161 // for ( iNode = 0; iNode < ; iNode++ )
10162 // nodeSet->insert( fNodes[ iNode ] );
10165 } // loop on volumes of a side
10167 // // complete a set of faces if new nodes in a nodeSet appeared
10168 // // ----------------------------------------------------------
10169 // if ( nodeSetSize != nodeSet->size() ) {
10170 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10171 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10172 // while ( fIt->more() ) { // loop on faces sharing a node
10173 // const SMDS_MeshElement* f = fIt->next();
10174 // if ( faceSet->find( f ) == faceSet->end() ) {
10175 // // check if all nodes are in nodeSet and
10176 // // complete setOfFaceNodeSet if they are
10177 // set <const SMDS_MeshNode*> faceNodeSet;
10178 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10179 // bool allInSet = true;
10180 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10181 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10182 // if ( nodeSet->find( n ) == nodeSet->end() )
10183 // allInSet = false;
10185 // faceNodeSet.insert( n );
10187 // if ( allInSet ) {
10188 // faceSet->insert( f );
10189 // setOfFaceNodeSet.insert( faceNodeSet );
10195 } // Create temporary faces, if there are volumes given
10198 if ( faceSet1.size() != faceSet2.size() ) {
10199 // delete temporary faces: they are in reverseElements of actual nodes
10200 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10201 // while ( tmpFaceIt->more() )
10202 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10203 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10204 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10205 // aMesh->RemoveElement(*tmpFaceIt);
10206 MESSAGE("Diff nb of faces");
10207 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10210 // ============================================================
10211 // 2. Find nodes to merge:
10212 // bind a node to remove to a node to put instead
10213 // ============================================================
10215 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10216 if ( theFirstNode1 != theFirstNode2 )
10217 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10218 if ( theSecondNode1 != theSecondNode2 )
10219 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10221 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10222 set< long > linkIdSet; // links to process
10223 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10225 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10226 list< NLink > linkList[2];
10227 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10228 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10229 // loop on links in linkList; find faces by links and append links
10230 // of the found faces to linkList
10231 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10232 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10234 NLink link[] = { *linkIt[0], *linkIt[1] };
10235 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10236 if ( !linkIdSet.count( linkID ) )
10239 // by links, find faces in the face sets,
10240 // and find indices of link nodes in the found faces;
10241 // in a face set, there is only one or no face sharing a link
10242 // ---------------------------------------------------------------
10244 const SMDS_MeshElement* face[] = { 0, 0 };
10245 vector<const SMDS_MeshNode*> fnodes[2];
10246 int iLinkNode[2][2];
10247 TIDSortedElemSet avoidSet;
10248 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10249 const SMDS_MeshNode* n1 = link[iSide].first;
10250 const SMDS_MeshNode* n2 = link[iSide].second;
10251 //cout << "Side " << iSide << " ";
10252 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10253 // find a face by two link nodes
10254 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10255 *faceSetPtr[ iSide ], avoidSet,
10256 &iLinkNode[iSide][0],
10257 &iLinkNode[iSide][1] );
10258 if ( face[ iSide ])
10260 //cout << " F " << face[ iSide]->GetID() <<endl;
10261 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10262 // put face nodes to fnodes
10263 if ( face[ iSide ]->IsQuadratic() )
10265 // use interlaced nodes iterator
10266 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10267 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10268 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10269 while ( nIter->more() )
10270 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10274 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10275 face[ iSide ]->end_nodes() );
10277 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10281 // check similarity of elements of the sides
10282 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10283 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10284 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10285 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10288 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10290 break; // do not return because it's necessary to remove tmp faces
10293 // set nodes to merge
10294 // -------------------
10296 if ( face[0] && face[1] ) {
10297 const int nbNodes = face[0]->NbNodes();
10298 if ( nbNodes != face[1]->NbNodes() ) {
10299 MESSAGE("Diff nb of face nodes");
10300 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10301 break; // do not return because it s necessary to remove tmp faces
10303 bool reverse[] = { false, false }; // order of nodes in the link
10304 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10305 // analyse link orientation in faces
10306 int i1 = iLinkNode[ iSide ][ 0 ];
10307 int i2 = iLinkNode[ iSide ][ 1 ];
10308 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10310 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10311 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10312 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10314 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10315 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10318 // add other links of the faces to linkList
10319 // -----------------------------------------
10321 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10322 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10323 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10324 if ( !iter_isnew.second ) { // already in a set: no need to process
10325 linkIdSet.erase( iter_isnew.first );
10327 else // new in set == encountered for the first time: add
10329 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10330 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10331 linkList[0].push_back ( NLink( n1, n2 ));
10332 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10337 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10340 } // loop on link lists
10342 if ( aResult == SEW_OK &&
10343 ( //linkIt[0] != linkList[0].end() ||
10344 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10345 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10346 " " << (faceSetPtr[1]->empty()));
10347 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10350 // ====================================================================
10351 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10352 // ====================================================================
10354 // delete temporary faces
10355 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10356 // while ( tmpFaceIt->more() )
10357 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10358 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10359 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10360 aMesh->RemoveElement(*tmpFaceIt);
10362 if ( aResult != SEW_OK)
10365 list< int > nodeIDsToRemove;
10366 vector< const SMDS_MeshNode*> nodes;
10367 ElemFeatures elemType;
10369 // loop on nodes replacement map
10370 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10371 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10372 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10374 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10375 nodeIDsToRemove.push_back( nToRemove->GetID() );
10376 // loop on elements sharing nToRemove
10377 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10378 while ( invElemIt->more() ) {
10379 const SMDS_MeshElement* e = invElemIt->next();
10380 // get a new suite of nodes: make replacement
10381 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10382 nodes.resize( nbNodes );
10383 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10384 while ( nIt->more() ) {
10385 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10386 nnIt = nReplaceMap.find( n );
10387 if ( nnIt != nReplaceMap.end() ) {
10389 n = (*nnIt).second;
10393 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10394 // elemIDsToRemove.push_back( e->GetID() );
10398 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10399 aMesh->RemoveElement( e );
10401 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10403 AddToSameGroups( newElem, e, aMesh );
10404 if ( int aShapeId = e->getshapeId() )
10405 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10411 Remove( nodeIDsToRemove, true );
10416 //================================================================================
10418 * \brief Find corresponding nodes in two sets of faces
10419 * \param theSide1 - first face set
10420 * \param theSide2 - second first face
10421 * \param theFirstNode1 - a boundary node of set 1
10422 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10423 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10424 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10425 * \param nReplaceMap - output map of corresponding nodes
10426 * \return bool - is a success or not
10428 //================================================================================
10431 //#define DEBUG_MATCHING_NODES
10434 SMESH_MeshEditor::Sew_Error
10435 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10436 set<const SMDS_MeshElement*>& theSide2,
10437 const SMDS_MeshNode* theFirstNode1,
10438 const SMDS_MeshNode* theFirstNode2,
10439 const SMDS_MeshNode* theSecondNode1,
10440 const SMDS_MeshNode* theSecondNode2,
10441 TNodeNodeMap & nReplaceMap)
10443 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10445 nReplaceMap.clear();
10446 if ( theFirstNode1 != theFirstNode2 )
10447 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10448 if ( theSecondNode1 != theSecondNode2 )
10449 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10451 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10452 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10454 list< NLink > linkList[2];
10455 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10456 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10458 // loop on links in linkList; find faces by links and append links
10459 // of the found faces to linkList
10460 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10461 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10462 NLink link[] = { *linkIt[0], *linkIt[1] };
10463 if ( linkSet.find( link[0] ) == linkSet.end() )
10466 // by links, find faces in the face sets,
10467 // and find indices of link nodes in the found faces;
10468 // in a face set, there is only one or no face sharing a link
10469 // ---------------------------------------------------------------
10471 const SMDS_MeshElement* face[] = { 0, 0 };
10472 list<const SMDS_MeshNode*> notLinkNodes[2];
10473 //bool reverse[] = { false, false }; // order of notLinkNodes
10475 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10477 const SMDS_MeshNode* n1 = link[iSide].first;
10478 const SMDS_MeshNode* n2 = link[iSide].second;
10479 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10480 set< const SMDS_MeshElement* > facesOfNode1;
10481 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10483 // during a loop of the first node, we find all faces around n1,
10484 // during a loop of the second node, we find one face sharing both n1 and n2
10485 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10486 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10487 while ( fIt->more() ) { // loop on faces sharing a node
10488 const SMDS_MeshElement* f = fIt->next();
10489 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10490 ! facesOfNode1.insert( f ).second ) // f encounters twice
10492 if ( face[ iSide ] ) {
10493 MESSAGE( "2 faces per link " );
10494 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10497 faceSet->erase( f );
10499 // get not link nodes
10500 int nbN = f->NbNodes();
10501 if ( f->IsQuadratic() )
10503 nbNodes[ iSide ] = nbN;
10504 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10505 int i1 = f->GetNodeIndex( n1 );
10506 int i2 = f->GetNodeIndex( n2 );
10507 int iEnd = nbN, iBeg = -1, iDelta = 1;
10508 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10510 std::swap( iEnd, iBeg ); iDelta = -1;
10515 if ( i == iEnd ) i = iBeg + iDelta;
10516 if ( i == i1 ) break;
10517 nodes.push_back ( f->GetNode( i ) );
10523 // check similarity of elements of the sides
10524 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10525 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10526 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10527 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10530 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10534 // set nodes to merge
10535 // -------------------
10537 if ( face[0] && face[1] ) {
10538 if ( nbNodes[0] != nbNodes[1] ) {
10539 MESSAGE("Diff nb of face nodes");
10540 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10542 #ifdef DEBUG_MATCHING_NODES
10543 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10544 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10545 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10547 int nbN = nbNodes[0];
10549 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10550 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10551 for ( int i = 0 ; i < nbN - 2; ++i ) {
10552 #ifdef DEBUG_MATCHING_NODES
10553 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10555 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10559 // add other links of the face 1 to linkList
10560 // -----------------------------------------
10562 const SMDS_MeshElement* f0 = face[0];
10563 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10564 for ( int i = 0; i < nbN; i++ )
10566 const SMDS_MeshNode* n2 = f0->GetNode( i );
10567 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10568 linkSet.insert( SMESH_TLink( n1, n2 ));
10569 if ( !iter_isnew.second ) { // already in a set: no need to process
10570 linkSet.erase( iter_isnew.first );
10572 else // new in set == encountered for the first time: add
10574 #ifdef DEBUG_MATCHING_NODES
10575 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10576 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10578 linkList[0].push_back ( NLink( n1, n2 ));
10579 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10584 } // loop on link lists
10589 //================================================================================
10591 * \brief Create elements equal (on same nodes) to given ones
10592 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10593 * elements of the uppest dimension are duplicated.
10595 //================================================================================
10597 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10599 ClearLastCreated();
10600 SMESHDS_Mesh* mesh = GetMeshDS();
10602 // get an element type and an iterator over elements
10604 SMDSAbs_ElementType type = SMDSAbs_All;
10605 SMDS_ElemIteratorPtr elemIt;
10606 vector< const SMDS_MeshElement* > allElems;
10607 if ( theElements.empty() )
10609 if ( mesh->NbNodes() == 0 )
10611 // get most complex type
10612 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10613 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10614 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10616 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10617 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10622 // put all elements in the vector <allElems>
10623 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10624 elemIt = mesh->elementsIterator( type );
10625 while ( elemIt->more() )
10626 allElems.push_back( elemIt->next());
10627 elemIt = elemSetIterator( allElems );
10631 type = (*theElements.begin())->GetType();
10632 elemIt = elemSetIterator( theElements );
10635 // duplicate elements
10637 ElemFeatures elemType;
10639 vector< const SMDS_MeshNode* > nodes;
10640 while ( elemIt->more() )
10642 const SMDS_MeshElement* elem = elemIt->next();
10643 if ( elem->GetType() != type )
10646 elemType.Init( elem, /*basicOnly=*/false );
10647 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10649 AddElement( nodes, elemType );
10653 //================================================================================
10655 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10656 \param theElems - the list of elements (edges or faces) to be replicated
10657 The nodes for duplication could be found from these elements
10658 \param theNodesNot - list of nodes to NOT replicate
10659 \param theAffectedElems - the list of elements (cells and edges) to which the
10660 replicated nodes should be associated to.
10661 \return TRUE if operation has been completed successfully, FALSE otherwise
10663 //================================================================================
10665 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10666 const TIDSortedElemSet& theNodesNot,
10667 const TIDSortedElemSet& theAffectedElems )
10669 myLastCreatedElems.Clear();
10670 myLastCreatedNodes.Clear();
10672 if ( theElems.size() == 0 )
10675 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10680 TNodeNodeMap anOldNodeToNewNode;
10681 // duplicate elements and nodes
10682 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10683 // replce nodes by duplications
10684 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10688 //================================================================================
10690 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10691 \param theMeshDS - mesh instance
10692 \param theElems - the elements replicated or modified (nodes should be changed)
10693 \param theNodesNot - nodes to NOT replicate
10694 \param theNodeNodeMap - relation of old node to new created node
10695 \param theIsDoubleElem - flag os to replicate element or modify
10696 \return TRUE if operation has been completed successfully, FALSE otherwise
10698 //================================================================================
10700 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10701 const TIDSortedElemSet& theElems,
10702 const TIDSortedElemSet& theNodesNot,
10703 TNodeNodeMap& theNodeNodeMap,
10704 const bool theIsDoubleElem )
10706 // iterate through element and duplicate them (by nodes duplication)
10708 std::vector<const SMDS_MeshNode*> newNodes;
10709 ElemFeatures elemType;
10711 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10712 for ( ; elemItr != theElems.end(); ++elemItr )
10714 const SMDS_MeshElement* anElem = *elemItr;
10718 // duplicate nodes to duplicate element
10719 bool isDuplicate = false;
10720 newNodes.resize( anElem->NbNodes() );
10721 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10723 while ( anIter->more() )
10725 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10726 const SMDS_MeshNode* aNewNode = aCurrNode;
10727 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10728 if ( n2n != theNodeNodeMap.end() )
10730 aNewNode = n2n->second;
10732 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10735 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10736 copyPosition( aCurrNode, aNewNode );
10737 theNodeNodeMap[ aCurrNode ] = aNewNode;
10738 myLastCreatedNodes.Append( aNewNode );
10740 isDuplicate |= (aCurrNode != aNewNode);
10741 newNodes[ ind++ ] = aNewNode;
10743 if ( !isDuplicate )
10746 if ( theIsDoubleElem )
10747 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10749 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10756 //================================================================================
10758 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10759 \param theNodes - identifiers of nodes to be doubled
10760 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10761 nodes. If list of element identifiers is empty then nodes are doubled but
10762 they not assigned to elements
10763 \return TRUE if operation has been completed successfully, FALSE otherwise
10765 //================================================================================
10767 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10768 const std::list< int >& theListOfModifiedElems )
10770 myLastCreatedElems.Clear();
10771 myLastCreatedNodes.Clear();
10773 if ( theListOfNodes.size() == 0 )
10776 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10780 // iterate through nodes and duplicate them
10782 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10784 std::list< int >::const_iterator aNodeIter;
10785 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10787 int aCurr = *aNodeIter;
10788 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10794 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10797 copyPosition( aNode, aNewNode );
10798 anOldNodeToNewNode[ aNode ] = aNewNode;
10799 myLastCreatedNodes.Append( aNewNode );
10803 // Create map of new nodes for modified elements
10805 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10807 std::list< int >::const_iterator anElemIter;
10808 for ( anElemIter = theListOfModifiedElems.begin();
10809 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10811 int aCurr = *anElemIter;
10812 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10816 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10818 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10820 while ( anIter->more() )
10822 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10823 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10825 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10826 aNodeArr[ ind++ ] = aNewNode;
10829 aNodeArr[ ind++ ] = aCurrNode;
10831 anElemToNodes[ anElem ] = aNodeArr;
10834 // Change nodes of elements
10836 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10837 anElemToNodesIter = anElemToNodes.begin();
10838 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10840 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10841 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10844 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10853 //================================================================================
10855 \brief Check if element located inside shape
10856 \return TRUE if IN or ON shape, FALSE otherwise
10858 //================================================================================
10860 template<class Classifier>
10861 bool isInside(const SMDS_MeshElement* theElem,
10862 Classifier& theClassifier,
10863 const double theTol)
10865 gp_XYZ centerXYZ (0, 0, 0);
10866 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10867 while (aNodeItr->more())
10868 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10870 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10871 theClassifier.Perform(aPnt, theTol);
10872 TopAbs_State aState = theClassifier.State();
10873 return (aState == TopAbs_IN || aState == TopAbs_ON );
10876 //================================================================================
10878 * \brief Classifier of the 3D point on the TopoDS_Face
10879 * with interaface suitable for isInside()
10881 //================================================================================
10883 struct _FaceClassifier
10885 Extrema_ExtPS _extremum;
10886 BRepAdaptor_Surface _surface;
10887 TopAbs_State _state;
10889 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10891 _extremum.Initialize( _surface,
10892 _surface.FirstUParameter(), _surface.LastUParameter(),
10893 _surface.FirstVParameter(), _surface.LastVParameter(),
10894 _surface.Tolerance(), _surface.Tolerance() );
10896 void Perform(const gp_Pnt& aPnt, double theTol)
10899 _state = TopAbs_OUT;
10900 _extremum.Perform(aPnt);
10901 if ( _extremum.IsDone() )
10902 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10903 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10905 TopAbs_State State() const
10912 //================================================================================
10914 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10915 This method is the first step of DoubleNodeElemGroupsInRegion.
10916 \param theElems - list of groups of elements (edges or faces) to be replicated
10917 \param theNodesNot - list of groups of nodes not to replicated
10918 \param theShape - shape to detect affected elements (element which geometric center
10919 located on or inside shape). If the shape is null, detection is done on faces orientations
10920 (select elements with a gravity center on the side given by faces normals).
10921 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10922 The replicated nodes should be associated to affected elements.
10923 \return groups of affected elements
10924 \sa DoubleNodeElemGroupsInRegion()
10926 //================================================================================
10928 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10929 const TIDSortedElemSet& theNodesNot,
10930 const TopoDS_Shape& theShape,
10931 TIDSortedElemSet& theAffectedElems)
10933 if ( theShape.IsNull() )
10935 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10936 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10937 std::set<const SMDS_MeshElement*> edgesToCheck;
10938 alreadyCheckedNodes.clear();
10939 alreadyCheckedElems.clear();
10940 edgesToCheck.clear();
10942 // --- iterates on elements to be replicated and get elements by back references from their nodes
10944 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10945 for ( ; elemItr != theElems.end(); ++elemItr )
10947 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10948 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10951 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10952 std::set<const SMDS_MeshNode*> nodesElem;
10954 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10955 while ( nodeItr->more() )
10957 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10958 nodesElem.insert(aNode);
10960 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10961 for (; nodit != nodesElem.end(); nodit++)
10963 const SMDS_MeshNode* aNode = *nodit;
10964 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10966 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10968 alreadyCheckedNodes.insert(aNode);
10969 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10970 while ( backElemItr->more() )
10972 const SMDS_MeshElement* curElem = backElemItr->next();
10973 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10975 if (theElems.find(curElem) != theElems.end())
10977 alreadyCheckedElems.insert(curElem);
10978 double x=0, y=0, z=0;
10980 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10981 while ( nodeItr2->more() )
10983 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10984 x += anotherNode->X();
10985 y += anotherNode->Y();
10986 z += anotherNode->Z();
10990 p.SetCoord( x/nb -aNode->X(),
10992 z/nb -aNode->Z() );
10995 theAffectedElems.insert( curElem );
10997 else if (curElem->GetType() == SMDSAbs_Edge)
10998 edgesToCheck.insert(curElem);
11002 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11003 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11004 for( ; eit != edgesToCheck.end(); eit++)
11006 bool onside = true;
11007 const SMDS_MeshElement* anEdge = *eit;
11008 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11009 while ( nodeItr->more() )
11011 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11012 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11020 theAffectedElems.insert(anEdge);
11026 const double aTol = Precision::Confusion();
11027 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11028 auto_ptr<_FaceClassifier> aFaceClassifier;
11029 if ( theShape.ShapeType() == TopAbs_SOLID )
11031 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11032 bsc3d->PerformInfinitePoint(aTol);
11034 else if (theShape.ShapeType() == TopAbs_FACE )
11036 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11039 // iterates on indicated elements and get elements by back references from their nodes
11040 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11041 for ( ; elemItr != theElems.end(); ++elemItr )
11043 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11046 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11047 while ( nodeItr->more() )
11049 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11050 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11052 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11053 while ( backElemItr->more() )
11055 const SMDS_MeshElement* curElem = backElemItr->next();
11056 if ( curElem && theElems.find(curElem) == theElems.end() &&
11058 isInside( curElem, *bsc3d, aTol ) :
11059 isInside( curElem, *aFaceClassifier, aTol )))
11060 theAffectedElems.insert( curElem );
11068 //================================================================================
11070 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11071 \param theElems - group of of elements (edges or faces) to be replicated
11072 \param theNodesNot - group of nodes not to replicate
11073 \param theShape - shape to detect affected elements (element which geometric center
11074 located on or inside shape).
11075 The replicated nodes should be associated to affected elements.
11076 \return TRUE if operation has been completed successfully, FALSE otherwise
11078 //================================================================================
11080 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11081 const TIDSortedElemSet& theNodesNot,
11082 const TopoDS_Shape& theShape )
11084 if ( theShape.IsNull() )
11087 const double aTol = Precision::Confusion();
11088 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11089 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11090 if ( theShape.ShapeType() == TopAbs_SOLID )
11092 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11093 bsc3d->PerformInfinitePoint(aTol);
11095 else if (theShape.ShapeType() == TopAbs_FACE )
11097 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11100 // iterates on indicated elements and get elements by back references from their nodes
11101 TIDSortedElemSet anAffected;
11102 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11103 for ( ; elemItr != theElems.end(); ++elemItr )
11105 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11109 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11110 while ( nodeItr->more() )
11112 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11113 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11115 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11116 while ( backElemItr->more() )
11118 const SMDS_MeshElement* curElem = backElemItr->next();
11119 if ( curElem && theElems.find(curElem) == theElems.end() &&
11121 isInside( curElem, *bsc3d, aTol ) :
11122 isInside( curElem, *aFaceClassifier, aTol )))
11123 anAffected.insert( curElem );
11127 return DoubleNodes( theElems, theNodesNot, anAffected );
11131 * \brief compute an oriented angle between two planes defined by four points.
11132 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11133 * @param p0 base of the rotation axe
11134 * @param p1 extremity of the rotation axe
11135 * @param g1 belongs to the first plane
11136 * @param g2 belongs to the second plane
11138 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11140 gp_Vec vref(p0, p1);
11143 gp_Vec n1 = vref.Crossed(v1);
11144 gp_Vec n2 = vref.Crossed(v2);
11146 return n2.AngleWithRef(n1, vref);
11148 catch ( Standard_Failure ) {
11150 return Max( v1.Magnitude(), v2.Magnitude() );
11154 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11155 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11156 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11157 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11158 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11159 * 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.
11160 * 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.
11161 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11162 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11163 * \param theElems - list of groups of volumes, where a group of volume is a set of
11164 * SMDS_MeshElements sorted by Id.
11165 * \param createJointElems - if TRUE, create the elements
11166 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11167 * the boundary between \a theDomains and the rest mesh
11168 * \return TRUE if operation has been completed successfully, FALSE otherwise
11170 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11171 bool createJointElems,
11172 bool onAllBoundaries)
11174 // MESSAGE("----------------------------------------------");
11175 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11176 // MESSAGE("----------------------------------------------");
11178 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11179 meshDS->BuildDownWardConnectivity(true);
11181 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11183 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11184 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11185 // build the list of nodes shared by 2 or more domains, with their domain indexes
11187 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11188 std::map<int,int>celldom; // cell vtkId --> domain
11189 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11190 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11191 faceDomains.clear();
11193 cellDomains.clear();
11194 nodeDomains.clear();
11195 std::map<int,int> emptyMap;
11196 std::set<int> emptySet;
11199 //MESSAGE(".. Number of domains :"<<theElems.size());
11201 TIDSortedElemSet theRestDomElems;
11202 const int iRestDom = -1;
11203 const int idom0 = onAllBoundaries ? iRestDom : 0;
11204 const int nbDomains = theElems.size();
11206 // Check if the domains do not share an element
11207 for (int idom = 0; idom < nbDomains-1; idom++)
11209 // MESSAGE("... Check of domain #" << idom);
11210 const TIDSortedElemSet& domain = theElems[idom];
11211 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11212 for (; elemItr != domain.end(); ++elemItr)
11214 const SMDS_MeshElement* anElem = *elemItr;
11215 int idombisdeb = idom + 1 ;
11216 // check if the element belongs to a domain further in the list
11217 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11219 const TIDSortedElemSet& domainbis = theElems[idombis];
11220 if ( domainbis.count( anElem ))
11222 MESSAGE(".... Domain #" << idom);
11223 MESSAGE(".... Domain #" << idombis);
11224 throw SALOME_Exception("The domains are not disjoint.");
11231 for (int idom = 0; idom < nbDomains; idom++)
11234 // --- build a map (face to duplicate --> volume to modify)
11235 // with all the faces shared by 2 domains (group of elements)
11236 // and corresponding volume of this domain, for each shared face.
11237 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11239 //MESSAGE("... Neighbors of domain #" << idom);
11240 const TIDSortedElemSet& domain = theElems[idom];
11241 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11242 for (; elemItr != domain.end(); ++elemItr)
11244 const SMDS_MeshElement* anElem = *elemItr;
11247 int vtkId = anElem->getVtkId();
11248 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11249 int neighborsVtkIds[NBMAXNEIGHBORS];
11250 int downIds[NBMAXNEIGHBORS];
11251 unsigned char downTypes[NBMAXNEIGHBORS];
11252 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11253 for (int n = 0; n < nbNeighbors; n++)
11255 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11256 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11257 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11260 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11262 // MESSAGE("Domain " << idombis);
11263 const TIDSortedElemSet& domainbis = theElems[idombis];
11264 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11266 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11268 DownIdType face(downIds[n], downTypes[n]);
11269 if (!faceDomains[face].count(idom))
11271 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11272 celldom[vtkId] = idom;
11273 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11277 theRestDomElems.insert( elem );
11278 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11279 celldom[neighborsVtkIds[n]] = iRestDom;
11287 //MESSAGE("Number of shared faces " << faceDomains.size());
11288 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11290 // --- explore the shared faces domain by domain,
11291 // explore the nodes of the face and see if they belong to a cell in the domain,
11292 // which has only a node or an edge on the border (not a shared face)
11294 for (int idomain = idom0; idomain < nbDomains; idomain++)
11296 //MESSAGE("Domain " << idomain);
11297 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11298 itface = faceDomains.begin();
11299 for (; itface != faceDomains.end(); ++itface)
11301 const std::map<int, int>& domvol = itface->second;
11302 if (!domvol.count(idomain))
11304 DownIdType face = itface->first;
11305 //MESSAGE(" --- face " << face.cellId);
11306 std::set<int> oldNodes;
11308 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11309 std::set<int>::iterator itn = oldNodes.begin();
11310 for (; itn != oldNodes.end(); ++itn)
11313 //MESSAGE(" node " << oldId);
11314 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11315 for (int i=0; i<l.ncells; i++)
11317 int vtkId = l.cells[i];
11318 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11319 if (!domain.count(anElem))
11321 int vtkType = grid->GetCellType(vtkId);
11322 int downId = grid->CellIdToDownId(vtkId);
11325 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11326 continue; // not OK at this stage of the algorithm:
11327 //no cells created after BuildDownWardConnectivity
11329 DownIdType aCell(downId, vtkType);
11330 cellDomains[aCell][idomain] = vtkId;
11331 celldom[vtkId] = idomain;
11332 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11338 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11339 // for each shared face, get the nodes
11340 // for each node, for each domain of the face, create a clone of the node
11342 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11343 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11344 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11346 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11347 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11348 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11350 //MESSAGE(".. Duplication of the nodes");
11351 for (int idomain = idom0; idomain < nbDomains; idomain++)
11353 itface = faceDomains.begin();
11354 for (; itface != faceDomains.end(); ++itface)
11356 const std::map<int, int>& domvol = itface->second;
11357 if (!domvol.count(idomain))
11359 DownIdType face = itface->first;
11360 //MESSAGE(" --- face " << face.cellId);
11361 std::set<int> oldNodes;
11363 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11364 std::set<int>::iterator itn = oldNodes.begin();
11365 for (; itn != oldNodes.end(); ++itn)
11368 if (nodeDomains[oldId].empty())
11370 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11371 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11373 std::map<int, int>::const_iterator itdom = domvol.begin();
11374 for (; itdom != domvol.end(); ++itdom)
11376 int idom = itdom->first;
11377 //MESSAGE(" domain " << idom);
11378 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11380 if (nodeDomains[oldId].size() >= 2) // a multiple node
11382 vector<int> orderedDoms;
11383 //MESSAGE("multiple node " << oldId);
11384 if (mutipleNodes.count(oldId))
11385 orderedDoms = mutipleNodes[oldId];
11388 map<int,int>::iterator it = nodeDomains[oldId].begin();
11389 for (; it != nodeDomains[oldId].end(); ++it)
11390 orderedDoms.push_back(it->first);
11392 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11393 //stringstream txt;
11394 //for (int i=0; i<orderedDoms.size(); i++)
11395 // txt << orderedDoms[i] << " ";
11396 //MESSAGE("orderedDoms " << txt.str());
11397 mutipleNodes[oldId] = orderedDoms;
11399 double *coords = grid->GetPoint(oldId);
11400 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11401 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11402 int newId = newNode->getVtkId();
11403 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11404 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11411 //MESSAGE(".. Creation of elements");
11412 for (int idomain = idom0; idomain < nbDomains; idomain++)
11414 itface = faceDomains.begin();
11415 for (; itface != faceDomains.end(); ++itface)
11417 std::map<int, int> domvol = itface->second;
11418 if (!domvol.count(idomain))
11420 DownIdType face = itface->first;
11421 //MESSAGE(" --- face " << face.cellId);
11422 std::set<int> oldNodes;
11424 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11425 int nbMultipleNodes = 0;
11426 std::set<int>::iterator itn = oldNodes.begin();
11427 for (; itn != oldNodes.end(); ++itn)
11430 if (mutipleNodes.count(oldId))
11433 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11435 //MESSAGE("multiple Nodes detected on a shared face");
11436 int downId = itface->first.cellId;
11437 unsigned char cellType = itface->first.cellType;
11438 // --- shared edge or shared face ?
11439 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11442 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11443 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11444 if (mutipleNodes.count(nodes[i]))
11445 if (!mutipleNodesToFace.count(nodes[i]))
11446 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11448 else // shared face (between two volumes)
11450 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11451 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11452 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11453 for (int ie =0; ie < nbEdges; ie++)
11456 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11457 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11459 vector<int> vn0 = mutipleNodes[nodes[0]];
11460 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11462 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11463 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11464 if ( vn0[i0] == vn1[i1] )
11465 doms.push_back( vn0[ i0 ]);
11466 if ( doms.size() > 2 )
11468 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11469 double *coords = grid->GetPoint(nodes[0]);
11470 gp_Pnt p0(coords[0], coords[1], coords[2]);
11471 coords = grid->GetPoint(nodes[nbNodes - 1]);
11472 gp_Pnt p1(coords[0], coords[1], coords[2]);
11474 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11475 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11476 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11477 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11478 for ( size_t id = 0; id < doms.size(); id++ )
11480 int idom = doms[id];
11481 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11482 for ( int ivol = 0; ivol < nbvol; ivol++ )
11484 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11485 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11486 if (domain.count(elem))
11488 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11489 domvol[idom] = svol;
11490 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11492 vtkIdType npts = 0;
11493 vtkIdType* pts = 0;
11494 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11495 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11498 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11499 angleDom[idom] = 0;
11503 gp_Pnt g(values[0], values[1], values[2]);
11504 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11505 //MESSAGE(" angle=" << angleDom[idom]);
11511 map<double, int> sortedDom; // sort domains by angle
11512 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11513 sortedDom[ia->second] = ia->first;
11514 vector<int> vnodes;
11516 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11518 vdom.push_back(ib->second);
11519 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11521 for (int ino = 0; ino < nbNodes; ino++)
11522 vnodes.push_back(nodes[ino]);
11523 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11532 // --- iterate on shared faces (volumes to modify, face to extrude)
11533 // get node id's of the face (id SMDS = id VTK)
11534 // create flat element with old and new nodes if requested
11536 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11537 // (domain1 X domain2) = domain1 + MAXINT*domain2
11539 std::map<int, std::map<long,int> > nodeQuadDomains;
11540 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11542 //MESSAGE(".. Creation of elements: simple junction");
11543 if (createJointElems)
11546 string joints2DName = "joints2D";
11547 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11548 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11549 string joints3DName = "joints3D";
11550 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11551 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11553 itface = faceDomains.begin();
11554 for (; itface != faceDomains.end(); ++itface)
11556 DownIdType face = itface->first;
11557 std::set<int> oldNodes;
11558 std::set<int>::iterator itn;
11560 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11562 std::map<int, int> domvol = itface->second;
11563 std::map<int, int>::iterator itdom = domvol.begin();
11564 int dom1 = itdom->first;
11565 int vtkVolId = itdom->second;
11567 int dom2 = itdom->first;
11568 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11570 stringstream grpname;
11573 grpname << dom1 << "_" << dom2;
11575 grpname << dom2 << "_" << dom1;
11576 string namegrp = grpname.str();
11577 if (!mapOfJunctionGroups.count(namegrp))
11578 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11579 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11581 sgrp->Add(vol->GetID());
11582 if (vol->GetType() == SMDSAbs_Volume)
11583 joints3DGrp->Add(vol->GetID());
11584 else if (vol->GetType() == SMDSAbs_Face)
11585 joints2DGrp->Add(vol->GetID());
11589 // --- create volumes on multiple domain intersection if requested
11590 // iterate on mutipleNodesToFace
11591 // iterate on edgesMultiDomains
11593 //MESSAGE(".. Creation of elements: multiple junction");
11594 if (createJointElems)
11596 // --- iterate on mutipleNodesToFace
11598 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11599 for (; itn != mutipleNodesToFace.end(); ++itn)
11601 int node = itn->first;
11602 vector<int> orderDom = itn->second;
11603 vector<vtkIdType> orderedNodes;
11604 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11605 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11606 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11608 stringstream grpname;
11610 grpname << 0 << "_" << 0;
11612 string namegrp = grpname.str();
11613 if (!mapOfJunctionGroups.count(namegrp))
11614 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11615 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11617 sgrp->Add(face->GetID());
11620 // --- iterate on edgesMultiDomains
11622 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11623 for (; ite != edgesMultiDomains.end(); ++ite)
11625 vector<int> nodes = ite->first;
11626 vector<int> orderDom = ite->second;
11627 vector<vtkIdType> orderedNodes;
11628 if (nodes.size() == 2)
11630 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11631 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11632 if ( orderDom.size() == 3 )
11633 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11634 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11636 for (int idom = orderDom.size()-1; idom >=0; idom--)
11637 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11638 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11641 string namegrp = "jointsMultiples";
11642 if (!mapOfJunctionGroups.count(namegrp))
11643 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11644 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11646 sgrp->Add(vol->GetID());
11650 //INFOS("Quadratic multiple joints not implemented");
11651 // TODO quadratic nodes
11656 // --- list the explicit faces and edges of the mesh that need to be modified,
11657 // i.e. faces and edges built with one or more duplicated nodes.
11658 // associate these faces or edges to their corresponding domain.
11659 // only the first domain found is kept when a face or edge is shared
11661 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11662 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11663 faceOrEdgeDom.clear();
11666 //MESSAGE(".. Modification of elements");
11667 for (int idomain = idom0; idomain < nbDomains; idomain++)
11669 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11670 for (; itnod != nodeDomains.end(); ++itnod)
11672 int oldId = itnod->first;
11673 //MESSAGE(" node " << oldId);
11674 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11675 for (int i = 0; i < l.ncells; i++)
11677 int vtkId = l.cells[i];
11678 int vtkType = grid->GetCellType(vtkId);
11679 int downId = grid->CellIdToDownId(vtkId);
11681 continue; // new cells: not to be modified
11682 DownIdType aCell(downId, vtkType);
11683 int volParents[1000];
11684 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11685 for (int j = 0; j < nbvol; j++)
11686 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11687 if (!feDom.count(vtkId))
11689 feDom[vtkId] = idomain;
11690 faceOrEdgeDom[aCell] = emptyMap;
11691 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11692 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11693 // << " type " << vtkType << " downId " << downId);
11699 // --- iterate on shared faces (volumes to modify, face to extrude)
11700 // get node id's of the face
11701 // replace old nodes by new nodes in volumes, and update inverse connectivity
11703 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11704 for (int m=0; m<3; m++)
11706 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11707 itface = (*amap).begin();
11708 for (; itface != (*amap).end(); ++itface)
11710 DownIdType face = itface->first;
11711 std::set<int> oldNodes;
11712 std::set<int>::iterator itn;
11714 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11715 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11716 std::map<int, int> localClonedNodeIds;
11718 std::map<int, int> domvol = itface->second;
11719 std::map<int, int>::iterator itdom = domvol.begin();
11720 for (; itdom != domvol.end(); ++itdom)
11722 int idom = itdom->first;
11723 int vtkVolId = itdom->second;
11724 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11725 localClonedNodeIds.clear();
11726 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11729 if (nodeDomains[oldId].count(idom))
11731 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11732 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11735 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11740 // Remove empty groups (issue 0022812)
11741 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11742 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11744 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11745 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11748 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11749 grid->DeleteLinks();
11757 * \brief Double nodes on some external faces and create flat elements.
11758 * Flat elements are mainly used by some types of mechanic calculations.
11760 * Each group of the list must be constituted of faces.
11761 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11762 * @param theElems - list of groups of faces, where a group of faces is a set of
11763 * SMDS_MeshElements sorted by Id.
11764 * @return TRUE if operation has been completed successfully, FALSE otherwise
11766 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11768 // MESSAGE("-------------------------------------------------");
11769 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11770 // MESSAGE("-------------------------------------------------");
11772 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11774 // --- For each group of faces
11775 // duplicate the nodes, create a flat element based on the face
11776 // replace the nodes of the faces by their clones
11778 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11779 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11780 clonedNodes.clear();
11781 intermediateNodes.clear();
11782 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11783 mapOfJunctionGroups.clear();
11785 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11787 const TIDSortedElemSet& domain = theElems[idom];
11788 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11789 for ( ; elemItr != domain.end(); ++elemItr )
11791 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11792 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11795 // MESSAGE("aFace=" << aFace->GetID());
11796 bool isQuad = aFace->IsQuadratic();
11797 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11799 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11801 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11802 while (nodeIt->more())
11804 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11805 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11807 ln2.push_back(node);
11809 ln0.push_back(node);
11811 const SMDS_MeshNode* clone = 0;
11812 if (!clonedNodes.count(node))
11814 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11815 copyPosition( node, clone );
11816 clonedNodes[node] = clone;
11819 clone = clonedNodes[node];
11822 ln3.push_back(clone);
11824 ln1.push_back(clone);
11826 const SMDS_MeshNode* inter = 0;
11827 if (isQuad && (!isMedium))
11829 if (!intermediateNodes.count(node))
11831 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11832 copyPosition( node, inter );
11833 intermediateNodes[node] = inter;
11836 inter = intermediateNodes[node];
11837 ln4.push_back(inter);
11841 // --- extrude the face
11843 vector<const SMDS_MeshNode*> ln;
11844 SMDS_MeshVolume* vol = 0;
11845 vtkIdType aType = aFace->GetVtkType();
11849 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11850 // MESSAGE("vol prism " << vol->GetID());
11851 ln.push_back(ln1[0]);
11852 ln.push_back(ln1[1]);
11853 ln.push_back(ln1[2]);
11856 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11857 // MESSAGE("vol hexa " << vol->GetID());
11858 ln.push_back(ln1[0]);
11859 ln.push_back(ln1[1]);
11860 ln.push_back(ln1[2]);
11861 ln.push_back(ln1[3]);
11863 case VTK_QUADRATIC_TRIANGLE:
11864 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11865 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11866 // MESSAGE("vol quad prism " << vol->GetID());
11867 ln.push_back(ln1[0]);
11868 ln.push_back(ln1[1]);
11869 ln.push_back(ln1[2]);
11870 ln.push_back(ln3[0]);
11871 ln.push_back(ln3[1]);
11872 ln.push_back(ln3[2]);
11874 case VTK_QUADRATIC_QUAD:
11875 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11876 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11877 // ln4[0], ln4[1], ln4[2], ln4[3]);
11878 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11879 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11880 ln4[0], ln4[1], ln4[2], ln4[3]);
11881 // MESSAGE("vol quad hexa " << vol->GetID());
11882 ln.push_back(ln1[0]);
11883 ln.push_back(ln1[1]);
11884 ln.push_back(ln1[2]);
11885 ln.push_back(ln1[3]);
11886 ln.push_back(ln3[0]);
11887 ln.push_back(ln3[1]);
11888 ln.push_back(ln3[2]);
11889 ln.push_back(ln3[3]);
11899 stringstream grpname;
11903 string namegrp = grpname.str();
11904 if (!mapOfJunctionGroups.count(namegrp))
11905 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11906 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11908 sgrp->Add(vol->GetID());
11911 // --- modify the face
11913 aFace->ChangeNodes(&ln[0], ln.size());
11920 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11921 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11922 * groups of faces to remove inside the object, (idem edges).
11923 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11925 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11926 const TopoDS_Shape& theShape,
11927 SMESH_NodeSearcher* theNodeSearcher,
11928 const char* groupName,
11929 std::vector<double>& nodesCoords,
11930 std::vector<std::vector<int> >& listOfListOfNodes)
11932 // MESSAGE("--------------------------------");
11933 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11934 // MESSAGE("--------------------------------");
11936 // --- zone of volumes to remove is given :
11937 // 1 either by a geom shape (one or more vertices) and a radius,
11938 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11939 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11940 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11941 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11942 // defined by it's name.
11944 SMESHDS_GroupBase* groupDS = 0;
11945 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11946 while ( groupIt->more() )
11949 SMESH_Group * group = groupIt->next();
11950 if ( !group ) continue;
11951 groupDS = group->GetGroupDS();
11952 if ( !groupDS || groupDS->IsEmpty() ) continue;
11953 std::string grpName = group->GetName();
11954 //MESSAGE("grpName=" << grpName);
11955 if (grpName == groupName)
11961 bool isNodeGroup = false;
11962 bool isNodeCoords = false;
11965 if (groupDS->GetType() != SMDSAbs_Node)
11967 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11970 if (nodesCoords.size() > 0)
11971 isNodeCoords = true; // a list o nodes given by their coordinates
11972 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11974 // --- define groups to build
11976 int idg; // --- group of SMDS volumes
11977 string grpvName = groupName;
11978 grpvName += "_vol";
11979 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11982 MESSAGE("group not created " << grpvName);
11985 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11987 int idgs; // --- group of SMDS faces on the skin
11988 string grpsName = groupName;
11989 grpsName += "_skin";
11990 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11993 MESSAGE("group not created " << grpsName);
11996 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11998 int idgi; // --- group of SMDS faces internal (several shapes)
11999 string grpiName = groupName;
12000 grpiName += "_internalFaces";
12001 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12004 MESSAGE("group not created " << grpiName);
12007 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12009 int idgei; // --- group of SMDS faces internal (several shapes)
12010 string grpeiName = groupName;
12011 grpeiName += "_internalEdges";
12012 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12015 MESSAGE("group not created " << grpeiName);
12018 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12020 // --- build downward connectivity
12022 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12023 meshDS->BuildDownWardConnectivity(true);
12024 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12026 // --- set of volumes detected inside
12028 std::set<int> setOfInsideVol;
12029 std::set<int> setOfVolToCheck;
12031 std::vector<gp_Pnt> gpnts;
12034 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12036 //MESSAGE("group of nodes provided");
12037 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12038 while ( elemIt->more() )
12040 const SMDS_MeshElement* elem = elemIt->next();
12043 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12046 SMDS_MeshElement* vol = 0;
12047 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12048 while (volItr->more())
12050 vol = (SMDS_MeshElement*)volItr->next();
12051 setOfInsideVol.insert(vol->getVtkId());
12052 sgrp->Add(vol->GetID());
12056 else if (isNodeCoords)
12058 //MESSAGE("list of nodes coordinates provided");
12061 while ( i < nodesCoords.size()-2 )
12063 double x = nodesCoords[i++];
12064 double y = nodesCoords[i++];
12065 double z = nodesCoords[i++];
12066 gp_Pnt p = gp_Pnt(x, y ,z);
12067 gpnts.push_back(p);
12068 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12072 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12074 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12075 TopTools_IndexedMapOfShape vertexMap;
12076 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12077 gp_Pnt p = gp_Pnt(0,0,0);
12078 if (vertexMap.Extent() < 1)
12081 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12083 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12084 p = BRep_Tool::Pnt(vertex);
12085 gpnts.push_back(p);
12086 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12090 if (gpnts.size() > 0)
12092 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12093 //MESSAGE("startNode->nodeId " << nodeId);
12095 double radius2 = radius*radius;
12096 //MESSAGE("radius2 " << radius2);
12098 // --- volumes on start node
12100 setOfVolToCheck.clear();
12101 SMDS_MeshElement* startVol = 0;
12102 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12103 while (volItr->more())
12105 startVol = (SMDS_MeshElement*)volItr->next();
12106 setOfVolToCheck.insert(startVol->getVtkId());
12108 if (setOfVolToCheck.empty())
12110 MESSAGE("No volumes found");
12114 // --- starting with central volumes then their neighbors, check if they are inside
12115 // or outside the domain, until no more new neighbor volume is inside.
12116 // Fill the group of inside volumes
12118 std::map<int, double> mapOfNodeDistance2;
12119 mapOfNodeDistance2.clear();
12120 std::set<int> setOfOutsideVol;
12121 while (!setOfVolToCheck.empty())
12123 std::set<int>::iterator it = setOfVolToCheck.begin();
12125 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12126 bool volInside = false;
12127 vtkIdType npts = 0;
12128 vtkIdType* pts = 0;
12129 grid->GetCellPoints(vtkId, npts, pts);
12130 for (int i=0; i<npts; i++)
12132 double distance2 = 0;
12133 if (mapOfNodeDistance2.count(pts[i]))
12135 distance2 = mapOfNodeDistance2[pts[i]];
12136 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12140 double *coords = grid->GetPoint(pts[i]);
12141 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12143 for ( size_t j = 0; j < gpnts.size(); j++ )
12145 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12146 if (d2 < distance2)
12149 if (distance2 < radius2)
12153 mapOfNodeDistance2[pts[i]] = distance2;
12154 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12156 if (distance2 < radius2)
12158 volInside = true; // one or more nodes inside the domain
12159 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12165 setOfInsideVol.insert(vtkId);
12166 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12167 int neighborsVtkIds[NBMAXNEIGHBORS];
12168 int downIds[NBMAXNEIGHBORS];
12169 unsigned char downTypes[NBMAXNEIGHBORS];
12170 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12171 for (int n = 0; n < nbNeighbors; n++)
12172 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12173 setOfVolToCheck.insert(neighborsVtkIds[n]);
12177 setOfOutsideVol.insert(vtkId);
12178 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12180 setOfVolToCheck.erase(vtkId);
12184 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12185 // If yes, add the volume to the inside set
12187 bool addedInside = true;
12188 std::set<int> setOfVolToReCheck;
12189 while (addedInside)
12191 //MESSAGE(" --------------------------- re check");
12192 addedInside = false;
12193 std::set<int>::iterator itv = setOfInsideVol.begin();
12194 for (; itv != setOfInsideVol.end(); ++itv)
12197 int neighborsVtkIds[NBMAXNEIGHBORS];
12198 int downIds[NBMAXNEIGHBORS];
12199 unsigned char downTypes[NBMAXNEIGHBORS];
12200 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12201 for (int n = 0; n < nbNeighbors; n++)
12202 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12203 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12205 setOfVolToCheck = setOfVolToReCheck;
12206 setOfVolToReCheck.clear();
12207 while (!setOfVolToCheck.empty())
12209 std::set<int>::iterator it = setOfVolToCheck.begin();
12211 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12213 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12214 int countInside = 0;
12215 int neighborsVtkIds[NBMAXNEIGHBORS];
12216 int downIds[NBMAXNEIGHBORS];
12217 unsigned char downTypes[NBMAXNEIGHBORS];
12218 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12219 for (int n = 0; n < nbNeighbors; n++)
12220 if (setOfInsideVol.count(neighborsVtkIds[n]))
12222 //MESSAGE("countInside " << countInside);
12223 if (countInside > 1)
12225 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12226 setOfInsideVol.insert(vtkId);
12227 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12228 addedInside = true;
12231 setOfVolToReCheck.insert(vtkId);
12233 setOfVolToCheck.erase(vtkId);
12237 // --- map of Downward faces at the boundary, inside the global volume
12238 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12239 // fill group of SMDS faces inside the volume (when several volume shapes)
12240 // fill group of SMDS faces on the skin of the global volume (if skin)
12242 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12243 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12244 std::set<int>::iterator it = setOfInsideVol.begin();
12245 for (; it != setOfInsideVol.end(); ++it)
12248 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12249 int neighborsVtkIds[NBMAXNEIGHBORS];
12250 int downIds[NBMAXNEIGHBORS];
12251 unsigned char downTypes[NBMAXNEIGHBORS];
12252 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12253 for (int n = 0; n < nbNeighbors; n++)
12255 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12256 if (neighborDim == 3)
12258 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12260 DownIdType face(downIds[n], downTypes[n]);
12261 boundaryFaces[face] = vtkId;
12263 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12264 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12265 if (vtkFaceId >= 0)
12267 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12268 // find also the smds edges on this face
12269 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12270 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12271 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12272 for (int i = 0; i < nbEdges; i++)
12274 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12275 if (vtkEdgeId >= 0)
12276 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12280 else if (neighborDim == 2) // skin of the volume
12282 DownIdType face(downIds[n], downTypes[n]);
12283 skinFaces[face] = vtkId;
12284 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12285 if (vtkFaceId >= 0)
12286 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12291 // --- identify the edges constituting the wire of each subshape on the skin
12292 // define polylines with the nodes of edges, equivalent to wires
12293 // project polylines on subshapes, and partition, to get geom faces
12295 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12296 std::set<int> emptySet;
12298 std::set<int> shapeIds;
12300 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12301 while (itelem->more())
12303 const SMDS_MeshElement *elem = itelem->next();
12304 int shapeId = elem->getshapeId();
12305 int vtkId = elem->getVtkId();
12306 if (!shapeIdToVtkIdSet.count(shapeId))
12308 shapeIdToVtkIdSet[shapeId] = emptySet;
12309 shapeIds.insert(shapeId);
12311 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12314 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12315 std::set<DownIdType, DownIdCompare> emptyEdges;
12316 emptyEdges.clear();
12318 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12319 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12321 int shapeId = itShape->first;
12322 //MESSAGE(" --- Shape ID --- "<< shapeId);
12323 shapeIdToEdges[shapeId] = emptyEdges;
12325 std::vector<int> nodesEdges;
12327 std::set<int>::iterator its = itShape->second.begin();
12328 for (; its != itShape->second.end(); ++its)
12331 //MESSAGE(" " << vtkId);
12332 int neighborsVtkIds[NBMAXNEIGHBORS];
12333 int downIds[NBMAXNEIGHBORS];
12334 unsigned char downTypes[NBMAXNEIGHBORS];
12335 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12336 for (int n = 0; n < nbNeighbors; n++)
12338 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12340 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12341 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12342 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12344 DownIdType edge(downIds[n], downTypes[n]);
12345 if (!shapeIdToEdges[shapeId].count(edge))
12347 shapeIdToEdges[shapeId].insert(edge);
12349 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12350 nodesEdges.push_back(vtkNodeId[0]);
12351 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12352 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12358 std::list<int> order;
12360 if (nodesEdges.size() > 0)
12362 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12363 nodesEdges[0] = -1;
12364 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12365 nodesEdges[1] = -1; // do not reuse this edge
12369 int nodeTofind = order.back(); // try first to push back
12371 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12372 if (nodesEdges[i] == nodeTofind)
12374 if ( i == (int) nodesEdges.size() )
12375 found = false; // no follower found on back
12378 if (i%2) // odd ==> use the previous one
12379 if (nodesEdges[i-1] < 0)
12383 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12384 nodesEdges[i-1] = -1;
12386 else // even ==> use the next one
12387 if (nodesEdges[i+1] < 0)
12391 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12392 nodesEdges[i+1] = -1;
12397 // try to push front
12399 nodeTofind = order.front(); // try to push front
12400 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12401 if ( nodesEdges[i] == nodeTofind )
12403 if ( i == (int)nodesEdges.size() )
12405 found = false; // no predecessor found on front
12408 if (i%2) // odd ==> use the previous one
12409 if (nodesEdges[i-1] < 0)
12413 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12414 nodesEdges[i-1] = -1;
12416 else // even ==> use the next one
12417 if (nodesEdges[i+1] < 0)
12421 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12422 nodesEdges[i+1] = -1;
12428 std::vector<int> nodes;
12429 nodes.push_back(shapeId);
12430 std::list<int>::iterator itl = order.begin();
12431 for (; itl != order.end(); itl++)
12433 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12434 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12436 listOfListOfNodes.push_back(nodes);
12439 // partition geom faces with blocFissure
12440 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12441 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12447 //================================================================================
12449 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12450 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12451 * \return TRUE if operation has been completed successfully, FALSE otherwise
12453 //================================================================================
12455 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12457 // iterates on volume elements and detect all free faces on them
12458 SMESHDS_Mesh* aMesh = GetMeshDS();
12462 ElemFeatures faceType( SMDSAbs_Face );
12463 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12464 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12467 const SMDS_MeshVolume* volume = vIt->next();
12468 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12469 vTool.SetExternalNormal();
12470 const int iQuad = volume->IsQuadratic();
12471 faceType.SetQuad( iQuad );
12472 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12474 if (!vTool.IsFreeFace(iface))
12477 vector<const SMDS_MeshNode *> nodes;
12478 int nbFaceNodes = vTool.NbFaceNodes(iface);
12479 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12481 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12482 nodes.push_back(faceNodes[inode]);
12484 if (iQuad) // add medium nodes
12486 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12487 nodes.push_back(faceNodes[inode]);
12488 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12489 nodes.push_back(faceNodes[8]);
12491 // add new face based on volume nodes
12492 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12494 nbExisted++; // face already exsist
12498 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12503 return ( nbFree == ( nbExisted + nbCreated ));
12508 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12510 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12512 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12515 //================================================================================
12517 * \brief Creates missing boundary elements
12518 * \param elements - elements whose boundary is to be checked
12519 * \param dimension - defines type of boundary elements to create
12520 * \param group - a group to store created boundary elements in
12521 * \param targetMesh - a mesh to store created boundary elements in
12522 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12523 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12524 * boundary elements will be copied into the targetMesh
12525 * \param toAddExistingBondary - if true, not only new but also pre-existing
12526 * boundary elements will be added into the new group
12527 * \param aroundElements - if true, elements will be created on boundary of given
12528 * elements else, on boundary of the whole mesh.
12529 * \return nb of added boundary elements
12531 //================================================================================
12533 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12534 Bnd_Dimension dimension,
12535 SMESH_Group* group/*=0*/,
12536 SMESH_Mesh* targetMesh/*=0*/,
12537 bool toCopyElements/*=false*/,
12538 bool toCopyExistingBoundary/*=false*/,
12539 bool toAddExistingBondary/*= false*/,
12540 bool aroundElements/*= false*/)
12542 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12543 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12544 // hope that all elements are of the same type, do not check them all
12545 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12546 throw SALOME_Exception(LOCALIZED("wrong element type"));
12549 toCopyElements = toCopyExistingBoundary = false;
12551 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12552 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12553 int nbAddedBnd = 0;
12555 // editor adding present bnd elements and optionally holding elements to add to the group
12556 SMESH_MeshEditor* presentEditor;
12557 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12558 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12560 SMESH_MesherHelper helper( *myMesh );
12561 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12562 SMDS_VolumeTool vTool;
12563 TIDSortedElemSet avoidSet;
12564 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12567 typedef vector<const SMDS_MeshNode*> TConnectivity;
12568 TConnectivity tgtNodes;
12569 ElemFeatures elemKind( missType ), elemToCopy;
12571 vector<const SMDS_MeshElement*> presentBndElems;
12572 vector<TConnectivity> missingBndElems;
12573 vector<int> freeFacets;
12574 TConnectivity nodes, elemNodes;
12576 SMDS_ElemIteratorPtr eIt;
12577 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12578 else eIt = elemSetIterator( elements );
12580 while (eIt->more())
12582 const SMDS_MeshElement* elem = eIt->next();
12583 const int iQuad = elem->IsQuadratic();
12584 elemKind.SetQuad( iQuad );
12586 // ------------------------------------------------------------------------------------
12587 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12588 // ------------------------------------------------------------------------------------
12589 presentBndElems.clear();
12590 missingBndElems.clear();
12591 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12592 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12594 const SMDS_MeshElement* otherVol = 0;
12595 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12597 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12598 ( !aroundElements || elements.count( otherVol )))
12600 freeFacets.push_back( iface );
12602 if ( missType == SMDSAbs_Face )
12603 vTool.SetExternalNormal();
12604 for ( size_t i = 0; i < freeFacets.size(); ++i )
12606 int iface = freeFacets[i];
12607 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12608 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12609 if ( missType == SMDSAbs_Edge ) // boundary edges
12611 nodes.resize( 2+iQuad );
12612 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12614 for ( size_t j = 0; j < nodes.size(); ++j )
12615 nodes[ j ] = nn[ i+j ];
12616 if ( const SMDS_MeshElement* edge =
12617 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12618 presentBndElems.push_back( edge );
12620 missingBndElems.push_back( nodes );
12623 else // boundary face
12626 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12627 nodes.push_back( nn[inode] ); // add corner nodes
12629 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12630 nodes.push_back( nn[inode] ); // add medium nodes
12631 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12633 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12635 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12636 SMDSAbs_Face, /*noMedium=*/false ))
12637 presentBndElems.push_back( f );
12639 missingBndElems.push_back( nodes );
12641 if ( targetMesh != myMesh )
12643 // add 1D elements on face boundary to be added to a new mesh
12644 const SMDS_MeshElement* edge;
12645 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12648 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12650 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12651 if ( edge && avoidSet.insert( edge ).second )
12652 presentBndElems.push_back( edge );
12658 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12660 avoidSet.clear(), avoidSet.insert( elem );
12661 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12662 SMDS_MeshElement::iterator() );
12663 elemNodes.push_back( elemNodes[0] );
12664 nodes.resize( 2 + iQuad );
12665 const int nbLinks = elem->NbCornerNodes();
12666 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12668 nodes[0] = elemNodes[iN];
12669 nodes[1] = elemNodes[iN+1+iQuad];
12670 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12671 continue; // not free link
12673 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12674 if ( const SMDS_MeshElement* edge =
12675 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12676 presentBndElems.push_back( edge );
12678 missingBndElems.push_back( nodes );
12682 // ---------------------------------
12683 // 2. Add missing boundary elements
12684 // ---------------------------------
12685 if ( targetMesh != myMesh )
12686 // instead of making a map of nodes in this mesh and targetMesh,
12687 // we create nodes with same IDs.
12688 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12690 TConnectivity& srcNodes = missingBndElems[i];
12691 tgtNodes.resize( srcNodes.size() );
12692 for ( inode = 0; inode < srcNodes.size(); ++inode )
12693 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12694 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12696 /*noMedium=*/false))
12698 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12702 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12704 TConnectivity& nodes = missingBndElems[ i ];
12705 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12707 /*noMedium=*/false))
12709 SMDS_MeshElement* newElem =
12710 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12711 nbAddedBnd += bool( newElem );
12713 // try to set a new element to a shape
12714 if ( myMesh->HasShapeToMesh() )
12717 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12718 const size_t nbN = nodes.size() / (iQuad+1 );
12719 for ( inode = 0; inode < nbN && ok; ++inode )
12721 pair<int, TopAbs_ShapeEnum> i_stype =
12722 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12723 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12724 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12726 if ( ok && mediumShapes.size() > 1 )
12728 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12729 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12730 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12732 if (( ok = ( stype_i->first != stype_i_0.first )))
12733 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12734 aMesh->IndexToShape( stype_i_0.second ));
12737 if ( ok && mediumShapes.begin()->first == missShapeType )
12738 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12742 // ----------------------------------
12743 // 3. Copy present boundary elements
12744 // ----------------------------------
12745 if ( toCopyExistingBoundary )
12746 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12748 const SMDS_MeshElement* e = presentBndElems[i];
12749 tgtNodes.resize( e->NbNodes() );
12750 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12751 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12752 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12754 else // store present elements to add them to a group
12755 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12757 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12760 } // loop on given elements
12762 // ---------------------------------------------
12763 // 4. Fill group with boundary elements
12764 // ---------------------------------------------
12767 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12768 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12769 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12771 tgtEditor.myLastCreatedElems.Clear();
12772 tgtEditor2.myLastCreatedElems.Clear();
12774 // -----------------------
12775 // 5. Copy given elements
12776 // -----------------------
12777 if ( toCopyElements && targetMesh != myMesh )
12779 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12780 else eIt = elemSetIterator( elements );
12781 while (eIt->more())
12783 const SMDS_MeshElement* elem = eIt->next();
12784 tgtNodes.resize( elem->NbNodes() );
12785 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12786 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12787 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12789 tgtEditor.myLastCreatedElems.Clear();
12795 //================================================================================
12797 * \brief Copy node position and set \a to node on the same geometry
12799 //================================================================================
12801 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12802 const SMDS_MeshNode* to )
12804 if ( !from || !to ) return;
12806 SMDS_PositionPtr pos = from->GetPosition();
12807 if ( !pos || from->getshapeId() < 1 ) return;
12809 switch ( pos->GetTypeOfPosition() )
12811 case SMDS_TOP_3DSPACE: break;
12813 case SMDS_TOP_FACE:
12815 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12816 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12817 fPos->GetUParameter(), fPos->GetVParameter() );
12820 case SMDS_TOP_EDGE:
12822 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12823 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12824 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12827 case SMDS_TOP_VERTEX:
12829 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12832 case SMDS_TOP_UNSPEC: