1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Return mesh DS
132 //================================================================================
134 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
136 return myMesh->GetMeshDS();
140 //================================================================================
142 * \brief Clears myLastCreatedNodes and myLastCreatedElems
144 //================================================================================
146 void SMESH_MeshEditor::ClearLastCreated()
148 myLastCreatedNodes.Clear();
149 myLastCreatedElems.Clear();
152 //================================================================================
154 * \brief Initializes members by an existing element
155 * \param [in] elem - the source element
156 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
158 //================================================================================
160 SMESH_MeshEditor::ElemFeatures&
161 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
165 myType = elem->GetType();
166 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
168 myIsPoly = elem->IsPoly();
171 myIsQuad = elem->IsQuadratic();
172 if ( myType == SMDSAbs_Volume && !basicOnly )
174 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
175 myPolyhedQuantities.swap( quant );
179 else if ( myType == SMDSAbs_Ball && !basicOnly )
181 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
187 //=======================================================================
191 //=======================================================================
194 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
195 const ElemFeatures& features)
197 SMDS_MeshElement* e = 0;
198 int nbnode = node.size();
199 SMESHDS_Mesh* mesh = GetMeshDS();
200 const int ID = features.myID;
202 switch ( features.myType ) {
204 if ( !features.myIsPoly ) {
206 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2] );
209 else if (nbnode == 4) {
210 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
211 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
213 else if (nbnode == 6) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
219 else if (nbnode == 7) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6] );
225 else if (nbnode == 8) {
226 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7], ID);
228 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
229 node[4], node[5], node[6], node[7] );
231 else if (nbnode == 9) {
232 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7], node[8], ID);
234 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
235 node[4], node[5], node[6], node[7], node[8] );
238 else if ( !features.myIsQuad )
240 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
241 else e = mesh->AddPolygonalFace (node );
243 else if ( nbnode % 2 == 0 ) // just a protection
245 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
246 else e = mesh->AddQuadPolygonalFace (node );
251 if ( !features.myIsPoly ) {
253 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
256 else if (nbnode == 5) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 else if (nbnode == 6) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], ID);
265 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 else if (nbnode == 8) {
269 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7], ID);
271 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7] );
274 else if (nbnode == 10) {
275 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
276 node[4], node[5], node[6], node[7],
277 node[8], node[9], ID);
278 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
279 node[4], node[5], node[6], node[7],
282 else if (nbnode == 12) {
283 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10], node[11], ID);
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10], node[11] );
290 else if (nbnode == 13) {
291 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292 node[4], node[5], node[6], node[7],
293 node[8], node[9], node[10],node[11],
295 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
300 else if (nbnode == 15) {
301 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302 node[4], node[5], node[6], node[7],
303 node[8], node[9], node[10],node[11],
304 node[12],node[13],node[14],ID);
305 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14] );
310 else if (nbnode == 20) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],ID);
316 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
317 node[4], node[5], node[6], node[7],
318 node[8], node[9], node[10],node[11],
319 node[12],node[13],node[14],node[15],
320 node[16],node[17],node[18],node[19] );
322 else if (nbnode == 27) {
323 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
324 node[4], node[5], node[6], node[7],
325 node[8], node[9], node[10],node[11],
326 node[12],node[13],node[14],node[15],
327 node[16],node[17],node[18],node[19],
328 node[20],node[21],node[22],node[23],
329 node[24],node[25],node[26], ID);
330 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
331 node[4], node[5], node[6], node[7],
332 node[8], node[9], node[10],node[11],
333 node[12],node[13],node[14],node[15],
334 node[16],node[17],node[18],node[19],
335 node[20],node[21],node[22],node[23],
336 node[24],node[25],node[26] );
339 else if ( !features.myIsQuad )
341 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
342 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
346 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
347 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
353 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
354 else e = mesh->AddEdge (node[0], node[1] );
356 else if ( nbnode == 3 ) {
357 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
358 else e = mesh->AddEdge (node[0], node[1], node[2] );
362 case SMDSAbs_0DElement:
364 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
365 else e = mesh->Add0DElement (node[0] );
370 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
371 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
375 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
376 else e = mesh->AddBall (node[0], features.myBallDiameter );
381 if ( e ) myLastCreatedElems.Append( e );
385 //=======================================================================
389 //=======================================================================
391 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
392 const ElemFeatures& features)
394 vector<const SMDS_MeshNode*> nodes;
395 nodes.reserve( nodeIDs.size() );
396 vector<int>::const_iterator id = nodeIDs.begin();
397 while ( id != nodeIDs.end() ) {
398 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
399 nodes.push_back( node );
403 return AddElement( nodes, features );
406 //=======================================================================
408 //purpose : Remove a node or an element.
409 // Modify a compute state of sub-meshes which become empty
410 //=======================================================================
412 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
415 myLastCreatedElems.Clear();
416 myLastCreatedNodes.Clear();
418 SMESHDS_Mesh* aMesh = GetMeshDS();
419 set< SMESH_subMesh *> smmap;
422 list<int>::const_iterator it = theIDs.begin();
423 for ( ; it != theIDs.end(); it++ ) {
424 const SMDS_MeshElement * elem;
426 elem = aMesh->FindNode( *it );
428 elem = aMesh->FindElement( *it );
432 // Notify VERTEX sub-meshes about modification
434 const SMDS_MeshNode* node = cast2Node( elem );
435 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436 if ( int aShapeID = node->getshapeId() )
437 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440 // Find sub-meshes to notify about modification
441 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442 // while ( nodeIt->more() ) {
443 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444 // const SMDS_PositionPtr& aPosition = node->GetPosition();
445 // if ( aPosition.get() ) {
446 // if ( int aShapeID = aPosition->GetShapeId() ) {
447 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448 // smmap.insert( sm );
455 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
457 aMesh->RemoveElement( elem );
461 // Notify sub-meshes about modification
462 if ( !smmap.empty() ) {
463 set< SMESH_subMesh *>::iterator smIt;
464 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468 // // Check if the whole mesh becomes empty
469 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
475 //================================================================================
477 * \brief Create 0D elements on all nodes of the given object except those
478 * nodes on which a 0D element already exists.
479 * \param elements - Elements on whose nodes to create 0D elements; if empty,
480 * the all mesh is treated
481 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
483 //================================================================================
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486 TIDSortedElemSet& all0DElems )
488 SMDS_ElemIteratorPtr elemIt;
489 vector< const SMDS_MeshElement* > allNodes;
490 if ( elements.empty() )
492 allNodes.reserve( GetMeshDS()->NbNodes() );
493 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
494 while ( elemIt->more() )
495 allNodes.push_back( elemIt->next() );
497 elemIt = elemSetIterator( allNodes );
501 elemIt = elemSetIterator( elements );
504 while ( elemIt->more() )
506 const SMDS_MeshElement* e = elemIt->next();
507 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
508 while ( nodeIt->more() )
510 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
511 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
513 all0DElems.insert( it0D->next() );
515 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
516 all0DElems.insert( myLastCreatedElems.Last() );
522 //=======================================================================
523 //function : FindShape
524 //purpose : Return an index of the shape theElem is on
525 // or zero if a shape not found
526 //=======================================================================
528 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
530 myLastCreatedElems.Clear();
531 myLastCreatedNodes.Clear();
533 SMESHDS_Mesh * aMesh = GetMeshDS();
534 if ( aMesh->ShapeToMesh().IsNull() )
537 int aShapeID = theElem->getshapeId();
541 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
542 if ( sm->Contains( theElem ))
545 if ( theElem->GetType() == SMDSAbs_Node ) {
546 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
549 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
552 TopoDS_Shape aShape; // the shape a node of theElem is on
553 if ( theElem->GetType() != SMDSAbs_Node )
555 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
556 while ( nodeIt->more() ) {
557 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
558 if ((aShapeID = node->getshapeId()) > 0) {
559 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
560 if ( sm->Contains( theElem ))
562 if ( aShape.IsNull() )
563 aShape = aMesh->IndexToShape( aShapeID );
569 // None of nodes is on a proper shape,
570 // find the shape among ancestors of aShape on which a node is
571 if ( !aShape.IsNull() ) {
572 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
573 for ( ; ancIt.More(); ancIt.Next() ) {
574 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
575 if ( sm && sm->Contains( theElem ))
576 return aMesh->ShapeToIndex( ancIt.Value() );
581 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
582 while ( const SMESHDS_SubMesh* sm = smIt->next() )
583 if ( sm->Contains( theElem ))
590 //=======================================================================
591 //function : IsMedium
593 //=======================================================================
595 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
596 const SMDSAbs_ElementType typeToCheck)
598 bool isMedium = false;
599 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
600 while (it->more() && !isMedium ) {
601 const SMDS_MeshElement* elem = it->next();
602 isMedium = elem->IsMediumNode(node);
607 //=======================================================================
608 //function : shiftNodesQuadTria
609 //purpose : Shift nodes in the array corresponded to quadratic triangle
610 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
611 //=======================================================================
613 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
615 const SMDS_MeshNode* nd1 = aNodes[0];
616 aNodes[0] = aNodes[1];
617 aNodes[1] = aNodes[2];
619 const SMDS_MeshNode* nd2 = aNodes[3];
620 aNodes[3] = aNodes[4];
621 aNodes[4] = aNodes[5];
625 //=======================================================================
626 //function : nbEdgeConnectivity
627 //purpose : return number of the edges connected with the theNode.
628 // if theEdges has connections with the other type of the
629 // elements, return -1
630 //=======================================================================
632 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
634 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
636 // while(elemIt->more()) {
641 return theNode->NbInverseElements();
644 //=======================================================================
645 //function : getNodesFromTwoTria
647 //=======================================================================
649 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
650 const SMDS_MeshElement * theTria2,
651 vector< const SMDS_MeshNode*>& N1,
652 vector< const SMDS_MeshNode*>& N2)
654 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
655 if ( N1.size() < 6 ) return false;
656 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
657 if ( N2.size() < 6 ) return false;
659 int sames[3] = {-1,-1,-1};
671 if(nbsames!=2) return false;
673 shiftNodesQuadTria(N1);
675 shiftNodesQuadTria(N1);
678 i = sames[0] + sames[1] + sames[2];
680 shiftNodesQuadTria(N2);
682 // now we receive following N1 and N2 (using numeration as in the image below)
683 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
684 // i.e. first nodes from both arrays form a new diagonal
688 //=======================================================================
689 //function : InverseDiag
690 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
691 // but having other common link.
692 // Return False if args are improper
693 //=======================================================================
695 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
696 const SMDS_MeshElement * theTria2 )
698 myLastCreatedElems.Clear();
699 myLastCreatedNodes.Clear();
701 if (!theTria1 || !theTria2)
704 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
705 if (!F1) return false;
706 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
707 if (!F2) return false;
708 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
709 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
711 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
712 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
716 // put nodes in array and find out indices of the same ones
717 const SMDS_MeshNode* aNodes [6];
718 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
720 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
721 while ( it->more() ) {
722 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
724 if ( i > 2 ) // theTria2
725 // find same node of theTria1
726 for ( int j = 0; j < 3; j++ )
727 if ( aNodes[ i ] == aNodes[ j ]) {
736 return false; // theTria1 is not a triangle
737 it = theTria2->nodesIterator();
739 if ( i == 6 && it->more() )
740 return false; // theTria2 is not a triangle
743 // find indices of 1,2 and of A,B in theTria1
744 int iA = -1, iB = 0, i1 = 0, i2 = 0;
745 for ( i = 0; i < 6; i++ ) {
746 if ( sameInd [ i ] == -1 ) {
751 if ( iA >= 0) iB = i;
755 // nodes 1 and 2 should not be the same
756 if ( aNodes[ i1 ] == aNodes[ i2 ] )
760 aNodes[ iA ] = aNodes[ i2 ];
762 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
764 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
765 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
769 } // end if(F1 && F2)
771 // check case of quadratic faces
772 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
773 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
775 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
776 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
780 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
781 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
789 vector< const SMDS_MeshNode* > N1;
790 vector< const SMDS_MeshNode* > N2;
791 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
793 // now we receive following N1 and N2 (using numeration as above image)
794 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
795 // i.e. first nodes from both arrays determ new diagonal
797 vector< const SMDS_MeshNode*> N1new( N1.size() );
798 vector< const SMDS_MeshNode*> N2new( N2.size() );
799 N1new.back() = N1.back(); // central node of biquadratic
800 N2new.back() = N2.back();
801 N1new[0] = N1[0]; N2new[0] = N1[0];
802 N1new[1] = N2[0]; N2new[1] = N1[1];
803 N1new[2] = N2[1]; N2new[2] = N2[0];
804 N1new[3] = N1[4]; N2new[3] = N1[3];
805 N1new[4] = N2[3]; N2new[4] = N2[5];
806 N1new[5] = N1[5]; N2new[5] = N1[4];
807 // change nodes in faces
808 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
809 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
811 // move the central node of biquadratic triangle
812 SMESH_MesherHelper helper( *GetMesh() );
813 for ( int is2nd = 0; is2nd < 2; ++is2nd )
815 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
816 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
817 if ( nodes.size() < 7 )
819 helper.SetSubShape( tria->getshapeId() );
820 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
824 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
825 SMESH_TNodeXYZ( nodes[4] ) +
826 SMESH_TNodeXYZ( nodes[5] )) / 3.;
831 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
832 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
833 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
835 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
836 xyz = S->Value( uv.X(), uv.Y() );
837 xyz.Transform( loc );
838 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
839 nodes[6]->getshapeId() > 0 )
840 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
842 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
847 //=======================================================================
848 //function : findTriangles
849 //purpose : find triangles sharing theNode1-theNode2 link
850 //=======================================================================
852 static bool findTriangles(const SMDS_MeshNode * theNode1,
853 const SMDS_MeshNode * theNode2,
854 const SMDS_MeshElement*& theTria1,
855 const SMDS_MeshElement*& theTria2)
857 if ( !theNode1 || !theNode2 ) return false;
859 theTria1 = theTria2 = 0;
861 set< const SMDS_MeshElement* > emap;
862 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
864 const SMDS_MeshElement* elem = it->next();
865 if ( elem->NbCornerNodes() == 3 )
868 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
870 const SMDS_MeshElement* elem = it->next();
871 if ( emap.count( elem )) {
879 // theTria1 must be element with minimum ID
880 if ( theTria2->GetID() < theTria1->GetID() )
881 std::swap( theTria2, theTria1 );
889 //=======================================================================
890 //function : InverseDiag
891 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
892 // with ones built on the same 4 nodes but having other common link.
893 // Return false if proper faces not found
894 //=======================================================================
896 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
897 const SMDS_MeshNode * theNode2)
899 myLastCreatedElems.Clear();
900 myLastCreatedNodes.Clear();
902 const SMDS_MeshElement *tr1, *tr2;
903 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
906 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
907 if (!F1) return false;
908 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
909 if (!F2) return false;
910 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
911 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
913 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
914 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
918 // put nodes in array
919 // and find indices of 1,2 and of A in tr1 and of B in tr2
920 int i, iA1 = 0, i1 = 0;
921 const SMDS_MeshNode* aNodes1 [3];
922 SMDS_ElemIteratorPtr it;
923 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
924 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes1[ i ] == theNode1 )
926 iA1 = i; // node A in tr1
927 else if ( aNodes1[ i ] != theNode2 )
931 const SMDS_MeshNode* aNodes2 [3];
932 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
933 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
934 if ( aNodes2[ i ] == theNode2 )
935 iB2 = i; // node B in tr2
936 else if ( aNodes2[ i ] != theNode1 )
940 // nodes 1 and 2 should not be the same
941 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
945 aNodes1[ iA1 ] = aNodes2[ i2 ];
947 aNodes2[ iB2 ] = aNodes1[ i1 ];
949 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
950 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
955 // check case of quadratic faces
956 return InverseDiag(tr1,tr2);
959 //=======================================================================
960 //function : getQuadrangleNodes
961 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
962 // fusion of triangles tr1 and tr2 having shared link on
963 // theNode1 and theNode2
964 //=======================================================================
966 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
967 const SMDS_MeshNode * theNode1,
968 const SMDS_MeshNode * theNode2,
969 const SMDS_MeshElement * tr1,
970 const SMDS_MeshElement * tr2 )
972 if( tr1->NbNodes() != tr2->NbNodes() )
974 // find the 4-th node to insert into tr1
975 const SMDS_MeshNode* n4 = 0;
976 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
978 while ( !n4 && i<3 ) {
979 const SMDS_MeshNode * n = cast2Node( it->next() );
981 bool isDiag = ( n == theNode1 || n == theNode2 );
985 // Make an array of nodes to be in a quadrangle
986 int iNode = 0, iFirstDiag = -1;
987 it = tr1->nodesIterator();
990 const SMDS_MeshNode * n = cast2Node( it->next() );
992 bool isDiag = ( n == theNode1 || n == theNode2 );
994 if ( iFirstDiag < 0 )
996 else if ( iNode - iFirstDiag == 1 )
997 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
999 else if ( n == n4 ) {
1000 return false; // tr1 and tr2 should not have all the same nodes
1002 theQuadNodes[ iNode++ ] = n;
1004 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1005 theQuadNodes[ iNode ] = n4;
1010 //=======================================================================
1011 //function : DeleteDiag
1012 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1013 // with a quadrangle built on the same 4 nodes.
1014 // Return false if proper faces not found
1015 //=======================================================================
1017 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1018 const SMDS_MeshNode * theNode2)
1020 myLastCreatedElems.Clear();
1021 myLastCreatedNodes.Clear();
1023 const SMDS_MeshElement *tr1, *tr2;
1024 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1027 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1028 if (!F1) return false;
1029 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1030 if (!F2) return false;
1031 SMESHDS_Mesh * aMesh = GetMeshDS();
1033 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1034 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1036 const SMDS_MeshNode* aNodes [ 4 ];
1037 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1040 const SMDS_MeshElement* newElem = 0;
1041 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1042 myLastCreatedElems.Append(newElem);
1043 AddToSameGroups( newElem, tr1, aMesh );
1044 int aShapeId = tr1->getshapeId();
1047 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1049 aMesh->RemoveElement( tr1 );
1050 aMesh->RemoveElement( tr2 );
1055 // check case of quadratic faces
1056 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1058 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1062 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1063 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1071 vector< const SMDS_MeshNode* > N1;
1072 vector< const SMDS_MeshNode* > N2;
1073 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1075 // now we receive following N1 and N2 (using numeration as above image)
1076 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1077 // i.e. first nodes from both arrays determ new diagonal
1079 const SMDS_MeshNode* aNodes[8];
1089 const SMDS_MeshElement* newElem = 0;
1090 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1091 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1092 myLastCreatedElems.Append(newElem);
1093 AddToSameGroups( newElem, tr1, aMesh );
1094 int aShapeId = tr1->getshapeId();
1097 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1099 aMesh->RemoveElement( tr1 );
1100 aMesh->RemoveElement( tr2 );
1102 // remove middle node (9)
1103 GetMeshDS()->RemoveNode( N1[4] );
1108 //=======================================================================
1109 //function : Reorient
1110 //purpose : Reverse theElement orientation
1111 //=======================================================================
1113 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1115 myLastCreatedElems.Clear();
1116 myLastCreatedNodes.Clear();
1120 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1121 if ( !it || !it->more() )
1124 const SMDSAbs_ElementType type = theElem->GetType();
1125 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1128 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1129 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1131 const SMDS_VtkVolume* aPolyedre =
1132 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1134 MESSAGE("Warning: bad volumic element");
1137 const int nbFaces = aPolyedre->NbFaces();
1138 vector<const SMDS_MeshNode *> poly_nodes;
1139 vector<int> quantities (nbFaces);
1141 // reverse each face of the polyedre
1142 for (int iface = 1; iface <= nbFaces; iface++) {
1143 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1144 quantities[iface - 1] = nbFaceNodes;
1146 for (inode = nbFaceNodes; inode >= 1; inode--) {
1147 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1148 poly_nodes.push_back(curNode);
1151 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1153 else // other elements
1155 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1156 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1157 if ( interlace.empty() )
1159 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1163 SMDS_MeshCell::applyInterlace( interlace, nodes );
1165 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1170 //================================================================================
1172 * \brief Reorient faces.
1173 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1174 * \param theDirection - desired direction of normal of \a theFace
1175 * \param theFace - one of \a theFaces that sould be oriented according to
1176 * \a theDirection and whose orientation defines orientation of other faces
1177 * \return number of reoriented faces.
1179 //================================================================================
1181 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1182 const gp_Dir& theDirection,
1183 const SMDS_MeshElement * theFace)
1186 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1188 if ( theFaces.empty() )
1190 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1191 while ( fIt->more() )
1192 theFaces.insert( theFaces.end(), fIt->next() );
1195 // orient theFace according to theDirection
1197 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1198 if ( normal * theDirection.XYZ() < 0 )
1199 nbReori += Reorient( theFace );
1201 // Orient other faces
1203 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1204 TIDSortedElemSet avoidSet;
1205 set< SMESH_TLink > checkedLinks;
1206 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1208 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1209 theFaces.erase( theFace );
1210 startFaces.insert( theFace );
1212 int nodeInd1, nodeInd2;
1213 const SMDS_MeshElement* otherFace;
1214 vector< const SMDS_MeshElement* > facesNearLink;
1215 vector< std::pair< int, int > > nodeIndsOfFace;
1217 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1218 while ( !startFaces.empty() )
1220 startFace = startFaces.begin();
1221 theFace = *startFace;
1222 startFaces.erase( startFace );
1223 if ( !visitedFaces.insert( theFace ).second )
1227 avoidSet.insert(theFace);
1229 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1231 const int nbNodes = theFace->NbCornerNodes();
1232 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1234 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1235 linkIt_isNew = checkedLinks.insert( link );
1236 if ( !linkIt_isNew.second )
1238 // link has already been checked and won't be encountered more
1239 // if the group (theFaces) is manifold
1240 //checkedLinks.erase( linkIt_isNew.first );
1244 facesNearLink.clear();
1245 nodeIndsOfFace.clear();
1246 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1248 &nodeInd1, &nodeInd2 )))
1249 if ( otherFace != theFace)
1251 facesNearLink.push_back( otherFace );
1252 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1253 avoidSet.insert( otherFace );
1255 if ( facesNearLink.size() > 1 )
1257 // NON-MANIFOLD mesh shell !
1258 // select a face most co-directed with theFace,
1259 // other faces won't be visited this time
1261 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1262 double proj, maxProj = -1;
1263 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1264 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1265 if (( proj = Abs( NF * NOF )) > maxProj ) {
1267 otherFace = facesNearLink[i];
1268 nodeInd1 = nodeIndsOfFace[i].first;
1269 nodeInd2 = nodeIndsOfFace[i].second;
1272 // not to visit rejected faces
1273 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1274 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1275 visitedFaces.insert( facesNearLink[i] );
1277 else if ( facesNearLink.size() == 1 )
1279 otherFace = facesNearLink[0];
1280 nodeInd1 = nodeIndsOfFace.back().first;
1281 nodeInd2 = nodeIndsOfFace.back().second;
1283 if ( otherFace && otherFace != theFace)
1285 // link must be reverse in otherFace if orientation ot otherFace
1286 // is same as that of theFace
1287 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1289 nbReori += Reorient( otherFace );
1291 startFaces.insert( otherFace );
1294 std::swap( link.first, link.second ); // reverse the link
1300 //================================================================================
1302 * \brief Reorient faces basing on orientation of adjacent volumes.
1303 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1304 * \param theVolumes - reference volumes.
1305 * \param theOutsideNormal - to orient faces to have their normal
1306 * pointing either \a outside or \a inside the adjacent volumes.
1307 * \return number of reoriented faces.
1309 //================================================================================
1311 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1312 TIDSortedElemSet & theVolumes,
1313 const bool theOutsideNormal)
1317 SMDS_ElemIteratorPtr faceIt;
1318 if ( theFaces.empty() )
1319 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1321 faceIt = elemSetIterator( theFaces );
1323 vector< const SMDS_MeshNode* > faceNodes;
1324 TIDSortedElemSet checkedVolumes;
1325 set< const SMDS_MeshNode* > faceNodesSet;
1326 SMDS_VolumeTool volumeTool;
1328 while ( faceIt->more() ) // loop on given faces
1330 const SMDS_MeshElement* face = faceIt->next();
1331 if ( face->GetType() != SMDSAbs_Face )
1334 const size_t nbCornersNodes = face->NbCornerNodes();
1335 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1337 checkedVolumes.clear();
1338 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1339 while ( vIt->more() )
1341 const SMDS_MeshElement* volume = vIt->next();
1343 if ( !checkedVolumes.insert( volume ).second )
1345 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1348 // is volume adjacent?
1349 bool allNodesCommon = true;
1350 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1351 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1352 if ( !allNodesCommon )
1355 // get nodes of a corresponding volume facet
1356 faceNodesSet.clear();
1357 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1358 volumeTool.Set( volume );
1359 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1360 if ( facetID < 0 ) continue;
1361 volumeTool.SetExternalNormal();
1362 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1364 // compare order of faceNodes and facetNodes
1365 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1367 for ( int i = 0; i < 2; ++i )
1369 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1370 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1371 if ( faceNodes[ iN ] == n )
1377 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1378 if ( isOutside != theOutsideNormal )
1379 nbReori += Reorient( face );
1381 } // loop on given faces
1386 //=======================================================================
1387 //function : getBadRate
1389 //=======================================================================
1391 static double getBadRate (const SMDS_MeshElement* theElem,
1392 SMESH::Controls::NumericalFunctorPtr& theCrit)
1394 SMESH::Controls::TSequenceOfXYZ P;
1395 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1397 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1398 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1401 //=======================================================================
1402 //function : QuadToTri
1403 //purpose : Cut quadrangles into triangles.
1404 // theCrit is used to select a diagonal to cut
1405 //=======================================================================
1407 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1408 SMESH::Controls::NumericalFunctorPtr theCrit)
1410 myLastCreatedElems.Clear();
1411 myLastCreatedNodes.Clear();
1413 if ( !theCrit.get() )
1416 SMESHDS_Mesh * aMesh = GetMeshDS();
1418 Handle(Geom_Surface) surface;
1419 SMESH_MesherHelper helper( *GetMesh() );
1421 TIDSortedElemSet::iterator itElem;
1422 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1424 const SMDS_MeshElement* elem = *itElem;
1425 if ( !elem || elem->GetType() != SMDSAbs_Face )
1427 if ( elem->NbCornerNodes() != 4 )
1430 // retrieve element nodes
1431 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1433 // compare two sets of possible triangles
1434 double aBadRate1, aBadRate2; // to what extent a set is bad
1435 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1436 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1437 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1439 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1440 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1441 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1443 const int aShapeId = FindShape( elem );
1444 const SMDS_MeshElement* newElem1 = 0;
1445 const SMDS_MeshElement* newElem2 = 0;
1447 if ( !elem->IsQuadratic() ) // split liner quadrangle
1449 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1450 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1451 if ( aBadRate1 <= aBadRate2 ) {
1452 // tr1 + tr2 is better
1453 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1454 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1457 // tr3 + tr4 is better
1458 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1459 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1462 else // split quadratic quadrangle
1464 helper.SetIsQuadratic( true );
1465 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1467 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1468 if ( aNodes.size() == 9 )
1470 helper.SetIsBiQuadratic( true );
1471 if ( aBadRate1 <= aBadRate2 )
1472 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1474 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1476 // create a new element
1477 if ( aBadRate1 <= aBadRate2 ) {
1478 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1479 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1482 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1483 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1487 // care of a new element
1489 myLastCreatedElems.Append(newElem1);
1490 myLastCreatedElems.Append(newElem2);
1491 AddToSameGroups( newElem1, elem, aMesh );
1492 AddToSameGroups( newElem2, elem, aMesh );
1494 // put a new triangle on the same shape
1496 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1497 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1499 aMesh->RemoveElement( elem );
1504 //=======================================================================
1506 * \brief Split each of given quadrangles into 4 triangles.
1507 * \param theElems - The faces to be splitted. If empty all faces are split.
1509 //=======================================================================
1511 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1513 myLastCreatedElems.Clear();
1514 myLastCreatedNodes.Clear();
1516 SMESH_MesherHelper helper( *GetMesh() );
1517 helper.SetElementsOnShape( true );
1519 SMDS_ElemIteratorPtr faceIt;
1520 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1521 else faceIt = elemSetIterator( theElems );
1524 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1526 vector< const SMDS_MeshNode* > nodes;
1527 SMESHDS_SubMesh* subMeshDS = 0;
1529 Handle(Geom_Surface) surface;
1530 TopLoc_Location loc;
1532 while ( faceIt->more() )
1534 const SMDS_MeshElement* quad = faceIt->next();
1535 if ( !quad || quad->NbCornerNodes() != 4 )
1538 // get a surface the quad is on
1540 if ( quad->getshapeId() < 1 )
1543 helper.SetSubShape( 0 );
1546 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1548 helper.SetSubShape( quad->getshapeId() );
1549 if ( !helper.GetSubShape().IsNull() &&
1550 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1552 F = TopoDS::Face( helper.GetSubShape() );
1553 surface = BRep_Tool::Surface( F, loc );
1554 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1558 helper.SetSubShape( 0 );
1563 // create a central node
1565 const SMDS_MeshNode* nCentral;
1566 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1568 if ( nodes.size() == 9 )
1570 nCentral = nodes.back();
1577 for ( ; iN < nodes.size(); ++iN )
1578 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1580 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1581 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1583 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1584 xyz[0], xyz[1], xyz[2], xyz[3],
1585 xyz[4], xyz[5], xyz[6], xyz[7] );
1589 for ( ; iN < nodes.size(); ++iN )
1590 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1592 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1593 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1595 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1596 uv[0], uv[1], uv[2], uv[3],
1597 uv[4], uv[5], uv[6], uv[7] );
1599 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1603 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1604 uv[8].X(), uv[8].Y() );
1605 myLastCreatedNodes.Append( nCentral );
1608 // create 4 triangles
1610 helper.SetIsQuadratic ( nodes.size() > 4 );
1611 helper.SetIsBiQuadratic( nodes.size() == 9 );
1612 if ( helper.GetIsQuadratic() )
1613 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1615 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1617 for ( int i = 0; i < 4; ++i )
1619 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1622 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1623 myLastCreatedElems.Append( tria );
1628 //=======================================================================
1629 //function : BestSplit
1630 //purpose : Find better diagonal for cutting.
1631 //=======================================================================
1633 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1634 SMESH::Controls::NumericalFunctorPtr theCrit)
1636 myLastCreatedElems.Clear();
1637 myLastCreatedNodes.Clear();
1642 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1645 if( theQuad->NbNodes()==4 ||
1646 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1648 // retrieve element nodes
1649 const SMDS_MeshNode* aNodes [4];
1650 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1652 //while (itN->more())
1654 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1656 // compare two sets of possible triangles
1657 double aBadRate1, aBadRate2; // to what extent a set is bad
1658 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1659 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1660 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1662 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1663 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1664 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1665 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1666 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1667 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1668 return 1; // diagonal 1-3
1670 return 2; // diagonal 2-4
1677 // Methods of splitting volumes into tetra
1679 const int theHexTo5_1[5*4+1] =
1681 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1683 const int theHexTo5_2[5*4+1] =
1685 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1687 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1689 const int theHexTo6_1[6*4+1] =
1691 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1693 const int theHexTo6_2[6*4+1] =
1695 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1697 const int theHexTo6_3[6*4+1] =
1699 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1701 const int theHexTo6_4[6*4+1] =
1703 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1705 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1707 const int thePyraTo2_1[2*4+1] =
1709 0, 1, 2, 4, 0, 2, 3, 4, -1
1711 const int thePyraTo2_2[2*4+1] =
1713 1, 2, 3, 4, 1, 3, 0, 4, -1
1715 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1717 const int thePentaTo3_1[3*4+1] =
1719 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1721 const int thePentaTo3_2[3*4+1] =
1723 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1725 const int thePentaTo3_3[3*4+1] =
1727 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1729 const int thePentaTo3_4[3*4+1] =
1731 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1733 const int thePentaTo3_5[3*4+1] =
1735 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1737 const int thePentaTo3_6[3*4+1] =
1739 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1741 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1742 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1744 // Methods of splitting hexahedron into prisms
1746 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1748 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1750 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1752 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1754 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1756 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1759 const int theHexTo2Prisms_BT_1[6*2+1] =
1761 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1763 const int theHexTo2Prisms_BT_2[6*2+1] =
1765 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1767 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1769 const int theHexTo2Prisms_LR_1[6*2+1] =
1771 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1773 const int theHexTo2Prisms_LR_2[6*2+1] =
1775 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1777 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1779 const int theHexTo2Prisms_FB_1[6*2+1] =
1781 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1783 const int theHexTo2Prisms_FB_2[6*2+1] =
1785 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1787 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1790 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1793 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1794 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1795 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1796 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1802 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1803 bool _baryNode; //!< additional node is to be created at cell barycenter
1804 bool _ownConn; //!< to delete _connectivity in destructor
1805 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1807 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1808 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1809 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1810 bool hasFacet( const TTriangleFacet& facet ) const
1812 if ( _nbCorners == 4 )
1814 const int* tetConn = _connectivity;
1815 for ( ; tetConn[0] >= 0; tetConn += 4 )
1816 if (( facet.contains( tetConn[0] ) +
1817 facet.contains( tetConn[1] ) +
1818 facet.contains( tetConn[2] ) +
1819 facet.contains( tetConn[3] )) == 3 )
1822 else // prism, _nbCorners == 6
1824 const int* prismConn = _connectivity;
1825 for ( ; prismConn[0] >= 0; prismConn += 6 )
1827 if (( facet.contains( prismConn[0] ) &&
1828 facet.contains( prismConn[1] ) &&
1829 facet.contains( prismConn[2] ))
1831 ( facet.contains( prismConn[3] ) &&
1832 facet.contains( prismConn[4] ) &&
1833 facet.contains( prismConn[5] )))
1841 //=======================================================================
1843 * \brief return TSplitMethod for the given element to split into tetrahedra
1845 //=======================================================================
1847 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1849 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1851 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1852 // an edge and a face barycenter; tertaherdons are based on triangles and
1853 // a volume barycenter
1854 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1856 // Find out how adjacent volumes are split
1858 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1859 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1860 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1862 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1863 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1864 if ( nbNodes < 4 ) continue;
1866 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1867 const int* nInd = vol.GetFaceNodesIndices( iF );
1870 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1871 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1872 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1873 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1877 int iCom = 0; // common node of triangle faces to split into
1878 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1880 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1881 nInd[ iQ * ( (iCom+1)%nbNodes )],
1882 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1883 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1884 nInd[ iQ * ( (iCom+2)%nbNodes )],
1885 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1886 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1888 triaSplits.push_back( t012 );
1889 triaSplits.push_back( t023 );
1894 if ( !triaSplits.empty() )
1895 hasAdjacentSplits = true;
1898 // Among variants of split method select one compliant with adjacent volumes
1900 TSplitMethod method;
1901 if ( !vol.Element()->IsPoly() && !is24TetMode )
1903 int nbVariants = 2, nbTet = 0;
1904 const int** connVariants = 0;
1905 switch ( vol.Element()->GetEntityType() )
1907 case SMDSEntity_Hexa:
1908 case SMDSEntity_Quad_Hexa:
1909 case SMDSEntity_TriQuad_Hexa:
1910 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1911 connVariants = theHexTo5, nbTet = 5;
1913 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1915 case SMDSEntity_Pyramid:
1916 case SMDSEntity_Quad_Pyramid:
1917 connVariants = thePyraTo2; nbTet = 2;
1919 case SMDSEntity_Penta:
1920 case SMDSEntity_Quad_Penta:
1921 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1926 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1928 // check method compliancy with adjacent tetras,
1929 // all found splits must be among facets of tetras described by this method
1930 method = TSplitMethod( nbTet, connVariants[variant] );
1931 if ( hasAdjacentSplits && method._nbSplits > 0 )
1933 bool facetCreated = true;
1934 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1936 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1937 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1938 facetCreated = method.hasFacet( *facet );
1940 if ( !facetCreated )
1941 method = TSplitMethod(0); // incompatible method
1945 if ( method._nbSplits < 1 )
1947 // No standard method is applicable, use a generic solution:
1948 // each facet of a volume is split into triangles and
1949 // each of triangles and a volume barycenter form a tetrahedron.
1951 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1953 int* connectivity = new int[ maxTetConnSize + 1 ];
1954 method._connectivity = connectivity;
1955 method._ownConn = true;
1956 method._baryNode = !isHex27; // to create central node or not
1959 int baryCenInd = vol.NbNodes() - int( isHex27 );
1960 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1962 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1963 const int* nInd = vol.GetFaceNodesIndices( iF );
1964 // find common node of triangle facets of tetra to create
1965 int iCommon = 0; // index in linear numeration
1966 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1967 if ( !triaSplits.empty() )
1970 const TTriangleFacet* facet = &triaSplits.front();
1971 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1972 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1973 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1976 else if ( nbNodes > 3 && !is24TetMode )
1978 // find the best method of splitting into triangles by aspect ratio
1979 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1980 map< double, int > badness2iCommon;
1981 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1982 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1983 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1986 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1988 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1989 nodes[ iQ*((iLast-1)%nbNodes)],
1990 nodes[ iQ*((iLast )%nbNodes)]);
1991 badness += getBadRate( &tria, aspectRatio );
1993 badness2iCommon.insert( make_pair( badness, iCommon ));
1995 // use iCommon with lowest badness
1996 iCommon = badness2iCommon.begin()->second;
1998 if ( iCommon >= nbNodes )
1999 iCommon = 0; // something wrong
2001 // fill connectivity of tetrahedra based on a current face
2002 int nbTet = nbNodes - 2;
2003 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2008 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2009 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2013 method._faceBaryNode[ iF ] = 0;
2014 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2017 for ( int i = 0; i < nbTet; ++i )
2019 int i1 = i, i2 = (i+1) % nbNodes;
2020 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2021 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2022 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2023 connectivity[ connSize++ ] = faceBaryCenInd;
2024 connectivity[ connSize++ ] = baryCenInd;
2029 for ( int i = 0; i < nbTet; ++i )
2031 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2032 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2033 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2034 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2035 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2036 connectivity[ connSize++ ] = baryCenInd;
2039 method._nbSplits += nbTet;
2041 } // loop on volume faces
2043 connectivity[ connSize++ ] = -1;
2045 } // end of generic solution
2049 //=======================================================================
2051 * \brief return TSplitMethod to split haxhedron into prisms
2053 //=======================================================================
2055 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2056 const int methodFlags,
2057 const int facetToSplit)
2059 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2061 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2063 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2065 static TSplitMethod to4methods[4]; // order BT, LR, FB
2066 if ( to4methods[iF]._nbSplits == 0 )
2070 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2071 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2072 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2075 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2076 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2077 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2080 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2081 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2082 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2084 default: return to4methods[3];
2086 to4methods[iF]._nbSplits = 4;
2087 to4methods[iF]._nbCorners = 6;
2089 return to4methods[iF];
2091 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2093 TSplitMethod method;
2095 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2097 const int nbVariants = 2, nbSplits = 2;
2098 const int** connVariants = 0;
2100 case 0: connVariants = theHexTo2Prisms_BT; break;
2101 case 1: connVariants = theHexTo2Prisms_LR; break;
2102 case 2: connVariants = theHexTo2Prisms_FB; break;
2103 default: return method;
2106 // look for prisms adjacent via facetToSplit and an opposite one
2107 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2109 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2110 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2111 if ( nbNodes != 4 ) return method;
2113 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2114 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2115 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2117 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2119 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2124 // there are adjacent prism
2125 for ( int variant = 0; variant < nbVariants; ++variant )
2127 // check method compliancy with adjacent prisms,
2128 // the found prism facets must be among facets of prisms described by current method
2129 method._nbSplits = nbSplits;
2130 method._nbCorners = 6;
2131 method._connectivity = connVariants[ variant ];
2132 if ( method.hasFacet( *t ))
2137 // No adjacent prisms. Select a variant with a best aspect ratio.
2139 double badness[2] = { 0., 0. };
2140 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2141 const SMDS_MeshNode** nodes = vol.GetNodes();
2142 for ( int variant = 0; variant < nbVariants; ++variant )
2143 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2145 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2146 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2148 method._connectivity = connVariants[ variant ];
2149 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2150 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2151 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2153 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2156 badness[ variant ] += getBadRate( &tria, aspectRatio );
2158 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2160 method._nbSplits = nbSplits;
2161 method._nbCorners = 6;
2162 method._connectivity = connVariants[ iBetter ];
2167 //================================================================================
2169 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2171 //================================================================================
2173 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2174 const SMDSAbs_GeometryType geom ) const
2176 // find the tetrahedron including the three nodes of facet
2177 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2178 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2179 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2180 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2181 while ( volIt1->more() )
2183 const SMDS_MeshElement* v = volIt1->next();
2184 if ( v->GetGeomType() != geom )
2186 const int lastCornerInd = v->NbCornerNodes() - 1;
2187 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2188 continue; // medium node not allowed
2189 const int ind2 = v->GetNodeIndex( n2 );
2190 if ( ind2 < 0 || lastCornerInd < ind2 )
2192 const int ind3 = v->GetNodeIndex( n3 );
2193 if ( ind3 < 0 || lastCornerInd < ind3 )
2200 //=======================================================================
2202 * \brief A key of a face of volume
2204 //=======================================================================
2206 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2208 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2210 TIDSortedNodeSet sortedNodes;
2211 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2212 int nbNodes = vol.NbFaceNodes( iF );
2213 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2214 for ( int i = 0; i < nbNodes; i += iQ )
2215 sortedNodes.insert( fNodes[i] );
2216 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2217 first.first = (*(n++))->GetID();
2218 first.second = (*(n++))->GetID();
2219 second.first = (*(n++))->GetID();
2220 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2225 //=======================================================================
2226 //function : SplitVolumes
2227 //purpose : Split volume elements into tetrahedra or prisms.
2228 // If facet ID < 0, element is split into tetrahedra,
2229 // else a hexahedron is split into prisms so that the given facet is
2230 // split into triangles
2231 //=======================================================================
2233 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2234 const int theMethodFlags)
2236 SMDS_VolumeTool volTool;
2237 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2238 fHelper.ToFixNodeParameters( true );
2240 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2241 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2243 SMESH_SequenceOfElemPtr newNodes, newElems;
2245 // map face of volume to it's baricenrtic node
2246 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2248 vector<const SMDS_MeshElement* > splitVols;
2250 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2251 for ( ; elem2facet != theElems.end(); ++elem2facet )
2253 const SMDS_MeshElement* elem = elem2facet->first;
2254 const int facetToSplit = elem2facet->second;
2255 if ( elem->GetType() != SMDSAbs_Volume )
2257 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2258 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2261 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2263 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2264 getTetraSplitMethod( volTool, theMethodFlags ) :
2265 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2266 if ( splitMethod._nbSplits < 1 ) continue;
2268 // find submesh to add new tetras to
2269 if ( !subMesh || !subMesh->Contains( elem ))
2271 int shapeID = FindShape( elem );
2272 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2273 subMesh = GetMeshDS()->MeshElements( shapeID );
2276 if ( elem->IsQuadratic() )
2279 // add quadratic links to the helper
2280 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2282 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2283 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2284 for ( int iN = 0; iN < nbN; iN += iQ )
2285 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2287 helper.SetIsQuadratic( true );
2292 helper.SetIsQuadratic( false );
2294 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2295 volTool.GetNodes() + elem->NbNodes() );
2296 helper.SetElementsOnShape( true );
2297 if ( splitMethod._baryNode )
2299 // make a node at barycenter
2300 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2301 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2302 nodes.push_back( gcNode );
2303 newNodes.Append( gcNode );
2305 if ( !splitMethod._faceBaryNode.empty() )
2307 // make or find baricentric nodes of faces
2308 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2309 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2311 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2312 volFace2BaryNode.insert
2313 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2316 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2317 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2319 nodes.push_back( iF_n->second = f_n->second );
2324 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2325 const int* volConn = splitMethod._connectivity;
2326 if ( splitMethod._nbCorners == 4 ) // tetra
2327 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329 nodes[ volConn[1] ],
2330 nodes[ volConn[2] ],
2331 nodes[ volConn[3] ]));
2333 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2334 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2335 nodes[ volConn[1] ],
2336 nodes[ volConn[2] ],
2337 nodes[ volConn[3] ],
2338 nodes[ volConn[4] ],
2339 nodes[ volConn[5] ]));
2341 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2343 // Split faces on sides of the split volume
2345 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2346 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2348 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2349 if ( nbNodes < 4 ) continue;
2351 // find an existing face
2352 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2353 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2354 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2355 /*noMedium=*/false))
2358 helper.SetElementsOnShape( false );
2359 vector< const SMDS_MeshElement* > triangles;
2361 // find submesh to add new triangles in
2362 if ( !fSubMesh || !fSubMesh->Contains( face ))
2364 int shapeID = FindShape( face );
2365 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2367 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2368 if ( iF_n != splitMethod._faceBaryNode.end() )
2370 const SMDS_MeshNode *baryNode = iF_n->second;
2371 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2373 const SMDS_MeshNode* n1 = fNodes[iN];
2374 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2375 const SMDS_MeshNode *n3 = baryNode;
2376 if ( !volTool.IsFaceExternal( iF ))
2378 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2380 if ( fSubMesh ) // update position of the bary node on geometry
2383 subMesh->RemoveNode( baryNode, false );
2384 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2385 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2386 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2388 fHelper.SetSubShape( s );
2389 gp_XY uv( 1e100, 1e100 );
2391 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2392 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2395 // node is too far from the surface
2396 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2397 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2398 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2405 // among possible triangles create ones discribed by split method
2406 const int* nInd = volTool.GetFaceNodesIndices( iF );
2407 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2408 int iCom = 0; // common node of triangle faces to split into
2409 list< TTriangleFacet > facets;
2410 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2412 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2413 nInd[ iQ * ( (iCom+1)%nbNodes )],
2414 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2415 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2416 nInd[ iQ * ( (iCom+2)%nbNodes )],
2417 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2418 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2420 facets.push_back( t012 );
2421 facets.push_back( t023 );
2422 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2423 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2424 nInd[ iQ * ((iLast-1)%nbNodes )],
2425 nInd[ iQ * ((iLast )%nbNodes )]));
2429 list< TTriangleFacet >::iterator facet = facets.begin();
2430 if ( facet == facets.end() )
2432 for ( ; facet != facets.end(); ++facet )
2434 if ( !volTool.IsFaceExternal( iF ))
2435 swap( facet->_n2, facet->_n3 );
2436 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2437 volNodes[ facet->_n2 ],
2438 volNodes[ facet->_n3 ]));
2441 for ( size_t i = 0; i < triangles.size(); ++i )
2443 if ( !triangles[ i ]) continue;
2445 fSubMesh->AddElement( triangles[ i ]);
2446 newElems.Append( triangles[ i ]);
2448 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2449 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2451 } // while a face based on facet nodes exists
2452 } // loop on volume faces to split them into triangles
2454 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2456 if ( geomType == SMDSEntity_TriQuad_Hexa )
2458 // remove medium nodes that could become free
2459 for ( int i = 20; i < volTool.NbNodes(); ++i )
2460 if ( volNodes[i]->NbInverseElements() == 0 )
2461 GetMeshDS()->RemoveNode( volNodes[i] );
2463 } // loop on volumes to split
2465 myLastCreatedNodes = newNodes;
2466 myLastCreatedElems = newElems;
2469 //=======================================================================
2470 //function : GetHexaFacetsToSplit
2471 //purpose : For hexahedra that will be split into prisms, finds facets to
2472 // split into triangles. Only hexahedra adjacent to the one closest
2473 // to theFacetNormal.Location() are returned.
2474 //param [in,out] theHexas - the hexahedra
2475 //param [in] theFacetNormal - facet normal
2476 //param [out] theFacets - the hexahedra and found facet IDs
2477 //=======================================================================
2479 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2480 const gp_Ax1& theFacetNormal,
2481 TFacetOfElem & theFacets)
2483 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2485 // Find a hexa closest to the location of theFacetNormal
2487 const SMDS_MeshElement* startHex;
2489 // get SMDS_ElemIteratorPtr on theHexas
2490 typedef const SMDS_MeshElement* TValue;
2491 typedef TIDSortedElemSet::iterator TSetIterator;
2492 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2493 typedef SMDS_MeshElement::GeomFilter TFilter;
2494 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2495 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2496 ( new TElemSetIter( theHexas.begin(),
2498 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2500 SMESH_ElementSearcher* searcher =
2501 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2503 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2508 throw SALOME_Exception( THIS_METHOD "startHex not found");
2511 // Select a facet of startHex by theFacetNormal
2513 SMDS_VolumeTool vTool( startHex );
2514 double norm[3], dot, maxDot = 0;
2516 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2517 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2519 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2527 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2529 // Fill theFacets starting from facetID of startHex
2531 // facets used for seach of volumes adjacent to already treated ones
2532 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2533 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2534 TFacetMap facetsToCheck;
2536 set<const SMDS_MeshNode*> facetNodes;
2537 const SMDS_MeshElement* curHex;
2539 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2543 // move in two directions from startHex via facetID
2544 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2547 int curFacet = facetID;
2548 if ( is2nd ) // do not treat startHex twice
2550 vTool.Set( curHex );
2551 if ( vTool.IsFreeFace( curFacet, &curHex ))
2557 vTool.GetFaceNodes( curFacet, facetNodes );
2558 vTool.Set( curHex );
2559 curFacet = vTool.GetFaceIndex( facetNodes );
2564 // store a facet to split
2565 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2567 theFacets.insert( make_pair( curHex, -1 ));
2570 if ( !allHex && !theHexas.count( curHex ))
2573 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2574 theFacets.insert( make_pair( curHex, curFacet ));
2575 if ( !facetIt2isNew.second )
2578 // remember not-to-split facets in facetsToCheck
2579 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2580 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2582 if ( iF == curFacet && iF == oppFacet )
2584 TVolumeFaceKey facetKey ( vTool, iF );
2585 TElemFacets elemFacet( facetIt2isNew.first, iF );
2586 pair< TFacetMap::iterator, bool > it2isnew =
2587 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2588 if ( !it2isnew.second )
2589 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2591 // pass to a volume adjacent via oppFacet
2592 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2598 // get a new curFacet
2599 vTool.GetFaceNodes( oppFacet, facetNodes );
2600 vTool.Set( curHex );
2601 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2604 } // move in two directions from startHex via facetID
2606 // Find a new startHex by facetsToCheck
2610 TFacetMap::iterator fIt = facetsToCheck.begin();
2611 while ( !startHex && fIt != facetsToCheck.end() )
2613 const TElemFacets& elemFacets = fIt->second;
2614 const SMDS_MeshElement* hex = elemFacets.first->first;
2615 int splitFacet = elemFacets.first->second;
2616 int lateralFacet = elemFacets.second;
2617 facetsToCheck.erase( fIt );
2618 fIt = facetsToCheck.begin();
2621 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2622 curHex->GetGeomType() != SMDSGeom_HEXA )
2624 if ( !allHex && !theHexas.count( curHex ))
2629 // find a facet of startHex to split
2631 set<const SMDS_MeshNode*> lateralNodes;
2632 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2633 vTool.GetFaceNodes( splitFacet, facetNodes );
2634 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2635 vTool.Set( startHex );
2636 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2638 // look for a facet of startHex having common nodes with facetNodes
2639 // but not lateralFacet
2640 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2642 if ( iF == lateralFacet )
2644 int nbCommonNodes = 0;
2645 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2646 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2647 nbCommonNodes += facetNodes.count( nn[ iN ]);
2649 if ( nbCommonNodes >= 2 )
2656 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2658 } // while ( startHex )
2665 //================================================================================
2667 * \brief Selects nodes of several elements according to a given interlace
2668 * \param [in] srcNodes - nodes to select from
2669 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2670 * \param [in] interlace - indices of nodes for all elements
2671 * \param [in] nbElems - nb of elements
2672 * \param [in] nbNodes - nb of nodes in each element
2673 * \param [in] mesh - the mesh
2674 * \param [out] elemQueue - a list to push elements found by the selected nodes
2675 * \param [in] type - type of elements to look for
2677 //================================================================================
2679 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2680 vector< const SMDS_MeshNode* >* tgtNodesVec,
2681 const int* interlace,
2684 SMESHDS_Mesh* mesh = 0,
2685 list< const SMDS_MeshElement* >* elemQueue=0,
2686 SMDSAbs_ElementType type=SMDSAbs_All)
2688 for ( int iE = 0; iE < nbElems; ++iE )
2690 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2691 const int* select = & interlace[iE*nbNodes];
2692 elemNodes.resize( nbNodes );
2693 for ( int iN = 0; iN < nbNodes; ++iN )
2694 elemNodes[iN] = srcNodes[ select[ iN ]];
2696 const SMDS_MeshElement* e;
2698 for ( int iE = 0; iE < nbElems; ++iE )
2699 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2700 elemQueue->push_back( e );
2704 //=======================================================================
2706 * Split bi-quadratic elements into linear ones without creation of additional nodes
2707 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2708 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2709 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2710 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2711 * will be split in order to keep the mesh conformal.
2712 * \param elems - elements to split
2714 //=======================================================================
2716 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2718 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2719 vector<const SMDS_MeshElement* > splitElems;
2720 list< const SMDS_MeshElement* > elemQueue;
2721 list< const SMDS_MeshElement* >::iterator elemIt;
2723 SMESHDS_Mesh * mesh = GetMeshDS();
2724 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2725 int nbElems, nbNodes;
2727 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2728 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2731 elemQueue.push_back( *elemSetIt );
2732 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2734 const SMDS_MeshElement* elem = *elemIt;
2735 switch( elem->GetEntityType() )
2737 case SMDSEntity_TriQuad_Hexa: // HEX27
2739 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2740 nbElems = nbNodes = 8;
2741 elemType = & hexaType;
2743 // get nodes for new elements
2744 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2745 { 1,9,20,8, 17,22,26,21 },
2746 { 2,10,20,9, 18,23,26,22 },
2747 { 3,11,20,10, 19,24,26,23 },
2748 { 16,21,26,24, 4,12,25,15 },
2749 { 17,22,26,21, 5,13,25,12 },
2750 { 18,23,26,22, 6,14,25,13 },
2751 { 19,24,26,23, 7,15,25,14 }};
2752 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2754 // add boundary faces to elemQueue
2755 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2756 { 4,5,6,7, 12,13,14,15, 25 },
2757 { 0,1,5,4, 8,17,12,16, 21 },
2758 { 1,2,6,5, 9,18,13,17, 22 },
2759 { 2,3,7,6, 10,19,14,18, 23 },
2760 { 3,0,4,7, 11,16,15,19, 24 }};
2761 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2763 // add boundary segments to elemQueue
2764 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2765 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2766 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2767 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2770 case SMDSEntity_BiQuad_Triangle: // TRIA7
2772 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2775 elemType = & quadType;
2777 // get nodes for new elements
2778 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2779 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2781 // add boundary segments to elemQueue
2782 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2783 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2786 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2788 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2791 elemType = & quadType;
2793 // get nodes for new elements
2794 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2795 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2797 // add boundary segments to elemQueue
2798 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2799 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2802 case SMDSEntity_Quad_Edge:
2804 if ( elemIt == elemQueue.begin() )
2805 continue; // an elem is in theElems
2806 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2809 elemType = & segType;
2811 // get nodes for new elements
2812 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2813 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2817 } // switch( elem->GetEntityType() )
2819 // Create new elements
2821 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2825 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2826 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2827 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2828 //elemType->SetID( -1 );
2830 for ( int iE = 0; iE < nbElems; ++iE )
2831 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2834 ReplaceElemInGroups( elem, splitElems, mesh );
2837 for ( size_t i = 0; i < splitElems.size(); ++i )
2838 subMesh->AddElement( splitElems[i] );
2843 //=======================================================================
2844 //function : AddToSameGroups
2845 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2846 //=======================================================================
2848 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2849 const SMDS_MeshElement* elemInGroups,
2850 SMESHDS_Mesh * aMesh)
2852 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2853 if (!groups.empty()) {
2854 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2855 for ( ; grIt != groups.end(); grIt++ ) {
2856 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2857 if ( group && group->Contains( elemInGroups ))
2858 group->SMDSGroup().Add( elemToAdd );
2864 //=======================================================================
2865 //function : RemoveElemFromGroups
2866 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2867 //=======================================================================
2868 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2869 SMESHDS_Mesh * aMesh)
2871 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2872 if (!groups.empty())
2874 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2875 for (; GrIt != groups.end(); GrIt++)
2877 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2878 if (!grp || grp->IsEmpty()) continue;
2879 grp->SMDSGroup().Remove(removeelem);
2884 //================================================================================
2886 * \brief Replace elemToRm by elemToAdd in the all groups
2888 //================================================================================
2890 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2891 const SMDS_MeshElement* elemToAdd,
2892 SMESHDS_Mesh * aMesh)
2894 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2895 if (!groups.empty()) {
2896 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2897 for ( ; grIt != groups.end(); grIt++ ) {
2898 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2899 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2900 group->SMDSGroup().Add( elemToAdd );
2905 //================================================================================
2907 * \brief Replace elemToRm by elemToAdd in the all groups
2909 //================================================================================
2911 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2912 const vector<const SMDS_MeshElement*>& elemToAdd,
2913 SMESHDS_Mesh * aMesh)
2915 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2916 if (!groups.empty())
2918 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2919 for ( ; grIt != groups.end(); grIt++ ) {
2920 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2921 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2922 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2923 group->SMDSGroup().Add( elemToAdd[ i ] );
2928 //=======================================================================
2929 //function : QuadToTri
2930 //purpose : Cut quadrangles into triangles.
2931 // theCrit is used to select a diagonal to cut
2932 //=======================================================================
2934 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2935 const bool the13Diag)
2937 myLastCreatedElems.Clear();
2938 myLastCreatedNodes.Clear();
2940 SMESHDS_Mesh * aMesh = GetMeshDS();
2942 Handle(Geom_Surface) surface;
2943 SMESH_MesherHelper helper( *GetMesh() );
2945 TIDSortedElemSet::iterator itElem;
2946 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2948 const SMDS_MeshElement* elem = *itElem;
2949 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2952 if ( elem->NbNodes() == 4 ) {
2953 // retrieve element nodes
2954 const SMDS_MeshNode* aNodes [4];
2955 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2957 while ( itN->more() )
2958 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2960 int aShapeId = FindShape( elem );
2961 const SMDS_MeshElement* newElem1 = 0;
2962 const SMDS_MeshElement* newElem2 = 0;
2964 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2965 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2968 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2969 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2971 myLastCreatedElems.Append(newElem1);
2972 myLastCreatedElems.Append(newElem2);
2973 // put a new triangle on the same shape and add to the same groups
2976 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2977 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2979 AddToSameGroups( newElem1, elem, aMesh );
2980 AddToSameGroups( newElem2, elem, aMesh );
2981 aMesh->RemoveElement( elem );
2984 // Quadratic quadrangle
2986 else if ( elem->NbNodes() >= 8 )
2988 // get surface elem is on
2989 int aShapeId = FindShape( elem );
2990 if ( aShapeId != helper.GetSubShapeID() ) {
2994 shape = aMesh->IndexToShape( aShapeId );
2995 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2996 TopoDS_Face face = TopoDS::Face( shape );
2997 surface = BRep_Tool::Surface( face );
2998 if ( !surface.IsNull() )
2999 helper.SetSubShape( shape );
3003 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3004 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3005 for ( int i = 0; itN->more(); ++i )
3006 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3008 const SMDS_MeshNode* centrNode = aNodes[8];
3009 if ( centrNode == 0 )
3011 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3012 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3014 myLastCreatedNodes.Append(centrNode);
3017 // create a new element
3018 const SMDS_MeshElement* newElem1 = 0;
3019 const SMDS_MeshElement* newElem2 = 0;
3021 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3022 aNodes[6], aNodes[7], centrNode );
3023 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3024 centrNode, aNodes[4], aNodes[5] );
3027 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3028 aNodes[7], aNodes[4], centrNode );
3029 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3030 centrNode, aNodes[5], aNodes[6] );
3032 myLastCreatedElems.Append(newElem1);
3033 myLastCreatedElems.Append(newElem2);
3034 // put a new triangle on the same shape and add to the same groups
3037 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3038 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3040 AddToSameGroups( newElem1, elem, aMesh );
3041 AddToSameGroups( newElem2, elem, aMesh );
3042 aMesh->RemoveElement( elem );
3049 //=======================================================================
3050 //function : getAngle
3052 //=======================================================================
3054 double getAngle(const SMDS_MeshElement * tr1,
3055 const SMDS_MeshElement * tr2,
3056 const SMDS_MeshNode * n1,
3057 const SMDS_MeshNode * n2)
3059 double angle = 2. * M_PI; // bad angle
3062 SMESH::Controls::TSequenceOfXYZ P1, P2;
3063 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3064 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3067 if(!tr1->IsQuadratic())
3068 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3070 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3071 if ( N1.SquareMagnitude() <= gp::Resolution() )
3073 if(!tr2->IsQuadratic())
3074 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3076 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3077 if ( N2.SquareMagnitude() <= gp::Resolution() )
3080 // find the first diagonal node n1 in the triangles:
3081 // take in account a diagonal link orientation
3082 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3083 for ( int t = 0; t < 2; t++ ) {
3084 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3085 int i = 0, iDiag = -1;
3086 while ( it->more()) {
3087 const SMDS_MeshElement *n = it->next();
3088 if ( n == n1 || n == n2 ) {
3092 if ( i - iDiag == 1 )
3093 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3102 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3105 angle = N1.Angle( N2 );
3110 // =================================================
3111 // class generating a unique ID for a pair of nodes
3112 // and able to return nodes by that ID
3113 // =================================================
3117 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3118 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3121 long GetLinkID (const SMDS_MeshNode * n1,
3122 const SMDS_MeshNode * n2) const
3124 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3127 bool GetNodes (const long theLinkID,
3128 const SMDS_MeshNode* & theNode1,
3129 const SMDS_MeshNode* & theNode2) const
3131 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3132 if ( !theNode1 ) return false;
3133 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3134 if ( !theNode2 ) return false;
3140 const SMESHDS_Mesh* myMesh;
3145 //=======================================================================
3146 //function : TriToQuad
3147 //purpose : Fuse neighbour triangles into quadrangles.
3148 // theCrit is used to select a neighbour to fuse with.
3149 // theMaxAngle is a max angle between element normals at which
3150 // fusion is still performed.
3151 //=======================================================================
3153 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3154 SMESH::Controls::NumericalFunctorPtr theCrit,
3155 const double theMaxAngle)
3157 myLastCreatedElems.Clear();
3158 myLastCreatedNodes.Clear();
3160 if ( !theCrit.get() )
3163 SMESHDS_Mesh * aMesh = GetMeshDS();
3165 // Prepare data for algo: build
3166 // 1. map of elements with their linkIDs
3167 // 2. map of linkIDs with their elements
3169 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3170 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3171 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3172 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3174 TIDSortedElemSet::iterator itElem;
3175 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3177 const SMDS_MeshElement* elem = *itElem;
3178 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3179 bool IsTria = ( elem->NbCornerNodes()==3 );
3180 if (!IsTria) continue;
3182 // retrieve element nodes
3183 const SMDS_MeshNode* aNodes [4];
3184 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3187 aNodes[ i++ ] = itN->next();
3188 aNodes[ 3 ] = aNodes[ 0 ];
3191 for ( i = 0; i < 3; i++ ) {
3192 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3193 // check if elements sharing a link can be fused
3194 itLE = mapLi_listEl.find( link );
3195 if ( itLE != mapLi_listEl.end() ) {
3196 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3198 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3199 //if ( FindShape( elem ) != FindShape( elem2 ))
3200 // continue; // do not fuse triangles laying on different shapes
3201 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3202 continue; // avoid making badly shaped quads
3203 (*itLE).second.push_back( elem );
3206 mapLi_listEl[ link ].push_back( elem );
3208 mapEl_setLi [ elem ].insert( link );
3211 // Clean the maps from the links shared by a sole element, ie
3212 // links to which only one element is bound in mapLi_listEl
3214 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3215 int nbElems = (*itLE).second.size();
3216 if ( nbElems < 2 ) {
3217 const SMDS_MeshElement* elem = (*itLE).second.front();
3218 SMESH_TLink link = (*itLE).first;
3219 mapEl_setLi[ elem ].erase( link );
3220 if ( mapEl_setLi[ elem ].empty() )
3221 mapEl_setLi.erase( elem );
3225 // Algo: fuse triangles into quadrangles
3227 while ( ! mapEl_setLi.empty() ) {
3228 // Look for the start element:
3229 // the element having the least nb of shared links
3230 const SMDS_MeshElement* startElem = 0;
3232 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3233 int nbLinks = (*itEL).second.size();
3234 if ( nbLinks < minNbLinks ) {
3235 startElem = (*itEL).first;
3236 minNbLinks = nbLinks;
3237 if ( minNbLinks == 1 )
3242 // search elements to fuse starting from startElem or links of elements
3243 // fused earlyer - startLinks
3244 list< SMESH_TLink > startLinks;
3245 while ( startElem || !startLinks.empty() ) {
3246 while ( !startElem && !startLinks.empty() ) {
3247 // Get an element to start, by a link
3248 SMESH_TLink linkId = startLinks.front();
3249 startLinks.pop_front();
3250 itLE = mapLi_listEl.find( linkId );
3251 if ( itLE != mapLi_listEl.end() ) {
3252 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3253 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3254 for ( ; itE != listElem.end() ; itE++ )
3255 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3257 mapLi_listEl.erase( itLE );
3262 // Get candidates to be fused
3263 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3264 const SMESH_TLink *link12 = 0, *link13 = 0;
3266 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3267 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3268 ASSERT( !setLi.empty() );
3269 set< SMESH_TLink >::iterator itLi;
3270 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3272 const SMESH_TLink & link = (*itLi);
3273 itLE = mapLi_listEl.find( link );
3274 if ( itLE == mapLi_listEl.end() )
3277 const SMDS_MeshElement* elem = (*itLE).second.front();
3279 elem = (*itLE).second.back();
3280 mapLi_listEl.erase( itLE );
3281 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3292 // add other links of elem to list of links to re-start from
3293 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3294 set< SMESH_TLink >::iterator it;
3295 for ( it = links.begin(); it != links.end(); it++ ) {
3296 const SMESH_TLink& link2 = (*it);
3297 if ( link2 != link )
3298 startLinks.push_back( link2 );
3302 // Get nodes of possible quadrangles
3303 const SMDS_MeshNode *n12 [4], *n13 [4];
3304 bool Ok12 = false, Ok13 = false;
3305 const SMDS_MeshNode *linkNode1, *linkNode2;
3307 linkNode1 = link12->first;
3308 linkNode2 = link12->second;
3309 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3313 linkNode1 = link13->first;
3314 linkNode2 = link13->second;
3315 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3319 // Choose a pair to fuse
3320 if ( Ok12 && Ok13 ) {
3321 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3322 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3323 double aBadRate12 = getBadRate( &quad12, theCrit );
3324 double aBadRate13 = getBadRate( &quad13, theCrit );
3325 if ( aBadRate13 < aBadRate12 )
3332 // and remove fused elems and remove links from the maps
3333 mapEl_setLi.erase( tr1 );
3336 mapEl_setLi.erase( tr2 );
3337 mapLi_listEl.erase( *link12 );
3338 if ( tr1->NbNodes() == 3 )
3340 const SMDS_MeshElement* newElem = 0;
3341 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3342 myLastCreatedElems.Append(newElem);
3343 AddToSameGroups( newElem, tr1, aMesh );
3344 int aShapeId = tr1->getshapeId();
3346 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3347 aMesh->RemoveElement( tr1 );
3348 aMesh->RemoveElement( tr2 );
3351 vector< const SMDS_MeshNode* > N1;
3352 vector< const SMDS_MeshNode* > N2;
3353 getNodesFromTwoTria(tr1,tr2,N1,N2);
3354 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3355 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3356 // i.e. first nodes from both arrays form a new diagonal
3357 const SMDS_MeshNode* aNodes[8];
3366 const SMDS_MeshElement* newElem = 0;
3367 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3368 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3369 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3371 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3372 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3373 myLastCreatedElems.Append(newElem);
3374 AddToSameGroups( newElem, tr1, aMesh );
3375 int aShapeId = tr1->getshapeId();
3377 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3378 aMesh->RemoveElement( tr1 );
3379 aMesh->RemoveElement( tr2 );
3380 // remove middle node (9)
3381 if ( N1[4]->NbInverseElements() == 0 )
3382 aMesh->RemoveNode( N1[4] );
3383 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3384 aMesh->RemoveNode( N1[6] );
3385 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3386 aMesh->RemoveNode( N2[6] );
3391 mapEl_setLi.erase( tr3 );
3392 mapLi_listEl.erase( *link13 );
3393 if ( tr1->NbNodes() == 3 ) {
3394 const SMDS_MeshElement* newElem = 0;
3395 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3396 myLastCreatedElems.Append(newElem);
3397 AddToSameGroups( newElem, tr1, aMesh );
3398 int aShapeId = tr1->getshapeId();
3400 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3401 aMesh->RemoveElement( tr1 );
3402 aMesh->RemoveElement( tr3 );
3405 vector< const SMDS_MeshNode* > N1;
3406 vector< const SMDS_MeshNode* > N2;
3407 getNodesFromTwoTria(tr1,tr3,N1,N2);
3408 // now we receive following N1 and N2 (using numeration as above image)
3409 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3410 // i.e. first nodes from both arrays form a new diagonal
3411 const SMDS_MeshNode* aNodes[8];
3420 const SMDS_MeshElement* newElem = 0;
3421 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3422 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3423 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3425 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3426 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3427 myLastCreatedElems.Append(newElem);
3428 AddToSameGroups( newElem, tr1, aMesh );
3429 int aShapeId = tr1->getshapeId();
3431 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3432 aMesh->RemoveElement( tr1 );
3433 aMesh->RemoveElement( tr3 );
3434 // remove middle node (9)
3435 if ( N1[4]->NbInverseElements() == 0 )
3436 aMesh->RemoveNode( N1[4] );
3437 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3438 aMesh->RemoveNode( N1[6] );
3439 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3440 aMesh->RemoveNode( N2[6] );
3444 // Next element to fuse: the rejected one
3446 startElem = Ok12 ? tr3 : tr2;
3448 } // if ( startElem )
3449 } // while ( startElem || !startLinks.empty() )
3450 } // while ( ! mapEl_setLi.empty() )
3456 /*#define DUMPSO(txt) \
3457 // cout << txt << endl;
3458 //=============================================================================
3462 //=============================================================================
3463 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3467 int tmp = idNodes[ i1 ];
3468 idNodes[ i1 ] = idNodes[ i2 ];
3469 idNodes[ i2 ] = tmp;
3470 gp_Pnt Ptmp = P[ i1 ];
3473 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3476 //=======================================================================
3477 //function : SortQuadNodes
3478 //purpose : Set 4 nodes of a quadrangle face in a good order.
3479 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3481 //=======================================================================
3483 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3488 for ( i = 0; i < 4; i++ ) {
3489 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3491 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3494 gp_Vec V1(P[0], P[1]);
3495 gp_Vec V2(P[0], P[2]);
3496 gp_Vec V3(P[0], P[3]);
3498 gp_Vec Cross1 = V1 ^ V2;
3499 gp_Vec Cross2 = V2 ^ V3;
3502 if (Cross1.Dot(Cross2) < 0)
3507 if (Cross1.Dot(Cross2) < 0)
3511 swap ( i, i + 1, idNodes, P );
3513 // for ( int ii = 0; ii < 4; ii++ ) {
3514 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3515 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3521 //=======================================================================
3522 //function : SortHexaNodes
3523 //purpose : Set 8 nodes of a hexahedron in a good order.
3524 // Return success status
3525 //=======================================================================
3527 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3532 DUMPSO( "INPUT: ========================================");
3533 for ( i = 0; i < 8; i++ ) {
3534 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3535 if ( !n ) return false;
3536 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3537 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3539 DUMPSO( "========================================");
3542 set<int> faceNodes; // ids of bottom face nodes, to be found
3543 set<int> checkedId1; // ids of tried 2-nd nodes
3544 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3545 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3546 int iMin, iLoop1 = 0;
3548 // Loop to try the 2-nd nodes
3550 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3552 // Find not checked 2-nd node
3553 for ( i = 1; i < 8; i++ )
3554 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3555 int id1 = idNodes[i];
3556 swap ( 1, i, idNodes, P );
3557 checkedId1.insert ( id1 );
3561 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3562 // ie that all but meybe one (id3 which is on the same face) nodes
3563 // lay on the same side from the triangle plane.
3565 bool manyInPlane = false; // more than 4 nodes lay in plane
3567 while ( ++iLoop2 < 6 ) {
3569 // get 1-2-3 plane coeffs
3570 Standard_Real A, B, C, D;
3571 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3572 if ( N.SquareMagnitude() > gp::Resolution() )
3574 gp_Pln pln ( P[0], N );
3575 pln.Coefficients( A, B, C, D );
3577 // find the node (iMin) closest to pln
3578 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3580 for ( i = 3; i < 8; i++ ) {
3581 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3582 if ( fabs( dist[i] ) < minDist ) {
3583 minDist = fabs( dist[i] );
3586 if ( fabs( dist[i] ) <= tol )
3587 idInPln.insert( idNodes[i] );
3590 // there should not be more than 4 nodes in bottom plane
3591 if ( idInPln.size() > 1 )
3593 DUMPSO( "### idInPln.size() = " << idInPln.size());
3594 // idInPlane does not contain the first 3 nodes
3595 if ( manyInPlane || idInPln.size() == 5)
3596 return false; // all nodes in one plane
3599 // set the 1-st node to be not in plane
3600 for ( i = 3; i < 8; i++ ) {
3601 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3602 DUMPSO( "### Reset 0-th node");
3603 swap( 0, i, idNodes, P );
3608 // reset to re-check second nodes
3609 leastDist = DBL_MAX;
3613 break; // from iLoop2;
3616 // check that the other 4 nodes are on the same side
3617 bool sameSide = true;
3618 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3619 for ( i = 3; sameSide && i < 8; i++ ) {
3621 sameSide = ( isNeg == dist[i] <= 0.);
3624 // keep best solution
3625 if ( sameSide && minDist < leastDist ) {
3626 leastDist = minDist;
3628 faceNodes.insert( idNodes[ 1 ] );
3629 faceNodes.insert( idNodes[ 2 ] );
3630 faceNodes.insert( idNodes[ iMin ] );
3631 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3632 << " leastDist = " << leastDist);
3633 if ( leastDist <= DBL_MIN )
3638 // set next 3-d node to check
3639 int iNext = 2 + iLoop2;
3641 DUMPSO( "Try 2-nd");
3642 swap ( 2, iNext, idNodes, P );
3644 } // while ( iLoop2 < 6 )
3647 if ( faceNodes.empty() ) return false;
3649 // Put the faceNodes in proper places
3650 for ( i = 4; i < 8; i++ ) {
3651 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3652 // find a place to put
3654 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3656 DUMPSO( "Set faceNodes");
3657 swap ( iTo, i, idNodes, P );
3662 // Set nodes of the found bottom face in good order
3663 DUMPSO( " Found bottom face: ");
3664 i = SortQuadNodes( theMesh, idNodes );
3666 gp_Pnt Ptmp = P[ i ];
3671 // for ( int ii = 0; ii < 4; ii++ ) {
3672 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3673 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3676 // Gravity center of the top and bottom faces
3677 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3678 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3680 // Get direction from the bottom to the top face
3681 gp_Vec upDir ( aGCb, aGCt );
3682 Standard_Real upDirSize = upDir.Magnitude();
3683 if ( upDirSize <= gp::Resolution() ) return false;
3686 // Assure that the bottom face normal points up
3687 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3688 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3689 if ( Nb.Dot( upDir ) < 0 ) {
3690 DUMPSO( "Reverse bottom face");
3691 swap( 1, 3, idNodes, P );
3694 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3695 Standard_Real minDist = DBL_MAX;
3696 for ( i = 4; i < 8; i++ ) {
3697 // projection of P[i] to the plane defined by P[0] and upDir
3698 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3699 Standard_Real sqDist = P[0].SquareDistance( Pp );
3700 if ( sqDist < minDist ) {
3705 DUMPSO( "Set 4-th");
3706 swap ( 4, iMin, idNodes, P );
3708 // Set nodes of the top face in good order
3709 DUMPSO( "Sort top face");
3710 i = SortQuadNodes( theMesh, &idNodes[4] );
3713 gp_Pnt Ptmp = P[ i ];
3718 // Assure that direction of the top face normal is from the bottom face
3719 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3720 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3721 if ( Nt.Dot( upDir ) < 0 ) {
3722 DUMPSO( "Reverse top face");
3723 swap( 5, 7, idNodes, P );
3726 // DUMPSO( "OUTPUT: ========================================");
3727 // for ( i = 0; i < 8; i++ ) {
3728 // float *p = ugrid->GetPoint(idNodes[i]);
3729 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3735 //================================================================================
3737 * \brief Return nodes linked to the given one
3738 * \param theNode - the node
3739 * \param linkedNodes - the found nodes
3740 * \param type - the type of elements to check
3742 * Medium nodes are ignored
3744 //================================================================================
3746 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3747 TIDSortedElemSet & linkedNodes,
3748 SMDSAbs_ElementType type )
3750 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3751 while ( elemIt->more() )
3753 const SMDS_MeshElement* elem = elemIt->next();
3754 if(elem->GetType() == SMDSAbs_0DElement)
3757 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3758 if ( elem->GetType() == SMDSAbs_Volume )
3760 SMDS_VolumeTool vol( elem );
3761 while ( nodeIt->more() ) {
3762 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3763 if ( theNode != n && vol.IsLinked( theNode, n ))
3764 linkedNodes.insert( n );
3769 for ( int i = 0; nodeIt->more(); ++i ) {
3770 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3771 if ( n == theNode ) {
3772 int iBefore = i - 1;
3774 if ( elem->IsQuadratic() ) {
3775 int nb = elem->NbNodes() / 2;
3776 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3777 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3779 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3780 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3787 //=======================================================================
3788 //function : laplacianSmooth
3789 //purpose : pulls theNode toward the center of surrounding nodes directly
3790 // connected to that node along an element edge
3791 //=======================================================================
3793 void laplacianSmooth(const SMDS_MeshNode* theNode,
3794 const Handle(Geom_Surface)& theSurface,
3795 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3797 // find surrounding nodes
3799 TIDSortedElemSet nodeSet;
3800 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3802 // compute new coodrs
3804 double coord[] = { 0., 0., 0. };
3805 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3806 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3807 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3808 if ( theSurface.IsNull() ) { // smooth in 3D
3809 coord[0] += node->X();
3810 coord[1] += node->Y();
3811 coord[2] += node->Z();
3813 else { // smooth in 2D
3814 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3815 gp_XY* uv = theUVMap[ node ];
3816 coord[0] += uv->X();
3817 coord[1] += uv->Y();
3820 int nbNodes = nodeSet.size();
3823 coord[0] /= nbNodes;
3824 coord[1] /= nbNodes;
3826 if ( !theSurface.IsNull() ) {
3827 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3828 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3829 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3835 coord[2] /= nbNodes;
3839 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3842 //=======================================================================
3843 //function : centroidalSmooth
3844 //purpose : pulls theNode toward the element-area-weighted centroid of the
3845 // surrounding elements
3846 //=======================================================================
3848 void centroidalSmooth(const SMDS_MeshNode* theNode,
3849 const Handle(Geom_Surface)& theSurface,
3850 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3852 gp_XYZ aNewXYZ(0.,0.,0.);
3853 SMESH::Controls::Area anAreaFunc;
3854 double totalArea = 0.;
3859 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3860 while ( elemIt->more() )
3862 const SMDS_MeshElement* elem = elemIt->next();
3865 gp_XYZ elemCenter(0.,0.,0.);
3866 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3867 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3868 int nn = elem->NbNodes();
3869 if(elem->IsQuadratic()) nn = nn/2;
3871 //while ( itN->more() ) {
3873 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3875 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3876 aNodePoints.push_back( aP );
3877 if ( !theSurface.IsNull() ) { // smooth in 2D
3878 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3879 gp_XY* uv = theUVMap[ aNode ];
3880 aP.SetCoord( uv->X(), uv->Y(), 0. );
3884 double elemArea = anAreaFunc.GetValue( aNodePoints );
3885 totalArea += elemArea;
3887 aNewXYZ += elemCenter * elemArea;
3889 aNewXYZ /= totalArea;
3890 if ( !theSurface.IsNull() ) {
3891 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3892 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3897 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3900 //=======================================================================
3901 //function : getClosestUV
3902 //purpose : return UV of closest projection
3903 //=======================================================================
3905 static bool getClosestUV (Extrema_GenExtPS& projector,
3906 const gp_Pnt& point,
3909 projector.Perform( point );
3910 if ( projector.IsDone() ) {
3911 double u, v, minVal = DBL_MAX;
3912 for ( int i = projector.NbExt(); i > 0; i-- )
3913 if ( projector.SquareDistance( i ) < minVal ) {
3914 minVal = projector.SquareDistance( i );
3915 projector.Point( i ).Parameter( u, v );
3917 result.SetCoord( u, v );
3923 //=======================================================================
3925 //purpose : Smooth theElements during theNbIterations or until a worst
3926 // element has aspect ratio <= theTgtAspectRatio.
3927 // Aspect Ratio varies in range [1.0, inf].
3928 // If theElements is empty, the whole mesh is smoothed.
3929 // theFixedNodes contains additionally fixed nodes. Nodes built
3930 // on edges and boundary nodes are always fixed.
3931 //=======================================================================
3933 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3934 set<const SMDS_MeshNode*> & theFixedNodes,
3935 const SmoothMethod theSmoothMethod,
3936 const int theNbIterations,
3937 double theTgtAspectRatio,
3940 myLastCreatedElems.Clear();
3941 myLastCreatedNodes.Clear();
3943 if ( theTgtAspectRatio < 1.0 )
3944 theTgtAspectRatio = 1.0;
3946 const double disttol = 1.e-16;
3948 SMESH::Controls::AspectRatio aQualityFunc;
3950 SMESHDS_Mesh* aMesh = GetMeshDS();
3952 if ( theElems.empty() ) {
3953 // add all faces to theElems
3954 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3955 while ( fIt->more() ) {
3956 const SMDS_MeshElement* face = fIt->next();
3957 theElems.insert( theElems.end(), face );
3960 // get all face ids theElems are on
3961 set< int > faceIdSet;
3962 TIDSortedElemSet::iterator itElem;
3964 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3965 int fId = FindShape( *itElem );
3966 // check that corresponding submesh exists and a shape is face
3968 faceIdSet.find( fId ) == faceIdSet.end() &&
3969 aMesh->MeshElements( fId )) {
3970 TopoDS_Shape F = aMesh->IndexToShape( fId );
3971 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3972 faceIdSet.insert( fId );
3975 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3977 // ===============================================
3978 // smooth elements on each TopoDS_Face separately
3979 // ===============================================
3981 SMESH_MesherHelper helper( *GetMesh() );
3983 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3984 for ( ; fId != faceIdSet.rend(); ++fId )
3986 // get face surface and submesh
3987 Handle(Geom_Surface) surface;
3988 SMESHDS_SubMesh* faceSubMesh = 0;
3991 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3992 bool isUPeriodic = false, isVPeriodic = false;
3995 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3996 surface = BRep_Tool::Surface( face );
3997 faceSubMesh = aMesh->MeshElements( *fId );
3998 fToler2 = BRep_Tool::Tolerance( face );
3999 fToler2 *= fToler2 * 10.;
4000 isUPeriodic = surface->IsUPeriodic();
4003 isVPeriodic = surface->IsVPeriodic();
4006 surface->Bounds( u1, u2, v1, v2 );
4007 helper.SetSubShape( face );
4009 // ---------------------------------------------------------
4010 // for elements on a face, find movable and fixed nodes and
4011 // compute UV for them
4012 // ---------------------------------------------------------
4013 bool checkBoundaryNodes = false;
4014 bool isQuadratic = false;
4015 set<const SMDS_MeshNode*> setMovableNodes;
4016 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4017 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4018 list< const SMDS_MeshElement* > elemsOnFace;
4020 Extrema_GenExtPS projector;
4021 GeomAdaptor_Surface surfAdaptor;
4022 if ( !surface.IsNull() ) {
4023 surfAdaptor.Load( surface );
4024 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4026 int nbElemOnFace = 0;
4027 itElem = theElems.begin();
4028 // loop on not yet smoothed elements: look for elems on a face
4029 while ( itElem != theElems.end() )
4031 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4032 break; // all elements found
4034 const SMDS_MeshElement* elem = *itElem;
4035 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4036 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4040 elemsOnFace.push_back( elem );
4041 theElems.erase( itElem++ );
4045 isQuadratic = elem->IsQuadratic();
4047 // get movable nodes of elem
4048 const SMDS_MeshNode* node;
4049 SMDS_TypeOfPosition posType;
4050 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4051 int nn = 0, nbn = elem->NbNodes();
4052 if(elem->IsQuadratic())
4054 while ( nn++ < nbn ) {
4055 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4056 const SMDS_PositionPtr& pos = node->GetPosition();
4057 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4058 if (posType != SMDS_TOP_EDGE &&
4059 posType != SMDS_TOP_VERTEX &&
4060 theFixedNodes.find( node ) == theFixedNodes.end())
4062 // check if all faces around the node are on faceSubMesh
4063 // because a node on edge may be bound to face
4064 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4066 if ( faceSubMesh ) {
4067 while ( eIt->more() && all ) {
4068 const SMDS_MeshElement* e = eIt->next();
4069 all = faceSubMesh->Contains( e );
4073 setMovableNodes.insert( node );
4075 checkBoundaryNodes = true;
4077 if ( posType == SMDS_TOP_3DSPACE )
4078 checkBoundaryNodes = true;
4081 if ( surface.IsNull() )
4084 // get nodes to check UV
4085 list< const SMDS_MeshNode* > uvCheckNodes;
4086 const SMDS_MeshNode* nodeInFace = 0;
4087 itN = elem->nodesIterator();
4088 nn = 0; nbn = elem->NbNodes();
4089 if(elem->IsQuadratic())
4091 while ( nn++ < nbn ) {
4092 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4093 if ( node->GetPosition()->GetDim() == 2 )
4095 if ( uvMap.find( node ) == uvMap.end() )
4096 uvCheckNodes.push_back( node );
4097 // add nodes of elems sharing node
4098 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4099 // while ( eIt->more() ) {
4100 // const SMDS_MeshElement* e = eIt->next();
4101 // if ( e != elem ) {
4102 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4103 // while ( nIt->more() ) {
4104 // const SMDS_MeshNode* n =
4105 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4106 // if ( uvMap.find( n ) == uvMap.end() )
4107 // uvCheckNodes.push_back( n );
4113 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4114 for ( ; n != uvCheckNodes.end(); ++n ) {
4117 const SMDS_PositionPtr& pos = node->GetPosition();
4118 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4122 bool toCheck = true;
4123 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4125 // compute not existing UV
4126 bool project = ( posType == SMDS_TOP_3DSPACE );
4127 // double dist1 = DBL_MAX, dist2 = 0;
4128 // if ( posType != SMDS_TOP_3DSPACE ) {
4129 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4130 // project = dist1 > fToler2;
4132 if ( project ) { // compute new UV
4134 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4135 if ( !getClosestUV( projector, pNode, newUV )) {
4136 MESSAGE("Node Projection Failed " << node);
4140 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4142 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4144 // if ( posType != SMDS_TOP_3DSPACE )
4145 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4146 // if ( dist2 < dist1 )
4150 // store UV in the map
4151 listUV.push_back( uv );
4152 uvMap.insert( make_pair( node, &listUV.back() ));
4154 } // loop on not yet smoothed elements
4156 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4157 checkBoundaryNodes = true;
4159 // fix nodes on mesh boundary
4161 if ( checkBoundaryNodes ) {
4162 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4163 map< SMESH_TLink, int >::iterator link_nb;
4164 // put all elements links to linkNbMap
4165 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4166 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4167 const SMDS_MeshElement* elem = (*elemIt);
4168 int nbn = elem->NbCornerNodes();
4169 // loop on elem links: insert them in linkNbMap
4170 for ( int iN = 0; iN < nbn; ++iN ) {
4171 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4172 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4173 SMESH_TLink link( n1, n2 );
4174 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4178 // remove nodes that are in links encountered only once from setMovableNodes
4179 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4180 if ( link_nb->second == 1 ) {
4181 setMovableNodes.erase( link_nb->first.node1() );
4182 setMovableNodes.erase( link_nb->first.node2() );
4187 // -----------------------------------------------------
4188 // for nodes on seam edge, compute one more UV ( uvMap2 );
4189 // find movable nodes linked to nodes on seam and which
4190 // are to be smoothed using the second UV ( uvMap2 )
4191 // -----------------------------------------------------
4193 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4194 if ( !surface.IsNull() ) {
4195 TopExp_Explorer eExp( face, TopAbs_EDGE );
4196 for ( ; eExp.More(); eExp.Next() ) {
4197 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4198 if ( !BRep_Tool::IsClosed( edge, face ))
4200 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4201 if ( !sm ) continue;
4202 // find out which parameter varies for a node on seam
4205 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206 if ( pcurve.IsNull() ) continue;
4207 uv1 = pcurve->Value( f );
4209 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4210 if ( pcurve.IsNull() ) continue;
4211 uv2 = pcurve->Value( f );
4212 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4214 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4215 std::swap( uv1, uv2 );
4216 // get nodes on seam and its vertices
4217 list< const SMDS_MeshNode* > seamNodes;
4218 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4219 while ( nSeamIt->more() ) {
4220 const SMDS_MeshNode* node = nSeamIt->next();
4221 if ( !isQuadratic || !IsMedium( node ))
4222 seamNodes.push_back( node );
4224 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4225 for ( ; vExp.More(); vExp.Next() ) {
4226 sm = aMesh->MeshElements( vExp.Current() );
4228 nSeamIt = sm->GetNodes();
4229 while ( nSeamIt->more() )
4230 seamNodes.push_back( nSeamIt->next() );
4233 // loop on nodes on seam
4234 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4235 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4236 const SMDS_MeshNode* nSeam = *noSeIt;
4237 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4238 if ( n_uv == uvMap.end() )
4241 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4242 // set the second UV
4243 listUV.push_back( *n_uv->second );
4244 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4245 if ( uvMap2.empty() )
4246 uvMap2 = uvMap; // copy the uvMap contents
4247 uvMap2[ nSeam ] = &listUV.back();
4249 // collect movable nodes linked to ones on seam in nodesNearSeam
4250 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4251 while ( eIt->more() ) {
4252 const SMDS_MeshElement* e = eIt->next();
4253 int nbUseMap1 = 0, nbUseMap2 = 0;
4254 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4255 int nn = 0, nbn = e->NbNodes();
4256 if(e->IsQuadratic()) nbn = nbn/2;
4257 while ( nn++ < nbn )
4259 const SMDS_MeshNode* n =
4260 static_cast<const SMDS_MeshNode*>( nIt->next() );
4262 setMovableNodes.find( n ) == setMovableNodes.end() )
4264 // add only nodes being closer to uv2 than to uv1
4265 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4266 // 0.5 * ( n->Y() + nSeam->Y() ),
4267 // 0.5 * ( n->Z() + nSeam->Z() ));
4269 // getClosestUV( projector, pMid, uv );
4270 double x = uvMap[ n ]->Coord( iPar );
4271 if ( Abs( uv1.Coord( iPar ) - x ) >
4272 Abs( uv2.Coord( iPar ) - x )) {
4273 nodesNearSeam.insert( n );
4279 // for centroidalSmooth all element nodes must
4280 // be on one side of a seam
4281 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4282 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4284 while ( nn++ < nbn ) {
4285 const SMDS_MeshNode* n =
4286 static_cast<const SMDS_MeshNode*>( nIt->next() );
4287 setMovableNodes.erase( n );
4291 } // loop on nodes on seam
4292 } // loop on edge of a face
4293 } // if ( !face.IsNull() )
4295 if ( setMovableNodes.empty() ) {
4296 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4297 continue; // goto next face
4305 double maxRatio = -1., maxDisplacement = -1.;
4306 set<const SMDS_MeshNode*>::iterator nodeToMove;
4307 for ( it = 0; it < theNbIterations; it++ ) {
4308 maxDisplacement = 0.;
4309 nodeToMove = setMovableNodes.begin();
4310 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4311 const SMDS_MeshNode* node = (*nodeToMove);
4312 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4315 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4316 if ( theSmoothMethod == LAPLACIAN )
4317 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4319 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4321 // node displacement
4322 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4323 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4324 if ( aDispl > maxDisplacement )
4325 maxDisplacement = aDispl;
4327 // no node movement => exit
4328 //if ( maxDisplacement < 1.e-16 ) {
4329 if ( maxDisplacement < disttol ) {
4330 MESSAGE("-- no node movement --");
4334 // check elements quality
4336 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4337 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4338 const SMDS_MeshElement* elem = (*elemIt);
4339 if ( !elem || elem->GetType() != SMDSAbs_Face )
4341 SMESH::Controls::TSequenceOfXYZ aPoints;
4342 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4343 double aValue = aQualityFunc.GetValue( aPoints );
4344 if ( aValue > maxRatio )
4348 if ( maxRatio <= theTgtAspectRatio ) {
4349 MESSAGE("-- quality achived --");
4352 if (it+1 == theNbIterations) {
4353 MESSAGE("-- Iteration limit exceeded --");
4355 } // smoothing iterations
4357 MESSAGE(" Face id: " << *fId <<
4358 " Nb iterstions: " << it <<
4359 " Displacement: " << maxDisplacement <<
4360 " Aspect Ratio " << maxRatio);
4362 // ---------------------------------------
4363 // new nodes positions are computed,
4364 // record movement in DS and set new UV
4365 // ---------------------------------------
4366 nodeToMove = setMovableNodes.begin();
4367 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4368 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4369 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4370 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4371 if ( node_uv != uvMap.end() ) {
4372 gp_XY* uv = node_uv->second;
4374 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4378 // move medium nodes of quadratic elements
4381 vector<const SMDS_MeshNode*> nodes;
4383 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4384 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4386 const SMDS_MeshElement* QF = *elemIt;
4387 if ( QF->IsQuadratic() )
4389 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4390 SMDS_MeshElement::iterator() );
4391 nodes.push_back( nodes[0] );
4393 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4395 if ( !surface.IsNull() )
4397 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4398 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4399 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4400 xyz = surface->Value( uv.X(), uv.Y() );
4403 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4405 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4406 // we have to move a medium node
4407 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4413 } // loop on face ids
4419 //=======================================================================
4420 //function : isReverse
4421 //purpose : Return true if normal of prevNodes is not co-directied with
4422 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4423 // iNotSame is where prevNodes and nextNodes are different.
4424 // If result is true then future volume orientation is OK
4425 //=======================================================================
4427 bool isReverse(const SMDS_MeshElement* face,
4428 const vector<const SMDS_MeshNode*>& prevNodes,
4429 const vector<const SMDS_MeshNode*>& nextNodes,
4433 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4434 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4435 gp_XYZ extrDir( pN - pP ), faceNorm;
4436 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4438 return faceNorm * extrDir < 0.0;
4441 //================================================================================
4443 * \brief Assure that theElemSets[0] holds elements, not nodes
4445 //================================================================================
4447 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4449 if ( !theElemSets[0].empty() &&
4450 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4452 std::swap( theElemSets[0], theElemSets[1] );
4454 else if ( !theElemSets[1].empty() &&
4455 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4457 std::swap( theElemSets[0], theElemSets[1] );
4462 //=======================================================================
4464 * \brief Create elements by sweeping an element
4465 * \param elem - element to sweep
4466 * \param newNodesItVec - nodes generated from each node of the element
4467 * \param newElems - generated elements
4468 * \param nbSteps - number of sweeping steps
4469 * \param srcElements - to append elem for each generated element
4471 //=======================================================================
4473 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4474 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4475 list<const SMDS_MeshElement*>& newElems,
4476 const size_t nbSteps,
4477 SMESH_SequenceOfElemPtr& srcElements)
4479 SMESHDS_Mesh* aMesh = GetMeshDS();
4481 const int nbNodes = elem->NbNodes();
4482 const int nbCorners = elem->NbCornerNodes();
4483 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4484 polyhedron creation !!! */
4485 // Loop on elem nodes:
4486 // find new nodes and detect same nodes indices
4487 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4488 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4489 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4490 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4492 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4493 vector<int> sames(nbNodes);
4494 vector<bool> isSingleNode(nbNodes);
4496 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4497 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4498 const SMDS_MeshNode* node = nnIt->first;
4499 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4500 if ( listNewNodes.empty() )
4503 itNN [ iNode ] = listNewNodes.begin();
4504 prevNod[ iNode ] = node;
4505 nextNod[ iNode ] = listNewNodes.front();
4507 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4508 corner node of linear */
4509 if ( prevNod[ iNode ] != nextNod [ iNode ])
4510 nbDouble += !isSingleNode[iNode];
4512 if( iNode < nbCorners ) { // check corners only
4513 if ( prevNod[ iNode ] == nextNod [ iNode ])
4514 sames[nbSame++] = iNode;
4516 iNotSameNode = iNode;
4520 if ( nbSame == nbNodes || nbSame > 2) {
4521 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4525 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4527 // fix nodes order to have bottom normal external
4528 if ( baseType == SMDSEntity_Polygon )
4530 std::reverse( itNN.begin(), itNN.end() );
4531 std::reverse( prevNod.begin(), prevNod.end() );
4532 std::reverse( midlNod.begin(), midlNod.end() );
4533 std::reverse( nextNod.begin(), nextNod.end() );
4534 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4538 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4539 SMDS_MeshCell::applyInterlace( ind, itNN );
4540 SMDS_MeshCell::applyInterlace( ind, prevNod );
4541 SMDS_MeshCell::applyInterlace( ind, nextNod );
4542 SMDS_MeshCell::applyInterlace( ind, midlNod );
4543 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4546 sames[nbSame] = iNotSameNode;
4547 for ( int j = 0; j <= nbSame; ++j )
4548 for ( size_t i = 0; i < ind.size(); ++i )
4549 if ( ind[i] == sames[j] )
4554 iNotSameNode = sames[nbSame];
4558 else if ( elem->GetType() == SMDSAbs_Edge )
4560 // orient a new face same as adjacent one
4562 const SMDS_MeshElement* e;
4563 TIDSortedElemSet dummy;
4564 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4565 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4566 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4568 // there is an adjacent face, check order of nodes in it
4569 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4572 std::swap( itNN[0], itNN[1] );
4573 std::swap( prevNod[0], prevNod[1] );
4574 std::swap( nextNod[0], nextNod[1] );
4575 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4577 sames[0] = 1 - sames[0];
4578 iNotSameNode = 1 - iNotSameNode;
4583 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4585 iSameNode = sames[ nbSame-1 ];
4586 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4587 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4588 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4591 if ( baseType == SMDSEntity_Polygon )
4593 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4594 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4596 else if ( baseType == SMDSEntity_Quad_Polygon )
4598 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4599 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4602 // make new elements
4603 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4606 for ( iNode = 0; iNode < nbNodes; iNode++ )
4608 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4609 nextNod[ iNode ] = *itNN[ iNode ]++;
4612 SMDS_MeshElement* aNewElem = 0;
4613 /*if(!elem->IsPoly())*/ {
4614 switch ( baseType ) {
4616 case SMDSEntity_Node: { // sweep NODE
4617 if ( nbSame == 0 ) {
4618 if ( isSingleNode[0] )
4619 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4621 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4627 case SMDSEntity_Edge: { // sweep EDGE
4628 if ( nbDouble == 0 )
4630 if ( nbSame == 0 ) // ---> quadrangle
4631 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4632 nextNod[ 1 ], nextNod[ 0 ] );
4633 else // ---> triangle
4634 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4635 nextNod[ iNotSameNode ] );
4637 else // ---> polygon
4639 vector<const SMDS_MeshNode*> poly_nodes;
4640 poly_nodes.push_back( prevNod[0] );
4641 poly_nodes.push_back( prevNod[1] );
4642 if ( prevNod[1] != nextNod[1] )
4644 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4645 poly_nodes.push_back( nextNod[1] );
4647 if ( prevNod[0] != nextNod[0] )
4649 poly_nodes.push_back( nextNod[0] );
4650 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4652 switch ( poly_nodes.size() ) {
4654 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4657 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4658 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4661 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4666 case SMDSEntity_Triangle: // TRIANGLE --->
4668 if ( nbDouble > 0 ) break;
4669 if ( nbSame == 0 ) // ---> pentahedron
4670 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4671 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4673 else if ( nbSame == 1 ) // ---> pyramid
4674 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4675 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4676 nextNod[ iSameNode ]);
4678 else // 2 same nodes: ---> tetrahedron
4679 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4680 nextNod[ iNotSameNode ]);
4683 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4687 if ( nbDouble+nbSame == 2 )
4689 if(nbSame==0) { // ---> quadratic quadrangle
4690 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4691 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4693 else { //(nbSame==1) // ---> quadratic triangle
4695 return; // medium node on axis
4697 else if(sames[0]==0)
4698 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4699 prevNod[2], midlNod[1], nextNod[2] );
4701 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4702 prevNod[2], nextNod[2], midlNod[0]);
4705 else if ( nbDouble == 3 )
4707 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4708 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4709 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4716 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4717 if ( nbDouble > 0 ) break;
4719 if ( nbSame == 0 ) // ---> hexahedron
4720 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4721 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4723 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4724 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4725 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4726 nextNod[ iSameNode ]);
4727 newElems.push_back( aNewElem );
4728 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4729 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4730 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4732 else if ( nbSame == 2 ) { // ---> pentahedron
4733 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4734 // iBeforeSame is same too
4735 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4736 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4737 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4739 // iAfterSame is same too
4740 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4741 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4742 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4746 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4747 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4748 if ( nbDouble+nbSame != 3 ) break;
4750 // ---> pentahedron with 15 nodes
4751 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4752 nextNod[0], nextNod[1], nextNod[2],
4753 prevNod[3], prevNod[4], prevNod[5],
4754 nextNod[3], nextNod[4], nextNod[5],
4755 midlNod[0], midlNod[1], midlNod[2]);
4757 else if(nbSame==1) {
4758 // ---> 2d order pyramid of 13 nodes
4759 int apex = iSameNode;
4760 int i0 = ( apex + 1 ) % nbCorners;
4761 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4765 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4766 nextNod[i0], nextNod[i1], prevNod[apex],
4767 prevNod[i01], midlNod[i0],
4768 nextNod[i01], midlNod[i1],
4769 prevNod[i1a], prevNod[i0a],
4770 nextNod[i0a], nextNod[i1a]);
4772 else if(nbSame==2) {
4773 // ---> 2d order tetrahedron of 10 nodes
4774 int n1 = iNotSameNode;
4775 int n2 = ( n1 + 1 ) % nbCorners;
4776 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4780 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4781 prevNod[n12], prevNod[n23], prevNod[n31],
4782 midlNod[n1], nextNod[n12], nextNod[n31]);
4786 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4788 if ( nbDouble != 4 ) break;
4789 // ---> hexahedron with 20 nodes
4790 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4791 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4792 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4793 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4794 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4796 else if(nbSame==1) {
4797 // ---> pyramid + pentahedron - can not be created since it is needed
4798 // additional middle node at the center of face
4799 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4802 else if( nbSame == 2 ) {
4803 if ( nbDouble != 2 ) break;
4804 // ---> 2d order Pentahedron with 15 nodes
4806 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4807 // iBeforeSame is same too
4814 // iAfterSame is same too
4824 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4825 prevNod[n4], prevNod[n5], nextNod[n5],
4826 prevNod[n12], midlNod[n2], nextNod[n12],
4827 prevNod[n45], midlNod[n5], nextNod[n45],
4828 prevNod[n14], prevNod[n25], nextNod[n25]);
4832 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4834 if( nbSame == 0 && nbDouble == 9 ) {
4835 // ---> tri-quadratic hexahedron with 27 nodes
4836 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4837 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4838 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4839 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4840 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4841 prevNod[8], // bottom center
4842 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4843 nextNod[8], // top center
4844 midlNod[8]);// elem center
4852 case SMDSEntity_Polygon: { // sweep POLYGON
4854 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4855 // ---> hexagonal prism
4856 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4857 prevNod[3], prevNod[4], prevNod[5],
4858 nextNod[0], nextNod[1], nextNod[2],
4859 nextNod[3], nextNod[4], nextNod[5]);
4863 case SMDSEntity_Ball:
4868 } // switch ( baseType )
4871 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4873 if ( baseType != SMDSEntity_Polygon )
4875 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4876 SMDS_MeshCell::applyInterlace( ind, prevNod );
4877 SMDS_MeshCell::applyInterlace( ind, nextNod );
4878 SMDS_MeshCell::applyInterlace( ind, midlNod );
4879 SMDS_MeshCell::applyInterlace( ind, itNN );
4880 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4881 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4883 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4884 vector<int> quantities (nbNodes + 2);
4885 polyedre_nodes.clear();
4889 for (int inode = 0; inode < nbNodes; inode++)
4890 polyedre_nodes.push_back( prevNod[inode] );
4891 quantities.push_back( nbNodes );
4894 polyedre_nodes.push_back( nextNod[0] );
4895 for (int inode = nbNodes; inode-1; --inode )
4896 polyedre_nodes.push_back( nextNod[inode-1] );
4897 quantities.push_back( nbNodes );
4905 const int iQuad = elem->IsQuadratic();
4906 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4908 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4909 int inextface = (iface+1+iQuad) % nbNodes;
4910 int imid = (iface+1) % nbNodes;
4911 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4912 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4913 polyedre_nodes.push_back( prevNod[iface] ); // 1
4914 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4916 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4917 polyedre_nodes.push_back( nextNod[iface] ); // 2
4919 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4920 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4922 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4923 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4925 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4926 if ( nbFaceNodes > 2 )
4927 quantities.push_back( nbFaceNodes );
4928 else // degenerated face
4929 polyedre_nodes.resize( prevNbNodes );
4931 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4933 } // try to create a polyherdal prism
4936 newElems.push_back( aNewElem );
4937 myLastCreatedElems.Append(aNewElem);
4938 srcElements.Append( elem );
4941 // set new prev nodes
4942 for ( iNode = 0; iNode < nbNodes; iNode++ )
4943 prevNod[ iNode ] = nextNod[ iNode ];
4948 //=======================================================================
4950 * \brief Create 1D and 2D elements around swept elements
4951 * \param mapNewNodes - source nodes and ones generated from them
4952 * \param newElemsMap - source elements and ones generated from them
4953 * \param elemNewNodesMap - nodes generated from each node of each element
4954 * \param elemSet - all swept elements
4955 * \param nbSteps - number of sweeping steps
4956 * \param srcElements - to append elem for each generated element
4958 //=======================================================================
4960 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4961 TTElemOfElemListMap & newElemsMap,
4962 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4963 TIDSortedElemSet& elemSet,
4965 SMESH_SequenceOfElemPtr& srcElements)
4967 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4968 SMESHDS_Mesh* aMesh = GetMeshDS();
4970 // Find nodes belonging to only one initial element - sweep them into edges.
4972 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4973 for ( ; nList != mapNewNodes.end(); nList++ )
4975 const SMDS_MeshNode* node =
4976 static_cast<const SMDS_MeshNode*>( nList->first );
4977 if ( newElemsMap.count( node ))
4978 continue; // node was extruded into edge
4979 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4980 int nbInitElems = 0;
4981 const SMDS_MeshElement* el = 0;
4982 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4983 while ( eIt->more() && nbInitElems < 2 ) {
4984 const SMDS_MeshElement* e = eIt->next();
4985 SMDSAbs_ElementType type = e->GetType();
4986 if ( type == SMDSAbs_Volume ||
4990 if ( type > highType ) {
4997 if ( nbInitElems == 1 ) {
4998 bool NotCreateEdge = el && el->IsMediumNode(node);
4999 if(!NotCreateEdge) {
5000 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5001 list<const SMDS_MeshElement*> newEdges;
5002 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5007 // Make a ceiling for each element ie an equal element of last new nodes.
5008 // Find free links of faces - make edges and sweep them into faces.
5010 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5012 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5013 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5014 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5016 const SMDS_MeshElement* elem = itElem->first;
5017 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5019 if(itElem->second.size()==0) continue;
5021 const bool isQuadratic = elem->IsQuadratic();
5023 if ( elem->GetType() == SMDSAbs_Edge ) {
5024 // create a ceiling edge
5025 if ( !isQuadratic ) {
5026 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5027 vecNewNodes[ 1 ]->second.back())) {
5028 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5029 vecNewNodes[ 1 ]->second.back()));
5030 srcElements.Append( elem );
5034 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5035 vecNewNodes[ 1 ]->second.back(),
5036 vecNewNodes[ 2 ]->second.back())) {
5037 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5038 vecNewNodes[ 1 ]->second.back(),
5039 vecNewNodes[ 2 ]->second.back()));
5040 srcElements.Append( elem );
5044 if ( elem->GetType() != SMDSAbs_Face )
5047 bool hasFreeLinks = false;
5049 TIDSortedElemSet avoidSet;
5050 avoidSet.insert( elem );
5052 set<const SMDS_MeshNode*> aFaceLastNodes;
5053 int iNode, nbNodes = vecNewNodes.size();
5054 if ( !isQuadratic ) {
5055 // loop on the face nodes
5056 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5057 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5058 // look for free links of the face
5059 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5060 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5061 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5062 // check if a link n1-n2 is free
5063 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5064 hasFreeLinks = true;
5065 // make a new edge and a ceiling for a new edge
5066 const SMDS_MeshElement* edge;
5067 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5068 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5069 srcElements.Append( myLastCreatedElems.Last() );
5071 n1 = vecNewNodes[ iNode ]->second.back();
5072 n2 = vecNewNodes[ iNext ]->second.back();
5073 if ( !aMesh->FindEdge( n1, n2 )) {
5074 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5075 srcElements.Append( edge );
5080 else { // elem is quadratic face
5081 int nbn = nbNodes/2;
5082 for ( iNode = 0; iNode < nbn; iNode++ ) {
5083 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5084 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5085 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5086 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5087 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5088 // check if a link is free
5089 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5090 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5091 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5092 hasFreeLinks = true;
5093 // make an edge and a ceiling for a new edge
5095 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5096 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5097 srcElements.Append( elem );
5099 n1 = vecNewNodes[ iNode ]->second.back();
5100 n2 = vecNewNodes[ iNext ]->second.back();
5101 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5102 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5103 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5104 srcElements.Append( elem );
5108 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5109 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5113 // sweep free links into faces
5115 if ( hasFreeLinks ) {
5116 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5117 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5119 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5120 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5121 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5122 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5123 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5125 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5126 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5127 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5129 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5130 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5131 std::advance( v, volNb );
5132 // find indices of free faces of a volume and their source edges
5133 list< int > freeInd;
5134 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5135 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5136 int iF, nbF = vTool.NbFaces();
5137 for ( iF = 0; iF < nbF; iF ++ ) {
5138 if (vTool.IsFreeFace( iF ) &&
5139 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5140 initNodeSet != faceNodeSet) // except an initial face
5142 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5144 if ( faceNodeSet == initNodeSetNoCenter )
5146 freeInd.push_back( iF );
5147 // find source edge of a free face iF
5148 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5149 vector<const SMDS_MeshNode*>::iterator lastCommom;
5150 commonNodes.resize( nbNodes, 0 );
5151 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5152 initNodeSet.begin(), initNodeSet.end(),
5153 commonNodes.begin());
5154 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5155 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5157 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5159 if ( !srcEdges.back() )
5161 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5162 << iF << " of volume #" << vTool.ID() << endl;
5167 if ( freeInd.empty() )
5170 // create wall faces for all steps;
5171 // if such a face has been already created by sweep of edge,
5172 // assure that its orientation is OK
5173 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5175 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5176 vTool.SetExternalNormal();
5177 const int nextShift = vTool.IsForward() ? +1 : -1;
5178 list< int >::iterator ind = freeInd.begin();
5179 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5180 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5182 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5183 int nbn = vTool.NbFaceNodes( *ind );
5184 const SMDS_MeshElement * f = 0;
5185 if ( nbn == 3 ) ///// triangle
5187 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5189 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5191 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5193 nodes[ 1 + nextShift ] };
5195 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5197 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5201 else if ( nbn == 4 ) ///// quadrangle
5203 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5205 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5207 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5208 nodes[ 2 ], nodes[ 2+nextShift ] };
5210 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5212 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5213 newOrder[ 2 ], newOrder[ 3 ]));
5216 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5218 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5220 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5222 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5224 nodes[2 + 2*nextShift],
5225 nodes[3 - 2*nextShift],
5227 nodes[3 + 2*nextShift]};
5229 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5231 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5239 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5241 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5242 nodes[1], nodes[3], nodes[5], nodes[7] );
5244 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5246 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5247 nodes[4 - 2*nextShift],
5249 nodes[4 + 2*nextShift],
5251 nodes[5 - 2*nextShift],
5253 nodes[5 + 2*nextShift] };
5255 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5257 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5258 newOrder[ 2 ], newOrder[ 3 ],
5259 newOrder[ 4 ], newOrder[ 5 ],
5260 newOrder[ 6 ], newOrder[ 7 ]));
5263 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5265 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5266 SMDSAbs_Face, /*noMedium=*/false);
5268 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5270 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5271 nodes[4 - 2*nextShift],
5273 nodes[4 + 2*nextShift],
5275 nodes[5 - 2*nextShift],
5277 nodes[5 + 2*nextShift],
5280 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5282 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5283 newOrder[ 2 ], newOrder[ 3 ],
5284 newOrder[ 4 ], newOrder[ 5 ],
5285 newOrder[ 6 ], newOrder[ 7 ],
5289 else //////// polygon
5291 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5292 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5294 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5296 if ( !vTool.IsForward() )
5297 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5299 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5301 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5305 while ( srcElements.Length() < myLastCreatedElems.Length() )
5306 srcElements.Append( *srcEdge );
5308 } // loop on free faces
5310 // go to the next volume
5312 while ( iVol++ < nbVolumesByStep ) v++;
5315 } // loop on volumes of one step
5316 } // sweep free links into faces
5318 // Make a ceiling face with a normal external to a volume
5320 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5321 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5322 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5324 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5325 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5326 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5330 lastVol.SetExternalNormal();
5331 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5332 const int nbn = lastVol.NbFaceNodes( iF );
5333 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5334 if ( !hasFreeLinks ||
5335 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5337 const vector<int>& interlace =
5338 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5339 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5341 AddElement( nodeVec, anyFace.Init( elem ));
5343 while ( srcElements.Length() < myLastCreatedElems.Length() )
5344 srcElements.Append( elem );
5347 } // loop on swept elements
5350 //=======================================================================
5351 //function : RotationSweep
5353 //=======================================================================
5355 SMESH_MeshEditor::PGroupIDs
5356 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5357 const gp_Ax1& theAxis,
5358 const double theAngle,
5359 const int theNbSteps,
5360 const double theTol,
5361 const bool theMakeGroups,
5362 const bool theMakeWalls)
5364 myLastCreatedElems.Clear();
5365 myLastCreatedNodes.Clear();
5367 // source elements for each generated one
5368 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5371 aTrsf.SetRotation( theAxis, theAngle );
5373 aTrsf2.SetRotation( theAxis, theAngle/2. );
5375 gp_Lin aLine( theAxis );
5376 double aSqTol = theTol * theTol;
5378 SMESHDS_Mesh* aMesh = GetMeshDS();
5380 TNodeOfNodeListMap mapNewNodes;
5381 TElemOfVecOfNnlmiMap mapElemNewNodes;
5382 TTElemOfElemListMap newElemsMap;
5384 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5385 myMesh->NbFaces(ORDER_QUADRATIC) +
5386 myMesh->NbVolumes(ORDER_QUADRATIC) );
5387 // loop on theElemSets
5388 setElemsFirst( theElemSets );
5389 TIDSortedElemSet::iterator itElem;
5390 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5392 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5393 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5394 const SMDS_MeshElement* elem = *itElem;
5395 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5397 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5398 newNodesItVec.reserve( elem->NbNodes() );
5400 // loop on elem nodes
5401 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402 while ( itN->more() )
5404 const SMDS_MeshNode* node = cast2Node( itN->next() );
5406 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5408 aXYZ.Coord( coord[0], coord[1], coord[2] );
5409 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5411 // check if a node has been already sweeped
5412 TNodeOfNodeListMapItr nIt =
5413 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5414 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5415 if ( listNewNodes.empty() )
5417 // check if we are to create medium nodes between corner ones
5418 bool needMediumNodes = false;
5419 if ( isQuadraticMesh )
5421 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5422 while (it->more() && !needMediumNodes )
5424 const SMDS_MeshElement* invElem = it->next();
5425 if ( invElem != elem && !theElems.count( invElem )) continue;
5426 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5427 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5428 needMediumNodes = true;
5433 const SMDS_MeshNode * newNode = node;
5434 for ( int i = 0; i < theNbSteps; i++ ) {
5436 if ( needMediumNodes ) // create a medium node
5438 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5439 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5440 myLastCreatedNodes.Append(newNode);
5441 srcNodes.Append( node );
5442 listNewNodes.push_back( newNode );
5443 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5446 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5448 // create a corner node
5449 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5450 myLastCreatedNodes.Append(newNode);
5451 srcNodes.Append( node );
5452 listNewNodes.push_back( newNode );
5455 listNewNodes.push_back( newNode );
5456 // if ( needMediumNodes )
5457 // listNewNodes.push_back( newNode );
5461 newNodesItVec.push_back( nIt );
5463 // make new elements
5464 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5469 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5471 PGroupIDs newGroupIDs;
5472 if ( theMakeGroups )
5473 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5478 //=======================================================================
5479 //function : ExtrusParam
5480 //purpose : standard construction
5481 //=======================================================================
5483 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5484 const int theNbSteps,
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 : auxilary 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 : auxilary 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)
7350 myLastCreatedElems.Clear();
7351 myLastCreatedNodes.Clear();
7353 SMESHDS_Mesh* aMesh = GetMeshDS();
7355 TNodeNodeMap nodeNodeMap; // node to replace - new node
7356 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7357 list< int > rmElemIds, rmNodeIds;
7359 // Fill nodeNodeMap and elems
7361 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7362 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7364 list<const SMDS_MeshNode*>& nodes = *grIt;
7365 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7366 const SMDS_MeshNode* nToKeep = *nIt;
7367 for ( ++nIt; nIt != nodes.end(); nIt++ )
7369 const SMDS_MeshNode* nToRemove = *nIt;
7370 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7371 if ( nToRemove != nToKeep )
7373 rmNodeIds.push_back( nToRemove->GetID() );
7374 AddToSameGroups( nToKeep, nToRemove, aMesh );
7375 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7376 // after MergeNodes() w/o creating node in place of merged ones.
7377 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7378 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7379 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7380 sm->SetIsAlwaysComputed( true );
7382 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7383 while ( invElemIt->more() ) {
7384 const SMDS_MeshElement* elem = invElemIt->next();
7389 // Change element nodes or remove an element
7391 set<const SMDS_MeshNode*> nodeSet;
7392 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7394 ElemFeatures elemType;
7396 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7397 for ( ; eIt != elems.end(); eIt++ )
7399 const SMDS_MeshElement* elem = *eIt;
7400 const int nbNodes = elem->NbNodes();
7401 const int aShapeId = FindShape( elem );
7402 SMDSAbs_EntityType entity = elem->GetEntityType();
7405 curNodes.resize( nbNodes );
7406 uniqueNodes.resize( nbNodes );
7407 iRepl.resize( nbNodes );
7408 int iUnique = 0, iCur = 0, nbRepl = 0;
7410 // get new seq of nodes
7411 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7412 while ( itN->more() )
7414 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7416 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7417 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7419 { ////////// BUG 0020185: begin
7420 bool stopRecur = false;
7421 set<const SMDS_MeshNode*> nodesRecur;
7422 nodesRecur.insert(n);
7423 while (!stopRecur) {
7424 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7425 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7426 n = (*nnIt_i).second;
7427 if (!nodesRecur.insert(n).second) {
7428 // error: recursive dependency
7435 } ////////// BUG 0020185: end
7437 curNodes[ iCur ] = n;
7438 bool isUnique = nodeSet.insert( n ).second;
7440 uniqueNodes[ iUnique++ ] = n;
7442 iRepl[ nbRepl++ ] = iCur;
7446 // Analyse element topology after replacement
7449 int nbUniqueNodes = nodeSet.size();
7450 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7452 if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7454 if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7456 elemType.Init( elem );
7457 const bool isQuad = elemType.myIsQuad;
7459 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7460 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7462 // a polygon can divide into several elements
7463 vector<const SMDS_MeshNode *> polygons_nodes;
7464 vector<int> quantities;
7465 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7468 vector<const SMDS_MeshNode *> face_nodes;
7470 for (int iface = 0; iface < nbNew; iface++)
7472 int nbNewNodes = quantities[iface];
7473 face_nodes.assign( polygons_nodes.begin() + inode,
7474 polygons_nodes.begin() + inode + nbNewNodes );
7475 inode += nbNewNodes;
7476 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7478 bool isValid = ( nbNewNodes % 2 == 0 );
7479 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7480 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7481 elemType.SetQuad( isValid );
7482 if ( isValid ) // put medium nodes after corners
7483 SMDS_MeshCell::applyInterlaceRev
7484 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7485 nbNewNodes ), face_nodes );
7487 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7489 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7491 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7494 rmElemIds.push_back(elem->GetID());
7498 else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7500 if ( nbUniqueNodes < 4 ) {
7501 rmElemIds.push_back(elem->GetID());
7504 // each face has to be analyzed in order to check volume validity
7505 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7508 int nbFaces = aPolyedre->NbFaces();
7510 vector<const SMDS_MeshNode *> poly_nodes;
7511 vector<int> quantities;
7512 vector<const SMDS_MeshNode *> faceNodes;
7514 for (int iface = 1; iface <= nbFaces; iface++)
7516 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7517 faceNodes.resize( nbFaceNodes );
7518 for (int inode = 1; inode <= nbFaceNodes; inode++)
7520 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7521 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7522 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7523 faceNode = (*nnIt).second;
7524 faceNodes[inode - 1] = faceNode;
7526 SimplifyFace(faceNodes, poly_nodes, quantities);
7529 if ( quantities.size() > 3 ) {
7530 // TODO: remove coincident faces
7533 if ( quantities.size() > 3 )
7535 const SMDS_MeshElement* newElem =
7536 aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7537 myLastCreatedElems.Append( newElem );
7538 if ( aShapeId && newElem )
7539 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7540 rmElemIds.push_back( elem->GetID() );
7544 rmElemIds.push_back( elem->GetID() );
7555 // TODO not all the possible cases are solved. Find something more generic?
7557 case SMDSEntity_Edge: //////// EDGE
7558 case SMDSEntity_Triangle: //// TRIANGLE
7559 case SMDSEntity_Quad_Triangle:
7560 case SMDSEntity_Tetra:
7561 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7566 case SMDSEntity_Quad_Edge:
7568 isOk = false; // to linear EDGE ???????
7571 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7573 if ( nbUniqueNodes < 3 )
7575 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7576 isOk = false; // opposite nodes stick
7579 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7588 if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7589 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7590 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7591 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7592 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7598 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7607 if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7608 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7609 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7610 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7611 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7617 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7620 if ( nbUniqueNodes == 4 ) {
7621 // ---------------------------------> tetrahedron
7622 if ( curNodes[3] == curNodes[4] &&
7623 curNodes[3] == curNodes[5] ) {
7627 else if ( curNodes[0] == curNodes[1] &&
7628 curNodes[0] == curNodes[2] ) {
7629 // bottom nodes stick: set a top before
7630 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7631 uniqueNodes[ 0 ] = curNodes [ 5 ];
7632 uniqueNodes[ 1 ] = curNodes [ 4 ];
7633 uniqueNodes[ 2 ] = curNodes [ 3 ];
7636 else if (( curNodes[0] == curNodes[3] ) +
7637 ( curNodes[1] == curNodes[4] ) +
7638 ( curNodes[2] == curNodes[5] ) == 2 ) {
7639 // a lateral face turns into a line
7643 else if ( nbUniqueNodes == 5 ) {
7644 // PENTAHEDRON --------------------> pyramid
7645 if ( curNodes[0] == curNodes[3] )
7647 uniqueNodes[ 0 ] = curNodes[ 1 ];
7648 uniqueNodes[ 1 ] = curNodes[ 4 ];
7649 uniqueNodes[ 2 ] = curNodes[ 5 ];
7650 uniqueNodes[ 3 ] = curNodes[ 2 ];
7651 uniqueNodes[ 4 ] = curNodes[ 0 ];
7654 if ( curNodes[1] == curNodes[4] )
7656 uniqueNodes[ 0 ] = curNodes[ 0 ];
7657 uniqueNodes[ 1 ] = curNodes[ 2 ];
7658 uniqueNodes[ 2 ] = curNodes[ 5 ];
7659 uniqueNodes[ 3 ] = curNodes[ 3 ];
7660 uniqueNodes[ 4 ] = curNodes[ 1 ];
7663 if ( curNodes[2] == curNodes[5] )
7665 uniqueNodes[ 0 ] = curNodes[ 0 ];
7666 uniqueNodes[ 1 ] = curNodes[ 3 ];
7667 uniqueNodes[ 2 ] = curNodes[ 4 ];
7668 uniqueNodes[ 3 ] = curNodes[ 1 ];
7669 uniqueNodes[ 4 ] = curNodes[ 2 ];
7675 case SMDSEntity_Hexa:
7677 //////////////////////////////////// HEXAHEDRON
7679 SMDS_VolumeTool hexa (elem);
7680 hexa.SetExternalNormal();
7681 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7682 //////////////////////// HEX ---> tetrahedron
7683 for ( int iFace = 0; iFace < 6; iFace++ ) {
7684 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7685 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7686 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7687 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7688 // one face turns into a point ...
7689 int pickInd = ind[ 0 ];
7690 int iOppFace = hexa.GetOppFaceIndex( iFace );
7691 ind = hexa.GetFaceNodesIndices( iOppFace );
7693 uniqueNodes.clear();
7694 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7695 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7698 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7700 if ( nbStick == 1 ) {
7701 // ... and the opposite one - into a triangle.
7703 uniqueNodes.push_back( curNodes[ pickInd ]);
7710 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7711 //////////////////////// HEX ---> prism
7712 int nbTria = 0, iTria[3];
7713 const int *ind; // indices of face nodes
7714 // look for triangular faces
7715 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7716 ind = hexa.GetFaceNodesIndices( iFace );
7717 TIDSortedNodeSet faceNodes;
7718 for ( iCur = 0; iCur < 4; iCur++ )
7719 faceNodes.insert( curNodes[ind[iCur]] );
7720 if ( faceNodes.size() == 3 )
7721 iTria[ nbTria++ ] = iFace;
7723 // check if triangles are opposite
7724 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7726 // set nodes of the bottom triangle
7727 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7729 for ( iCur = 0; iCur < 4; iCur++ )
7730 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7731 indB.push_back( ind[iCur] );
7732 if ( !hexa.IsForward() )
7733 std::swap( indB[0], indB[2] );
7734 for ( iCur = 0; iCur < 3; iCur++ )
7735 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7736 // set nodes of the top triangle
7737 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7738 for ( iCur = 0; iCur < 3; ++iCur )
7739 for ( int j = 0; j < 4; ++j )
7740 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7742 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7749 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7750 //////////////////// HEXAHEDRON ---> pyramid
7751 for ( int iFace = 0; iFace < 6; iFace++ ) {
7752 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7753 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7754 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7755 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7756 // one face turns into a point ...
7757 int iOppFace = hexa.GetOppFaceIndex( iFace );
7758 ind = hexa.GetFaceNodesIndices( iOppFace );
7759 uniqueNodes.clear();
7760 for ( iCur = 0; iCur < 4; iCur++ ) {
7761 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7764 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7766 if ( uniqueNodes.size() == 4 ) {
7767 // ... and the opposite one is a quadrangle
7769 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7770 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7778 if ( !isOk && nbUniqueNodes > 4 ) {
7779 ////////////////// HEXAHEDRON ---> polyhedron
7780 hexa.SetExternalNormal();
7781 vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7782 vector<int> quantities; quantities.reserve( 6 );
7783 for ( int iFace = 0; iFace < 6; iFace++ )
7785 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7786 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7787 curNodes[ind[1]] == curNodes[ind[3]] )
7790 break; // opposite nodes stick
7793 for ( iCur = 0; iCur < 4; iCur++ )
7795 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7796 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7798 if ( nodeSet.size() < 3 )
7799 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7801 quantities.push_back( nodeSet.size() );
7803 if ( quantities.size() >= 4 )
7805 const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7806 myLastCreatedElems.Append( newElem );
7807 if ( aShapeId && newElem )
7808 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7809 rmElemIds.push_back( elem->GetID() );
7813 } // case HEXAHEDRON
7817 } // switch ( nbNodes )
7819 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7821 if ( isOk ) // a non-poly elem remains valid after sticking nodes
7823 if ( nbNodes != nbUniqueNodes ||
7824 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7826 elemType.Init( elem ).SetID( elem->GetID() );
7828 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7829 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7831 uniqueNodes.resize(nbUniqueNodes);
7832 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7833 if ( sm && newElem )
7834 sm->AddElement( newElem );
7835 if ( elem != newElem )
7836 ReplaceElemInGroups( elem, newElem, aMesh );
7840 // Remove invalid regular element or invalid polygon
7841 rmElemIds.push_back( elem->GetID() );
7844 } // loop on elements
7846 // Remove bad elements, then equal nodes (order important)
7848 Remove( rmElemIds, false );
7849 Remove( rmNodeIds, true );
7855 // ========================================================
7856 // class : SortableElement
7857 // purpose : allow sorting elements basing on their nodes
7858 // ========================================================
7859 class SortableElement : public set <const SMDS_MeshElement*>
7863 SortableElement( const SMDS_MeshElement* theElem )
7866 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7867 while ( nodeIt->more() )
7868 this->insert( nodeIt->next() );
7871 const SMDS_MeshElement* Get() const
7875 mutable const SMDS_MeshElement* myElem;
7878 //=======================================================================
7879 //function : FindEqualElements
7880 //purpose : Return list of group of elements built on the same nodes.
7881 // Search among theElements or in the whole mesh if theElements is empty
7882 //=======================================================================
7884 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7885 TListOfListOfElementsID & theGroupsOfElementsID)
7887 myLastCreatedElems.Clear();
7888 myLastCreatedNodes.Clear();
7890 typedef map< SortableElement, int > TMapOfNodeSet;
7891 typedef list<int> TGroupOfElems;
7893 if ( theElements.empty() )
7894 { // get all elements in the mesh
7895 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7896 while ( eIt->more() )
7897 theElements.insert( theElements.end(), eIt->next() );
7900 vector< TGroupOfElems > arrayOfGroups;
7901 TGroupOfElems groupOfElems;
7902 TMapOfNodeSet mapOfNodeSet;
7904 TIDSortedElemSet::iterator elemIt = theElements.begin();
7905 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7907 const SMDS_MeshElement* curElem = *elemIt;
7908 SortableElement SE(curElem);
7910 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7911 if ( !pp.second ) { // one more coincident elem
7912 TMapOfNodeSet::iterator& itSE = pp.first;
7913 int ind = (*itSE).second;
7914 arrayOfGroups[ind].push_back( curElem->GetID() );
7917 arrayOfGroups.push_back( groupOfElems );
7918 arrayOfGroups.back().push_back( curElem->GetID() );
7923 groupOfElems.clear();
7924 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7925 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7927 if ( groupIt->size() > 1 ) {
7928 //groupOfElems.sort(); -- theElements is sorted already
7929 theGroupsOfElementsID.push_back( groupOfElems );
7930 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7935 //=======================================================================
7936 //function : MergeElements
7937 //purpose : In each given group, substitute all elements by the first one.
7938 //=======================================================================
7940 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7942 myLastCreatedElems.Clear();
7943 myLastCreatedNodes.Clear();
7945 typedef list<int> TListOfIDs;
7946 TListOfIDs rmElemIds; // IDs of elems to remove
7948 SMESHDS_Mesh* aMesh = GetMeshDS();
7950 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7951 while ( groupsIt != theGroupsOfElementsID.end() ) {
7952 TListOfIDs& aGroupOfElemID = *groupsIt;
7953 aGroupOfElemID.sort();
7954 int elemIDToKeep = aGroupOfElemID.front();
7955 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7956 aGroupOfElemID.pop_front();
7957 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7958 while ( idIt != aGroupOfElemID.end() ) {
7959 int elemIDToRemove = *idIt;
7960 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7961 // add the kept element in groups of removed one (PAL15188)
7962 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7963 rmElemIds.push_back( elemIDToRemove );
7969 Remove( rmElemIds, false );
7972 //=======================================================================
7973 //function : MergeEqualElements
7974 //purpose : Remove all but one of elements built on the same nodes.
7975 //=======================================================================
7977 void SMESH_MeshEditor::MergeEqualElements()
7979 TIDSortedElemSet aMeshElements; /* empty input ==
7980 to merge equal elements in the whole mesh */
7981 TListOfListOfElementsID aGroupsOfElementsID;
7982 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7983 MergeElements(aGroupsOfElementsID);
7986 //=======================================================================
7987 //function : findAdjacentFace
7989 //=======================================================================
7991 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7992 const SMDS_MeshNode* n2,
7993 const SMDS_MeshElement* elem)
7995 TIDSortedElemSet elemSet, avoidSet;
7997 avoidSet.insert ( elem );
7998 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8001 //=======================================================================
8002 //function : findSegment
8003 //purpose : Return a mesh segment by two nodes one of which can be medium
8004 //=======================================================================
8006 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8007 const SMDS_MeshNode* n2)
8009 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8010 while ( it->more() )
8012 const SMDS_MeshElement* seg = it->next();
8013 if ( seg->GetNodeIndex( n2 ) >= 0 )
8019 //=======================================================================
8020 //function : FindFreeBorder
8022 //=======================================================================
8024 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8026 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8027 const SMDS_MeshNode* theSecondNode,
8028 const SMDS_MeshNode* theLastNode,
8029 list< const SMDS_MeshNode* > & theNodes,
8030 list< const SMDS_MeshElement* >& theFaces)
8032 if ( !theFirstNode || !theSecondNode )
8034 // find border face between theFirstNode and theSecondNode
8035 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8039 theFaces.push_back( curElem );
8040 theNodes.push_back( theFirstNode );
8041 theNodes.push_back( theSecondNode );
8043 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8044 TIDSortedElemSet foundElems;
8045 bool needTheLast = ( theLastNode != 0 );
8047 while ( nStart != theLastNode ) {
8048 if ( nStart == theFirstNode )
8049 return !needTheLast;
8051 // find all free border faces sharing form nStart
8053 list< const SMDS_MeshElement* > curElemList;
8054 list< const SMDS_MeshNode* > nStartList;
8055 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8056 while ( invElemIt->more() ) {
8057 const SMDS_MeshElement* e = invElemIt->next();
8058 if ( e == curElem || foundElems.insert( e ).second ) {
8060 int iNode = 0, nbNodes = e->NbNodes();
8061 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8063 if ( e->IsQuadratic() ) {
8064 const SMDS_VtkFace* F =
8065 dynamic_cast<const SMDS_VtkFace*>(e);
8066 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8067 // use special nodes iterator
8068 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8069 while( anIter->more() ) {
8070 nodes[ iNode++ ] = cast2Node(anIter->next());
8074 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8075 while ( nIt->more() )
8076 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8078 nodes[ iNode ] = nodes[ 0 ];
8080 for ( iNode = 0; iNode < nbNodes; iNode++ )
8081 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8082 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8083 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8085 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8086 curElemList.push_back( e );
8090 // analyse the found
8092 int nbNewBorders = curElemList.size();
8093 if ( nbNewBorders == 0 ) {
8094 // no free border furthermore
8095 return !needTheLast;
8097 else if ( nbNewBorders == 1 ) {
8098 // one more element found
8100 nStart = nStartList.front();
8101 curElem = curElemList.front();
8102 theFaces.push_back( curElem );
8103 theNodes.push_back( nStart );
8106 // several continuations found
8107 list< const SMDS_MeshElement* >::iterator curElemIt;
8108 list< const SMDS_MeshNode* >::iterator nStartIt;
8109 // check if one of them reached the last node
8110 if ( needTheLast ) {
8111 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8112 curElemIt!= curElemList.end();
8113 curElemIt++, nStartIt++ )
8114 if ( *nStartIt == theLastNode ) {
8115 theFaces.push_back( *curElemIt );
8116 theNodes.push_back( *nStartIt );
8120 // find the best free border by the continuations
8121 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8122 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8123 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8124 curElemIt!= curElemList.end();
8125 curElemIt++, nStartIt++ )
8127 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8128 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8129 // find one more free border
8130 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8134 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8135 // choice: clear a worse one
8136 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8137 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8138 contNodes[ iWorse ].clear();
8139 contFaces[ iWorse ].clear();
8142 if ( contNodes[0].empty() && contNodes[1].empty() )
8145 // append the best free border
8146 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8147 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8148 theNodes.pop_back(); // remove nIgnore
8149 theNodes.pop_back(); // remove nStart
8150 theFaces.pop_back(); // remove curElem
8151 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8152 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8153 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8154 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8157 } // several continuations found
8158 } // while ( nStart != theLastNode )
8163 //=======================================================================
8164 //function : CheckFreeBorderNodes
8165 //purpose : Return true if the tree nodes are on a free border
8166 //=======================================================================
8168 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8169 const SMDS_MeshNode* theNode2,
8170 const SMDS_MeshNode* theNode3)
8172 list< const SMDS_MeshNode* > nodes;
8173 list< const SMDS_MeshElement* > faces;
8174 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8177 //=======================================================================
8178 //function : SewFreeBorder
8180 //warning : for border-to-side sewing theSideSecondNode is considered as
8181 // the last side node and theSideThirdNode is not used
8182 //=======================================================================
8184 SMESH_MeshEditor::Sew_Error
8185 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8186 const SMDS_MeshNode* theBordSecondNode,
8187 const SMDS_MeshNode* theBordLastNode,
8188 const SMDS_MeshNode* theSideFirstNode,
8189 const SMDS_MeshNode* theSideSecondNode,
8190 const SMDS_MeshNode* theSideThirdNode,
8191 const bool theSideIsFreeBorder,
8192 const bool toCreatePolygons,
8193 const bool toCreatePolyedrs)
8195 myLastCreatedElems.Clear();
8196 myLastCreatedNodes.Clear();
8198 Sew_Error aResult = SEW_OK;
8200 // ====================================
8201 // find side nodes and elements
8202 // ====================================
8204 list< const SMDS_MeshNode* > nSide[ 2 ];
8205 list< const SMDS_MeshElement* > eSide[ 2 ];
8206 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8207 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8211 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8212 nSide[0], eSide[0])) {
8213 MESSAGE(" Free Border 1 not found " );
8214 aResult = SEW_BORDER1_NOT_FOUND;
8216 if (theSideIsFreeBorder) {
8219 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8220 nSide[1], eSide[1])) {
8221 MESSAGE(" Free Border 2 not found " );
8222 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8225 if ( aResult != SEW_OK )
8228 if (!theSideIsFreeBorder) {
8232 // -------------------------------------------------------------------------
8234 // 1. If nodes to merge are not coincident, move nodes of the free border
8235 // from the coord sys defined by the direction from the first to last
8236 // nodes of the border to the correspondent sys of the side 2
8237 // 2. On the side 2, find the links most co-directed with the correspondent
8238 // links of the free border
8239 // -------------------------------------------------------------------------
8241 // 1. Since sewing may break if there are volumes to split on the side 2,
8242 // we wont move nodes but just compute new coordinates for them
8243 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8244 TNodeXYZMap nBordXYZ;
8245 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8246 list< const SMDS_MeshNode* >::iterator nBordIt;
8248 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8249 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8250 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8251 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8252 double tol2 = 1.e-8;
8253 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8254 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8255 // Need node movement.
8257 // find X and Z axes to create trsf
8258 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8260 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8262 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8265 gp_Ax3 toBordAx( Pb1, Zb, X );
8266 gp_Ax3 fromSideAx( Ps1, Zs, X );
8267 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8269 gp_Trsf toBordSys, fromSide2Sys;
8270 toBordSys.SetTransformation( toBordAx );
8271 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8272 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8275 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8276 const SMDS_MeshNode* n = *nBordIt;
8277 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8278 toBordSys.Transforms( xyz );
8279 fromSide2Sys.Transforms( xyz );
8280 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8284 // just insert nodes XYZ in the nBordXYZ map
8285 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8286 const SMDS_MeshNode* n = *nBordIt;
8287 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8291 // 2. On the side 2, find the links most co-directed with the correspondent
8292 // links of the free border
8294 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8295 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8296 sideNodes.push_back( theSideFirstNode );
8298 bool hasVolumes = false;
8299 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8300 set<long> foundSideLinkIDs, checkedLinkIDs;
8301 SMDS_VolumeTool volume;
8302 //const SMDS_MeshNode* faceNodes[ 4 ];
8304 const SMDS_MeshNode* sideNode;
8305 const SMDS_MeshElement* sideElem = 0;
8306 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8307 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8308 nBordIt = bordNodes.begin();
8310 // border node position and border link direction to compare with
8311 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8312 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8313 // choose next side node by link direction or by closeness to
8314 // the current border node:
8315 bool searchByDir = ( *nBordIt != theBordLastNode );
8317 // find the next node on the Side 2
8319 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8321 checkedLinkIDs.clear();
8322 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8324 // loop on inverse elements of current node (prevSideNode) on the Side 2
8325 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8326 while ( invElemIt->more() )
8328 const SMDS_MeshElement* elem = invElemIt->next();
8329 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8330 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8331 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8332 bool isVolume = volume.Set( elem );
8333 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8334 if ( isVolume ) // --volume
8336 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8337 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8338 if(elem->IsQuadratic()) {
8339 const SMDS_VtkFace* F =
8340 dynamic_cast<const SMDS_VtkFace*>(elem);
8341 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8342 // use special nodes iterator
8343 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8344 while( anIter->more() ) {
8345 nodes[ iNode ] = cast2Node(anIter->next());
8346 if ( nodes[ iNode++ ] == prevSideNode )
8347 iPrevNode = iNode - 1;
8351 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8352 while ( nIt->more() ) {
8353 nodes[ iNode ] = cast2Node( nIt->next() );
8354 if ( nodes[ iNode++ ] == prevSideNode )
8355 iPrevNode = iNode - 1;
8358 // there are 2 links to check
8363 // loop on links, to be precise, on the second node of links
8364 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8365 const SMDS_MeshNode* n = nodes[ iNode ];
8367 if ( !volume.IsLinked( n, prevSideNode ))
8371 if ( iNode ) // a node before prevSideNode
8372 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8373 else // a node after prevSideNode
8374 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8376 // check if this link was already used
8377 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8378 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8379 if (!isJustChecked &&
8380 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8382 // test a link geometrically
8383 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8384 bool linkIsBetter = false;
8385 double dot = 0.0, dist = 0.0;
8386 if ( searchByDir ) { // choose most co-directed link
8387 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8388 linkIsBetter = ( dot > maxDot );
8390 else { // choose link with the node closest to bordPos
8391 dist = ( nextXYZ - bordPos ).SquareModulus();
8392 linkIsBetter = ( dist < minDist );
8394 if ( linkIsBetter ) {
8403 } // loop on inverse elements of prevSideNode
8406 MESSAGE(" Cant find path by links of the Side 2 ");
8407 return SEW_BAD_SIDE_NODES;
8409 sideNodes.push_back( sideNode );
8410 sideElems.push_back( sideElem );
8411 foundSideLinkIDs.insert ( linkID );
8412 prevSideNode = sideNode;
8414 if ( *nBordIt == theBordLastNode )
8415 searchByDir = false;
8417 // find the next border link to compare with
8418 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8419 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8420 // move to next border node if sideNode is before forward border node (bordPos)
8421 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8422 prevBordNode = *nBordIt;
8424 bordPos = nBordXYZ[ *nBordIt ];
8425 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8426 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8430 while ( sideNode != theSideSecondNode );
8432 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8433 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8434 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8436 } // end nodes search on the side 2
8438 // ============================
8439 // sew the border to the side 2
8440 // ============================
8442 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8443 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8445 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8446 if ( toMergeConformal && toCreatePolygons )
8448 // do not merge quadrangles if polygons are OK (IPAL0052824)
8449 eIt[0] = eSide[0].begin();
8450 eIt[1] = eSide[1].begin();
8451 bool allQuads[2] = { true, true };
8452 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8453 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8454 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8456 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8459 TListOfListOfNodes nodeGroupsToMerge;
8460 if (( toMergeConformal ) ||
8461 ( theSideIsFreeBorder && !theSideThirdNode )) {
8463 // all nodes are to be merged
8465 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8466 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8467 nIt[0]++, nIt[1]++ )
8469 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8470 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8471 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8476 // insert new nodes into the border and the side to get equal nb of segments
8478 // get normalized parameters of nodes on the borders
8479 vector< double > param[ 2 ];
8480 param[0].resize( maxNbNodes );
8481 param[1].resize( maxNbNodes );
8483 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8484 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8485 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8486 const SMDS_MeshNode* nPrev = *nIt;
8487 double bordLength = 0;
8488 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8489 const SMDS_MeshNode* nCur = *nIt;
8490 gp_XYZ segment (nCur->X() - nPrev->X(),
8491 nCur->Y() - nPrev->Y(),
8492 nCur->Z() - nPrev->Z());
8493 double segmentLen = segment.Modulus();
8494 bordLength += segmentLen;
8495 param[ iBord ][ iNode ] = bordLength;
8498 // normalize within [0,1]
8499 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8500 param[ iBord ][ iNode ] /= bordLength;
8504 // loop on border segments
8505 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8506 int i[ 2 ] = { 0, 0 };
8507 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8508 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8510 TElemOfNodeListMap insertMap;
8511 TElemOfNodeListMap::iterator insertMapIt;
8513 // key: elem to insert nodes into
8514 // value: 2 nodes to insert between + nodes to be inserted
8516 bool next[ 2 ] = { false, false };
8518 // find min adjacent segment length after sewing
8519 double nextParam = 10., prevParam = 0;
8520 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8521 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8522 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8523 if ( i[ iBord ] > 0 )
8524 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8526 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8527 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8528 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8530 // choose to insert or to merge nodes
8531 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8532 if ( Abs( du ) <= minSegLen * 0.2 ) {
8535 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8536 const SMDS_MeshNode* n0 = *nIt[0];
8537 const SMDS_MeshNode* n1 = *nIt[1];
8538 nodeGroupsToMerge.back().push_back( n1 );
8539 nodeGroupsToMerge.back().push_back( n0 );
8540 // position of node of the border changes due to merge
8541 param[ 0 ][ i[0] ] += du;
8542 // move n1 for the sake of elem shape evaluation during insertion.
8543 // n1 will be removed by MergeNodes() anyway
8544 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8545 next[0] = next[1] = true;
8550 int intoBord = ( du < 0 ) ? 0 : 1;
8551 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8552 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8553 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8554 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8555 if ( intoBord == 1 ) {
8556 // move node of the border to be on a link of elem of the side
8557 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8558 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8559 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8560 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8561 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8563 insertMapIt = insertMap.find( elem );
8564 bool notFound = ( insertMapIt == insertMap.end() );
8565 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8567 // insert into another link of the same element:
8568 // 1. perform insertion into the other link of the elem
8569 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8570 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8571 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8572 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8573 // 2. perform insertion into the link of adjacent faces
8574 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8575 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8577 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8578 InsertNodesIntoLink( seg, n12, n22, nodeList );
8580 if (toCreatePolyedrs) {
8581 // perform insertion into the links of adjacent volumes
8582 UpdateVolumes(n12, n22, nodeList);
8584 // 3. find an element appeared on n1 and n2 after the insertion
8585 insertMap.erase( elem );
8586 elem = findAdjacentFace( n1, n2, 0 );
8588 if ( notFound || otherLink ) {
8589 // add element and nodes of the side into the insertMap
8590 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8591 (*insertMapIt).second.push_back( n1 );
8592 (*insertMapIt).second.push_back( n2 );
8594 // add node to be inserted into elem
8595 (*insertMapIt).second.push_back( nIns );
8596 next[ 1 - intoBord ] = true;
8599 // go to the next segment
8600 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8601 if ( next[ iBord ] ) {
8602 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8604 nPrev[ iBord ] = *nIt[ iBord ];
8605 nIt[ iBord ]++; i[ iBord ]++;
8609 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8611 // perform insertion of nodes into elements
8613 for (insertMapIt = insertMap.begin();
8614 insertMapIt != insertMap.end();
8617 const SMDS_MeshElement* elem = (*insertMapIt).first;
8618 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8619 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8620 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8622 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8624 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8625 InsertNodesIntoLink( seg, n1, n2, nodeList );
8628 if ( !theSideIsFreeBorder ) {
8629 // look for and insert nodes into the faces adjacent to elem
8630 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8631 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8634 if (toCreatePolyedrs) {
8635 // perform insertion into the links of adjacent volumes
8636 UpdateVolumes(n1, n2, nodeList);
8639 } // end: insert new nodes
8641 MergeNodes ( nodeGroupsToMerge );
8644 // Remove coincident segments
8647 TIDSortedElemSet segments;
8648 SMESH_SequenceOfElemPtr newFaces;
8649 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8651 if ( !myLastCreatedElems(i) ) continue;
8652 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8653 segments.insert( segments.end(), myLastCreatedElems(i) );
8655 newFaces.Append( myLastCreatedElems(i) );
8657 // get segments adjacent to merged nodes
8658 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8659 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8661 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8662 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8663 while ( segIt->more() )
8664 segments.insert( segIt->next() );
8668 TListOfListOfElementsID equalGroups;
8669 if ( !segments.empty() )
8670 FindEqualElements( segments, equalGroups );
8671 if ( !equalGroups.empty() )
8673 // remove from segments those that will be removed
8674 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8675 for ( ; itGroups != equalGroups.end(); ++itGroups )
8677 list< int >& group = *itGroups;
8678 list< int >::iterator id = group.begin();
8679 for ( ++id; id != group.end(); ++id )
8680 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8681 segments.erase( seg );
8683 // remove equal segments
8684 MergeElements( equalGroups );
8686 // restore myLastCreatedElems
8687 myLastCreatedElems = newFaces;
8688 TIDSortedElemSet::iterator seg = segments.begin();
8689 for ( ; seg != segments.end(); ++seg )
8690 myLastCreatedElems.Append( *seg );
8696 //=======================================================================
8697 //function : InsertNodesIntoLink
8698 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8699 // and theBetweenNode2 and split theElement
8700 //=======================================================================
8702 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8703 const SMDS_MeshNode* theBetweenNode1,
8704 const SMDS_MeshNode* theBetweenNode2,
8705 list<const SMDS_MeshNode*>& theNodesToInsert,
8706 const bool toCreatePoly)
8708 if ( !theElement ) return;
8710 SMESHDS_Mesh *aMesh = GetMeshDS();
8711 vector<const SMDS_MeshElement*> newElems;
8713 if ( theElement->GetType() == SMDSAbs_Edge )
8715 theNodesToInsert.push_front( theBetweenNode1 );
8716 theNodesToInsert.push_back ( theBetweenNode2 );
8717 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8718 const SMDS_MeshNode* n1 = *n;
8719 for ( ++n; n != theNodesToInsert.end(); ++n )
8721 const SMDS_MeshNode* n2 = *n;
8722 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8723 AddToSameGroups( seg, theElement, aMesh );
8725 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8728 theNodesToInsert.pop_front();
8729 theNodesToInsert.pop_back();
8731 if ( theElement->IsQuadratic() ) // add a not split part
8733 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8734 theElement->end_nodes() );
8735 int iOther = 0, nbN = nodes.size();
8736 for ( ; iOther < nbN; ++iOther )
8737 if ( nodes[iOther] != theBetweenNode1 &&
8738 nodes[iOther] != theBetweenNode2 )
8742 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8743 AddToSameGroups( seg, theElement, aMesh );
8745 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8747 else if ( iOther == 2 )
8749 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8750 AddToSameGroups( seg, theElement, aMesh );
8752 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8755 // treat new elements
8756 for ( size_t i = 0; i < newElems.size(); ++i )
8759 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8760 myLastCreatedElems.Append( newElems[i] );
8762 ReplaceElemInGroups( theElement, newElems, aMesh );
8763 aMesh->RemoveElement( theElement );
8766 } // if ( theElement->GetType() == SMDSAbs_Edge )
8768 const SMDS_MeshElement* theFace = theElement;
8769 if ( theFace->GetType() != SMDSAbs_Face ) return;
8771 // find indices of 2 link nodes and of the rest nodes
8772 int iNode = 0, il1, il2, i3, i4;
8773 il1 = il2 = i3 = i4 = -1;
8774 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8776 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8777 while ( nodeIt->more() ) {
8778 const SMDS_MeshNode* n = nodeIt->next();
8779 if ( n == theBetweenNode1 )
8781 else if ( n == theBetweenNode2 )
8787 nodes[ iNode++ ] = n;
8789 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8792 // arrange link nodes to go one after another regarding the face orientation
8793 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8794 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8799 aNodesToInsert.reverse();
8801 // check that not link nodes of a quadrangles are in good order
8802 int nbFaceNodes = theFace->NbNodes();
8803 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8809 if (toCreatePoly || theFace->IsPoly()) {
8812 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8814 // add nodes of face up to first node of link
8817 if ( theFace->IsQuadratic() ) {
8818 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8819 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8820 // use special nodes iterator
8821 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8822 while( anIter->more() && !isFLN ) {
8823 const SMDS_MeshNode* n = cast2Node(anIter->next());
8824 poly_nodes[iNode++] = n;
8825 if (n == nodes[il1]) {
8829 // add nodes to insert
8830 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8831 for (; nIt != aNodesToInsert.end(); nIt++) {
8832 poly_nodes[iNode++] = *nIt;
8834 // add nodes of face starting from last node of link
8835 while ( anIter->more() ) {
8836 poly_nodes[iNode++] = cast2Node(anIter->next());
8840 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8841 while ( nodeIt->more() && !isFLN ) {
8842 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8843 poly_nodes[iNode++] = n;
8844 if (n == nodes[il1]) {
8848 // add nodes to insert
8849 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8850 for (; nIt != aNodesToInsert.end(); nIt++) {
8851 poly_nodes[iNode++] = *nIt;
8853 // add nodes of face starting from last node of link
8854 while ( nodeIt->more() ) {
8855 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8856 poly_nodes[iNode++] = n;
8861 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8864 else if ( !theFace->IsQuadratic() )
8866 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8867 int nbLinkNodes = 2 + aNodesToInsert.size();
8868 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8869 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8870 linkNodes[ 0 ] = nodes[ il1 ];
8871 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8872 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8873 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8874 linkNodes[ iNode++ ] = *nIt;
8876 // decide how to split a quadrangle: compare possible variants
8877 // and choose which of splits to be a quadrangle
8878 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8879 if ( nbFaceNodes == 3 ) {
8880 iBestQuad = nbSplits;
8883 else if ( nbFaceNodes == 4 ) {
8884 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8885 double aBestRate = DBL_MAX;
8886 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8888 double aBadRate = 0;
8889 // evaluate elements quality
8890 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8891 if ( iSplit == iQuad ) {
8892 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8896 aBadRate += getBadRate( &quad, aCrit );
8899 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8901 nodes[ iSplit < iQuad ? i4 : i3 ]);
8902 aBadRate += getBadRate( &tria, aCrit );
8906 if ( aBadRate < aBestRate ) {
8908 aBestRate = aBadRate;
8913 // create new elements
8915 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8917 if ( iSplit == iBestQuad )
8918 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8923 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8925 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8928 const SMDS_MeshNode* newNodes[ 4 ];
8929 newNodes[ 0 ] = linkNodes[ i1 ];
8930 newNodes[ 1 ] = linkNodes[ i2 ];
8931 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8932 newNodes[ 3 ] = nodes[ i4 ];
8933 if (iSplit == iBestQuad)
8934 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8936 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8938 } // end if(!theFace->IsQuadratic())
8940 else { // theFace is quadratic
8941 // we have to split theFace on simple triangles and one simple quadrangle
8943 int nbshift = tmp*2;
8944 // shift nodes in nodes[] by nbshift
8946 for(i=0; i<nbshift; i++) {
8947 const SMDS_MeshNode* n = nodes[0];
8948 for(j=0; j<nbFaceNodes-1; j++) {
8949 nodes[j] = nodes[j+1];
8951 nodes[nbFaceNodes-1] = n;
8953 il1 = il1 - nbshift;
8954 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8955 // n0 n1 n2 n0 n1 n2
8956 // +-----+-----+ +-----+-----+
8965 // create new elements
8967 if ( nbFaceNodes == 6 ) { // quadratic triangle
8968 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8969 if ( theFace->IsMediumNode(nodes[il1]) ) {
8970 // create quadrangle
8971 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8977 // create quadrangle
8978 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8984 else { // nbFaceNodes==8 - quadratic quadrangle
8985 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8986 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8987 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8988 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8989 // create quadrangle
8990 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8996 // create quadrangle
8997 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9003 // create needed triangles using n1,n2,n3 and inserted nodes
9004 int nbn = 2 + aNodesToInsert.size();
9005 vector<const SMDS_MeshNode*> aNodes(nbn);
9006 aNodes[0 ] = nodes[n1];
9007 aNodes[nbn-1] = nodes[n2];
9008 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9009 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9010 aNodes[iNode++] = *nIt;
9012 for ( i = 1; i < nbn; i++ )
9013 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9016 // remove the old face
9017 for ( size_t i = 0; i < newElems.size(); ++i )
9020 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9021 myLastCreatedElems.Append( newElems[i] );
9023 ReplaceElemInGroups( theFace, newElems, aMesh );
9024 aMesh->RemoveElement(theFace);
9026 } // InsertNodesIntoLink()
9028 //=======================================================================
9029 //function : UpdateVolumes
9031 //=======================================================================
9033 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9034 const SMDS_MeshNode* theBetweenNode2,
9035 list<const SMDS_MeshNode*>& theNodesToInsert)
9037 myLastCreatedElems.Clear();
9038 myLastCreatedNodes.Clear();
9040 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9041 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9042 const SMDS_MeshElement* elem = invElemIt->next();
9044 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9045 SMDS_VolumeTool aVolume (elem);
9046 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9049 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9050 int iface, nbFaces = aVolume.NbFaces();
9051 vector<const SMDS_MeshNode *> poly_nodes;
9052 vector<int> quantities (nbFaces);
9054 for (iface = 0; iface < nbFaces; iface++) {
9055 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9056 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9057 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9059 for (int inode = 0; inode < nbFaceNodes; inode++) {
9060 poly_nodes.push_back(faceNodes[inode]);
9062 if (nbInserted == 0) {
9063 if (faceNodes[inode] == theBetweenNode1) {
9064 if (faceNodes[inode + 1] == theBetweenNode2) {
9065 nbInserted = theNodesToInsert.size();
9067 // add nodes to insert
9068 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9069 for (; nIt != theNodesToInsert.end(); nIt++) {
9070 poly_nodes.push_back(*nIt);
9074 else if (faceNodes[inode] == theBetweenNode2) {
9075 if (faceNodes[inode + 1] == theBetweenNode1) {
9076 nbInserted = theNodesToInsert.size();
9078 // add nodes to insert in reversed order
9079 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9081 for (; nIt != theNodesToInsert.begin(); nIt--) {
9082 poly_nodes.push_back(*nIt);
9084 poly_nodes.push_back(*nIt);
9091 quantities[iface] = nbFaceNodes + nbInserted;
9094 // Replace the volume
9095 SMESHDS_Mesh *aMesh = GetMeshDS();
9097 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9099 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9100 myLastCreatedElems.Append( newElem );
9101 ReplaceElemInGroups( elem, newElem, aMesh );
9103 aMesh->RemoveElement( elem );
9109 //================================================================================
9111 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9113 //================================================================================
9115 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9116 vector<const SMDS_MeshNode *> & nodes,
9117 vector<int> & nbNodeInFaces )
9120 nbNodeInFaces.clear();
9121 SMDS_VolumeTool vTool ( elem );
9122 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9124 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9125 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9126 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9131 //=======================================================================
9133 * \brief Convert elements contained in a sub-mesh to quadratic
9134 * \return int - nb of checked elements
9136 //=======================================================================
9138 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9139 SMESH_MesherHelper& theHelper,
9140 const bool theForce3d)
9143 if( !theSm ) return nbElem;
9145 vector<int> nbNodeInFaces;
9146 vector<const SMDS_MeshNode *> nodes;
9147 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9148 while(ElemItr->more())
9151 const SMDS_MeshElement* elem = ElemItr->next();
9152 if( !elem ) continue;
9154 // analyse a necessity of conversion
9155 const SMDSAbs_ElementType aType = elem->GetType();
9156 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9158 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9159 bool hasCentralNodes = false;
9160 if ( elem->IsQuadratic() )
9163 switch ( aGeomType ) {
9164 case SMDSEntity_Quad_Triangle:
9165 case SMDSEntity_Quad_Quadrangle:
9166 case SMDSEntity_Quad_Hexa:
9167 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9169 case SMDSEntity_BiQuad_Triangle:
9170 case SMDSEntity_BiQuad_Quadrangle:
9171 case SMDSEntity_TriQuad_Hexa:
9172 alreadyOK = theHelper.GetIsBiQuadratic();
9173 hasCentralNodes = true;
9178 // take into account already present modium nodes
9180 case SMDSAbs_Volume:
9181 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9183 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9185 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9191 // get elem data needed to re-create it
9193 const int id = elem->GetID();
9194 const int nbNodes = elem->NbCornerNodes();
9195 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9196 if ( aGeomType == SMDSEntity_Polyhedra )
9197 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9198 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9199 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9201 // remove a linear element
9202 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9204 // remove central nodes of biquadratic elements (biquad->quad convertion)
9205 if ( hasCentralNodes )
9206 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9207 if ( nodes[i]->NbInverseElements() == 0 )
9208 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9210 const SMDS_MeshElement* NewElem = 0;
9216 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9224 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9227 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9230 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9234 case SMDSAbs_Volume :
9238 case SMDSEntity_Tetra:
9239 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9241 case SMDSEntity_Pyramid:
9242 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9244 case SMDSEntity_Penta:
9245 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9247 case SMDSEntity_Hexa:
9248 case SMDSEntity_Quad_Hexa:
9249 case SMDSEntity_TriQuad_Hexa:
9250 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9251 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9253 case SMDSEntity_Hexagonal_Prism:
9255 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9262 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9263 if( NewElem && NewElem->getshapeId() < 1 )
9264 theSm->AddElement( NewElem );
9268 //=======================================================================
9269 //function : ConvertToQuadratic
9271 //=======================================================================
9273 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9275 SMESHDS_Mesh* meshDS = GetMeshDS();
9277 SMESH_MesherHelper aHelper(*myMesh);
9279 aHelper.SetIsQuadratic( true );
9280 aHelper.SetIsBiQuadratic( theToBiQuad );
9281 aHelper.SetElementsOnShape(true);
9282 aHelper.ToFixNodeParameters( true );
9284 // convert elements assigned to sub-meshes
9285 int nbCheckedElems = 0;
9286 if ( myMesh->HasShapeToMesh() )
9288 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9290 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9291 while ( smIt->more() ) {
9292 SMESH_subMesh* sm = smIt->next();
9293 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9294 aHelper.SetSubShape( sm->GetSubShape() );
9295 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9301 // convert elements NOT assigned to sub-meshes
9302 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9303 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9305 aHelper.SetElementsOnShape(false);
9306 SMESHDS_SubMesh *smDS = 0;
9309 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9310 while( aEdgeItr->more() )
9312 const SMDS_MeshEdge* edge = aEdgeItr->next();
9313 if ( !edge->IsQuadratic() )
9315 int id = edge->GetID();
9316 const SMDS_MeshNode* n1 = edge->GetNode(0);
9317 const SMDS_MeshNode* n2 = edge->GetNode(1);
9319 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9321 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9322 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9326 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9331 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9332 while( aFaceItr->more() )
9334 const SMDS_MeshFace* face = aFaceItr->next();
9335 if ( !face ) continue;
9337 const SMDSAbs_EntityType type = face->GetEntityType();
9341 case SMDSEntity_Quad_Triangle:
9342 case SMDSEntity_Quad_Quadrangle:
9343 alreadyOK = !theToBiQuad;
9344 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9346 case SMDSEntity_BiQuad_Triangle:
9347 case SMDSEntity_BiQuad_Quadrangle:
9348 alreadyOK = theToBiQuad;
9349 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9351 default: alreadyOK = false;
9356 const int id = face->GetID();
9357 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9359 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9361 SMDS_MeshFace * NewFace = 0;
9364 case SMDSEntity_Triangle:
9365 case SMDSEntity_Quad_Triangle:
9366 case SMDSEntity_BiQuad_Triangle:
9367 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9368 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9369 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9372 case SMDSEntity_Quadrangle:
9373 case SMDSEntity_Quad_Quadrangle:
9374 case SMDSEntity_BiQuad_Quadrangle:
9375 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9376 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9377 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9381 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9383 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9387 vector<int> nbNodeInFaces;
9388 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9389 while(aVolumeItr->more())
9391 const SMDS_MeshVolume* volume = aVolumeItr->next();
9392 if ( !volume ) continue;
9394 const SMDSAbs_EntityType type = volume->GetEntityType();
9395 if ( volume->IsQuadratic() )
9400 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9401 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9402 default: alreadyOK = true;
9406 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9410 const int id = volume->GetID();
9411 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9412 if ( type == SMDSEntity_Polyhedra )
9413 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9414 else if ( type == SMDSEntity_Hexagonal_Prism )
9415 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9417 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9419 SMDS_MeshVolume * NewVolume = 0;
9422 case SMDSEntity_Tetra:
9423 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9425 case SMDSEntity_Hexa:
9426 case SMDSEntity_Quad_Hexa:
9427 case SMDSEntity_TriQuad_Hexa:
9428 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9429 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9430 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9431 if ( nodes[i]->NbInverseElements() == 0 )
9432 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9434 case SMDSEntity_Pyramid:
9435 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9436 nodes[3], nodes[4], id, theForce3d);
9438 case SMDSEntity_Penta:
9439 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9440 nodes[3], nodes[4], nodes[5], id, theForce3d);
9442 case SMDSEntity_Hexagonal_Prism:
9444 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9446 ReplaceElemInGroups(volume, NewVolume, meshDS);
9451 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9452 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9453 // aHelper.FixQuadraticElements(myError);
9454 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9458 //================================================================================
9460 * \brief Makes given elements quadratic
9461 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9462 * \param theElements - elements to make quadratic
9464 //================================================================================
9466 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9467 TIDSortedElemSet& theElements,
9468 const bool theToBiQuad)
9470 if ( theElements.empty() ) return;
9472 // we believe that all theElements are of the same type
9473 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9475 // get all nodes shared by theElements
9476 TIDSortedNodeSet allNodes;
9477 TIDSortedElemSet::iterator eIt = theElements.begin();
9478 for ( ; eIt != theElements.end(); ++eIt )
9479 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9481 // complete theElements with elements of lower dim whose all nodes are in allNodes
9483 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9484 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9485 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9486 for ( ; nIt != allNodes.end(); ++nIt )
9488 const SMDS_MeshNode* n = *nIt;
9489 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9490 while ( invIt->more() )
9492 const SMDS_MeshElement* e = invIt->next();
9493 const SMDSAbs_ElementType type = e->GetType();
9494 if ( e->IsQuadratic() )
9496 quadAdjacentElems[ type ].insert( e );
9499 switch ( e->GetEntityType() ) {
9500 case SMDSEntity_Quad_Triangle:
9501 case SMDSEntity_Quad_Quadrangle:
9502 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9503 case SMDSEntity_BiQuad_Triangle:
9504 case SMDSEntity_BiQuad_Quadrangle:
9505 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9506 default: alreadyOK = true;
9511 if ( type >= elemType )
9512 continue; // same type or more complex linear element
9514 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9515 continue; // e is already checked
9519 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9520 while ( nodeIt->more() && allIn )
9521 allIn = allNodes.count( nodeIt->next() );
9523 theElements.insert(e );
9527 SMESH_MesherHelper helper(*myMesh);
9528 helper.SetIsQuadratic( true );
9529 helper.SetIsBiQuadratic( theToBiQuad );
9531 // add links of quadratic adjacent elements to the helper
9533 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9534 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9535 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9537 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9539 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9540 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9541 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9543 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9545 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9546 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9547 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9549 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9552 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9554 SMESHDS_Mesh* meshDS = GetMeshDS();
9555 SMESHDS_SubMesh* smDS = 0;
9556 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9558 const SMDS_MeshElement* elem = *eIt;
9561 int nbCentralNodes = 0;
9562 switch ( elem->GetEntityType() ) {
9563 // linear convertible
9564 case SMDSEntity_Edge:
9565 case SMDSEntity_Triangle:
9566 case SMDSEntity_Quadrangle:
9567 case SMDSEntity_Tetra:
9568 case SMDSEntity_Pyramid:
9569 case SMDSEntity_Hexa:
9570 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9571 // quadratic that can become bi-quadratic
9572 case SMDSEntity_Quad_Triangle:
9573 case SMDSEntity_Quad_Quadrangle:
9574 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9576 case SMDSEntity_BiQuad_Triangle:
9577 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9578 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9580 default: alreadyOK = true;
9582 if ( alreadyOK ) continue;
9584 const SMDSAbs_ElementType type = elem->GetType();
9585 const int id = elem->GetID();
9586 const int nbNodes = elem->NbCornerNodes();
9587 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9589 helper.SetSubShape( elem->getshapeId() );
9591 if ( !smDS || !smDS->Contains( elem ))
9592 smDS = meshDS->MeshElements( elem->getshapeId() );
9593 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9595 SMDS_MeshElement * newElem = 0;
9598 case 4: // cases for most frequently used element types go first (for optimization)
9599 if ( type == SMDSAbs_Volume )
9600 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9602 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9605 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9606 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9609 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9612 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9615 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9616 nodes[4], id, theForce3d);
9619 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9620 nodes[4], nodes[5], id, theForce3d);
9624 ReplaceElemInGroups( elem, newElem, meshDS);
9625 if( newElem && smDS )
9626 smDS->AddElement( newElem );
9628 // remove central nodes
9629 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9630 if ( nodes[i]->NbInverseElements() == 0 )
9631 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9633 } // loop on theElements
9636 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9637 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9638 // helper.FixQuadraticElements( myError );
9639 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9643 //=======================================================================
9645 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9646 * \return int - nb of checked elements
9648 //=======================================================================
9650 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9651 SMDS_ElemIteratorPtr theItr,
9652 const int theShapeID)
9655 SMESHDS_Mesh* meshDS = GetMeshDS();
9656 ElemFeatures elemType;
9657 vector<const SMDS_MeshNode *> nodes;
9659 while( theItr->more() )
9661 const SMDS_MeshElement* elem = theItr->next();
9663 if( elem && elem->IsQuadratic())
9666 int nbCornerNodes = elem->NbCornerNodes();
9667 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9669 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9671 //remove a quadratic element
9672 if ( !theSm || !theSm->Contains( elem ))
9673 theSm = meshDS->MeshElements( elem->getshapeId() );
9674 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9676 // remove medium nodes
9677 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9678 if ( nodes[i]->NbInverseElements() == 0 )
9679 meshDS->RemoveFreeNode( nodes[i], theSm );
9681 // add a linear element
9682 nodes.resize( nbCornerNodes );
9683 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9684 ReplaceElemInGroups(elem, newElem, meshDS);
9685 if( theSm && newElem )
9686 theSm->AddElement( newElem );
9692 //=======================================================================
9693 //function : ConvertFromQuadratic
9695 //=======================================================================
9697 bool SMESH_MeshEditor::ConvertFromQuadratic()
9699 int nbCheckedElems = 0;
9700 if ( myMesh->HasShapeToMesh() )
9702 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9704 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9705 while ( smIt->more() ) {
9706 SMESH_subMesh* sm = smIt->next();
9707 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9708 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9714 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9715 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9717 SMESHDS_SubMesh *aSM = 0;
9718 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9726 //================================================================================
9728 * \brief Return true if all medium nodes of the element are in the node set
9730 //================================================================================
9732 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9734 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9735 if ( !nodeSet.count( elem->GetNode(i) ))
9741 //================================================================================
9743 * \brief Makes given elements linear
9745 //================================================================================
9747 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9749 if ( theElements.empty() ) return;
9751 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9752 set<int> mediumNodeIDs;
9753 TIDSortedElemSet::iterator eIt = theElements.begin();
9754 for ( ; eIt != theElements.end(); ++eIt )
9756 const SMDS_MeshElement* e = *eIt;
9757 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9758 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9761 // replace given elements by linear ones
9762 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9763 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9765 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9766 // except those elements sharing medium nodes of quadratic element whose medium nodes
9767 // are not all in mediumNodeIDs
9769 // get remaining medium nodes
9770 TIDSortedNodeSet mediumNodes;
9771 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9772 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9773 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9774 mediumNodes.insert( mediumNodes.end(), n );
9776 // find more quadratic elements to convert
9777 TIDSortedElemSet moreElemsToConvert;
9778 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9779 for ( ; nIt != mediumNodes.end(); ++nIt )
9781 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9782 while ( invIt->more() )
9784 const SMDS_MeshElement* e = invIt->next();
9785 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9787 // find a more complex element including e and
9788 // whose medium nodes are not in mediumNodes
9789 bool complexFound = false;
9790 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9792 SMDS_ElemIteratorPtr invIt2 =
9793 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9794 while ( invIt2->more() )
9796 const SMDS_MeshElement* eComplex = invIt2->next();
9797 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9799 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9800 if ( nbCommonNodes == e->NbNodes())
9802 complexFound = true;
9803 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9809 if ( !complexFound )
9810 moreElemsToConvert.insert( e );
9814 elemIt = elemSetIterator( moreElemsToConvert );
9815 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9818 //=======================================================================
9819 //function : SewSideElements
9821 //=======================================================================
9823 SMESH_MeshEditor::Sew_Error
9824 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9825 TIDSortedElemSet& theSide2,
9826 const SMDS_MeshNode* theFirstNode1,
9827 const SMDS_MeshNode* theFirstNode2,
9828 const SMDS_MeshNode* theSecondNode1,
9829 const SMDS_MeshNode* theSecondNode2)
9831 myLastCreatedElems.Clear();
9832 myLastCreatedNodes.Clear();
9834 if ( theSide1.size() != theSide2.size() )
9835 return SEW_DIFF_NB_OF_ELEMENTS;
9837 Sew_Error aResult = SEW_OK;
9839 // 1. Build set of faces representing each side
9840 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9841 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9843 // =======================================================================
9844 // 1. Build set of faces representing each side:
9845 // =======================================================================
9846 // a. build set of nodes belonging to faces
9847 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9848 // c. create temporary faces representing side of volumes if correspondent
9849 // face does not exist
9851 SMESHDS_Mesh* aMesh = GetMeshDS();
9852 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9853 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9854 TIDSortedElemSet faceSet1, faceSet2;
9855 set<const SMDS_MeshElement*> volSet1, volSet2;
9856 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9857 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9858 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9859 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9860 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9861 int iSide, iFace, iNode;
9863 list<const SMDS_MeshElement* > tempFaceList;
9864 for ( iSide = 0; iSide < 2; iSide++ ) {
9865 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9866 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9867 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9868 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9869 set<const SMDS_MeshElement*>::iterator vIt;
9870 TIDSortedElemSet::iterator eIt;
9871 set<const SMDS_MeshNode*>::iterator nIt;
9873 // check that given nodes belong to given elements
9874 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9875 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9876 int firstIndex = -1, secondIndex = -1;
9877 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9878 const SMDS_MeshElement* elem = *eIt;
9879 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9880 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9881 if ( firstIndex > -1 && secondIndex > -1 ) break;
9883 if ( firstIndex < 0 || secondIndex < 0 ) {
9884 // we can simply return until temporary faces created
9885 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9888 // -----------------------------------------------------------
9889 // 1a. Collect nodes of existing faces
9890 // and build set of face nodes in order to detect missing
9891 // faces corresponding to sides of volumes
9892 // -----------------------------------------------------------
9894 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9896 // loop on the given element of a side
9897 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9898 //const SMDS_MeshElement* elem = *eIt;
9899 const SMDS_MeshElement* elem = *eIt;
9900 if ( elem->GetType() == SMDSAbs_Face ) {
9901 faceSet->insert( elem );
9902 set <const SMDS_MeshNode*> faceNodeSet;
9903 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9904 while ( nodeIt->more() ) {
9905 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9906 nodeSet->insert( n );
9907 faceNodeSet.insert( n );
9909 setOfFaceNodeSet.insert( faceNodeSet );
9911 else if ( elem->GetType() == SMDSAbs_Volume )
9912 volSet->insert( elem );
9914 // ------------------------------------------------------------------------------
9915 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9916 // ------------------------------------------------------------------------------
9918 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9919 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9920 while ( fIt->more() ) { // loop on faces sharing a node
9921 const SMDS_MeshElement* f = fIt->next();
9922 if ( faceSet->find( f ) == faceSet->end() ) {
9923 // check if all nodes are in nodeSet and
9924 // complete setOfFaceNodeSet if they are
9925 set <const SMDS_MeshNode*> faceNodeSet;
9926 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9927 bool allInSet = true;
9928 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9929 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9930 if ( nodeSet->find( n ) == nodeSet->end() )
9933 faceNodeSet.insert( n );
9936 faceSet->insert( f );
9937 setOfFaceNodeSet.insert( faceNodeSet );
9943 // -------------------------------------------------------------------------
9944 // 1c. Create temporary faces representing sides of volumes if correspondent
9945 // face does not exist
9946 // -------------------------------------------------------------------------
9948 if ( !volSet->empty() ) {
9949 //int nodeSetSize = nodeSet->size();
9951 // loop on given volumes
9952 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9953 SMDS_VolumeTool vol (*vIt);
9954 // loop on volume faces: find free faces
9955 // --------------------------------------
9956 list<const SMDS_MeshElement* > freeFaceList;
9957 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9958 if ( !vol.IsFreeFace( iFace ))
9960 // check if there is already a face with same nodes in a face set
9961 const SMDS_MeshElement* aFreeFace = 0;
9962 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9963 int nbNodes = vol.NbFaceNodes( iFace );
9964 set <const SMDS_MeshNode*> faceNodeSet;
9965 vol.GetFaceNodes( iFace, faceNodeSet );
9966 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9968 // no such a face is given but it still can exist, check it
9969 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9970 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9973 // create a temporary face
9974 if ( nbNodes == 3 ) {
9975 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9976 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9978 else if ( nbNodes == 4 ) {
9979 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9980 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9983 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9984 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9985 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9988 tempFaceList.push_back( aFreeFace );
9992 freeFaceList.push_back( aFreeFace );
9994 } // loop on faces of a volume
9996 // choose one of several free faces of a volume
9997 // --------------------------------------------
9998 if ( freeFaceList.size() > 1 ) {
9999 // choose a face having max nb of nodes shared by other elems of a side
10000 int maxNbNodes = -1;
10001 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10002 while ( fIt != freeFaceList.end() ) { // loop on free faces
10003 int nbSharedNodes = 0;
10004 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10005 while ( nodeIt->more() ) { // loop on free face nodes
10006 const SMDS_MeshNode* n =
10007 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10008 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10009 while ( invElemIt->more() ) {
10010 const SMDS_MeshElement* e = invElemIt->next();
10011 nbSharedNodes += faceSet->count( e );
10012 nbSharedNodes += elemSet->count( e );
10015 if ( nbSharedNodes > maxNbNodes ) {
10016 maxNbNodes = nbSharedNodes;
10017 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10019 else if ( nbSharedNodes == maxNbNodes ) {
10023 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10026 if ( freeFaceList.size() > 1 )
10028 // could not choose one face, use another way
10029 // choose a face most close to the bary center of the opposite side
10030 gp_XYZ aBC( 0., 0., 0. );
10031 set <const SMDS_MeshNode*> addedNodes;
10032 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10033 eIt = elemSet2->begin();
10034 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10035 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10036 while ( nodeIt->more() ) { // loop on free face nodes
10037 const SMDS_MeshNode* n =
10038 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10039 if ( addedNodes.insert( n ).second )
10040 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10043 aBC /= addedNodes.size();
10044 double minDist = DBL_MAX;
10045 fIt = freeFaceList.begin();
10046 while ( fIt != freeFaceList.end() ) { // loop on free faces
10048 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10049 while ( nodeIt->more() ) { // loop on free face nodes
10050 const SMDS_MeshNode* n =
10051 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10052 gp_XYZ p( n->X(),n->Y(),n->Z() );
10053 dist += ( aBC - p ).SquareModulus();
10055 if ( dist < minDist ) {
10057 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10060 fIt = freeFaceList.erase( fIt++ );
10063 } // choose one of several free faces of a volume
10065 if ( freeFaceList.size() == 1 ) {
10066 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10067 faceSet->insert( aFreeFace );
10068 // complete a node set with nodes of a found free face
10069 // for ( iNode = 0; iNode < ; iNode++ )
10070 // nodeSet->insert( fNodes[ iNode ] );
10073 } // loop on volumes of a side
10075 // // complete a set of faces if new nodes in a nodeSet appeared
10076 // // ----------------------------------------------------------
10077 // if ( nodeSetSize != nodeSet->size() ) {
10078 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10079 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10080 // while ( fIt->more() ) { // loop on faces sharing a node
10081 // const SMDS_MeshElement* f = fIt->next();
10082 // if ( faceSet->find( f ) == faceSet->end() ) {
10083 // // check if all nodes are in nodeSet and
10084 // // complete setOfFaceNodeSet if they are
10085 // set <const SMDS_MeshNode*> faceNodeSet;
10086 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10087 // bool allInSet = true;
10088 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10089 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10090 // if ( nodeSet->find( n ) == nodeSet->end() )
10091 // allInSet = false;
10093 // faceNodeSet.insert( n );
10095 // if ( allInSet ) {
10096 // faceSet->insert( f );
10097 // setOfFaceNodeSet.insert( faceNodeSet );
10103 } // Create temporary faces, if there are volumes given
10106 if ( faceSet1.size() != faceSet2.size() ) {
10107 // delete temporary faces: they are in reverseElements of actual nodes
10108 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10109 // while ( tmpFaceIt->more() )
10110 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10111 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10112 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10113 // aMesh->RemoveElement(*tmpFaceIt);
10114 MESSAGE("Diff nb of faces");
10115 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10118 // ============================================================
10119 // 2. Find nodes to merge:
10120 // bind a node to remove to a node to put instead
10121 // ============================================================
10123 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10124 if ( theFirstNode1 != theFirstNode2 )
10125 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10126 if ( theSecondNode1 != theSecondNode2 )
10127 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10129 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10130 set< long > linkIdSet; // links to process
10131 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10133 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10134 list< NLink > linkList[2];
10135 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10136 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10137 // loop on links in linkList; find faces by links and append links
10138 // of the found faces to linkList
10139 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10140 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10142 NLink link[] = { *linkIt[0], *linkIt[1] };
10143 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10144 if ( !linkIdSet.count( linkID ) )
10147 // by links, find faces in the face sets,
10148 // and find indices of link nodes in the found faces;
10149 // in a face set, there is only one or no face sharing a link
10150 // ---------------------------------------------------------------
10152 const SMDS_MeshElement* face[] = { 0, 0 };
10153 vector<const SMDS_MeshNode*> fnodes[2];
10154 int iLinkNode[2][2];
10155 TIDSortedElemSet avoidSet;
10156 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10157 const SMDS_MeshNode* n1 = link[iSide].first;
10158 const SMDS_MeshNode* n2 = link[iSide].second;
10159 //cout << "Side " << iSide << " ";
10160 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10161 // find a face by two link nodes
10162 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10163 *faceSetPtr[ iSide ], avoidSet,
10164 &iLinkNode[iSide][0],
10165 &iLinkNode[iSide][1] );
10166 if ( face[ iSide ])
10168 //cout << " F " << face[ iSide]->GetID() <<endl;
10169 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10170 // put face nodes to fnodes
10171 if ( face[ iSide ]->IsQuadratic() )
10173 // use interlaced nodes iterator
10174 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10175 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10176 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10177 while ( nIter->more() )
10178 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10182 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10183 face[ iSide ]->end_nodes() );
10185 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10189 // check similarity of elements of the sides
10190 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10191 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10192 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10193 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10196 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10198 break; // do not return because it's necessary to remove tmp faces
10201 // set nodes to merge
10202 // -------------------
10204 if ( face[0] && face[1] ) {
10205 const int nbNodes = face[0]->NbNodes();
10206 if ( nbNodes != face[1]->NbNodes() ) {
10207 MESSAGE("Diff nb of face nodes");
10208 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10209 break; // do not return because it s necessary to remove tmp faces
10211 bool reverse[] = { false, false }; // order of nodes in the link
10212 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10213 // analyse link orientation in faces
10214 int i1 = iLinkNode[ iSide ][ 0 ];
10215 int i2 = iLinkNode[ iSide ][ 1 ];
10216 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10218 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10219 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10220 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10222 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10223 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10226 // add other links of the faces to linkList
10227 // -----------------------------------------
10229 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10230 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10231 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10232 if ( !iter_isnew.second ) { // already in a set: no need to process
10233 linkIdSet.erase( iter_isnew.first );
10235 else // new in set == encountered for the first time: add
10237 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10238 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10239 linkList[0].push_back ( NLink( n1, n2 ));
10240 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10245 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10248 } // loop on link lists
10250 if ( aResult == SEW_OK &&
10251 ( //linkIt[0] != linkList[0].end() ||
10252 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10253 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10254 " " << (faceSetPtr[1]->empty()));
10255 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10258 // ====================================================================
10259 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10260 // ====================================================================
10262 // delete temporary faces
10263 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10264 // while ( tmpFaceIt->more() )
10265 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10266 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10267 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10268 aMesh->RemoveElement(*tmpFaceIt);
10270 if ( aResult != SEW_OK)
10273 list< int > nodeIDsToRemove;
10274 vector< const SMDS_MeshNode*> nodes;
10275 ElemFeatures elemType;
10277 // loop on nodes replacement map
10278 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10279 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10280 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10282 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10283 nodeIDsToRemove.push_back( nToRemove->GetID() );
10284 // loop on elements sharing nToRemove
10285 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10286 while ( invElemIt->more() ) {
10287 const SMDS_MeshElement* e = invElemIt->next();
10288 // get a new suite of nodes: make replacement
10289 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10290 nodes.resize( nbNodes );
10291 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10292 while ( nIt->more() ) {
10293 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10294 nnIt = nReplaceMap.find( n );
10295 if ( nnIt != nReplaceMap.end() ) {
10297 n = (*nnIt).second;
10301 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10302 // elemIDsToRemove.push_back( e->GetID() );
10306 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10307 aMesh->RemoveElement( e );
10309 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10311 AddToSameGroups( newElem, e, aMesh );
10312 if ( int aShapeId = e->getshapeId() )
10313 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10319 Remove( nodeIDsToRemove, true );
10324 //================================================================================
10326 * \brief Find corresponding nodes in two sets of faces
10327 * \param theSide1 - first face set
10328 * \param theSide2 - second first face
10329 * \param theFirstNode1 - a boundary node of set 1
10330 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10331 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10332 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10333 * \param nReplaceMap - output map of corresponding nodes
10334 * \return bool - is a success or not
10336 //================================================================================
10339 //#define DEBUG_MATCHING_NODES
10342 SMESH_MeshEditor::Sew_Error
10343 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10344 set<const SMDS_MeshElement*>& theSide2,
10345 const SMDS_MeshNode* theFirstNode1,
10346 const SMDS_MeshNode* theFirstNode2,
10347 const SMDS_MeshNode* theSecondNode1,
10348 const SMDS_MeshNode* theSecondNode2,
10349 TNodeNodeMap & nReplaceMap)
10351 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10353 nReplaceMap.clear();
10354 if ( theFirstNode1 != theFirstNode2 )
10355 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10356 if ( theSecondNode1 != theSecondNode2 )
10357 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10359 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10360 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10362 list< NLink > linkList[2];
10363 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10364 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10366 // loop on links in linkList; find faces by links and append links
10367 // of the found faces to linkList
10368 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10369 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10370 NLink link[] = { *linkIt[0], *linkIt[1] };
10371 if ( linkSet.find( link[0] ) == linkSet.end() )
10374 // by links, find faces in the face sets,
10375 // and find indices of link nodes in the found faces;
10376 // in a face set, there is only one or no face sharing a link
10377 // ---------------------------------------------------------------
10379 const SMDS_MeshElement* face[] = { 0, 0 };
10380 list<const SMDS_MeshNode*> notLinkNodes[2];
10381 //bool reverse[] = { false, false }; // order of notLinkNodes
10383 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10385 const SMDS_MeshNode* n1 = link[iSide].first;
10386 const SMDS_MeshNode* n2 = link[iSide].second;
10387 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10388 set< const SMDS_MeshElement* > facesOfNode1;
10389 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10391 // during a loop of the first node, we find all faces around n1,
10392 // during a loop of the second node, we find one face sharing both n1 and n2
10393 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10394 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10395 while ( fIt->more() ) { // loop on faces sharing a node
10396 const SMDS_MeshElement* f = fIt->next();
10397 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10398 ! facesOfNode1.insert( f ).second ) // f encounters twice
10400 if ( face[ iSide ] ) {
10401 MESSAGE( "2 faces per link " );
10402 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10405 faceSet->erase( f );
10407 // get not link nodes
10408 int nbN = f->NbNodes();
10409 if ( f->IsQuadratic() )
10411 nbNodes[ iSide ] = nbN;
10412 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10413 int i1 = f->GetNodeIndex( n1 );
10414 int i2 = f->GetNodeIndex( n2 );
10415 int iEnd = nbN, iBeg = -1, iDelta = 1;
10416 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10418 std::swap( iEnd, iBeg ); iDelta = -1;
10423 if ( i == iEnd ) i = iBeg + iDelta;
10424 if ( i == i1 ) break;
10425 nodes.push_back ( f->GetNode( i ) );
10431 // check similarity of elements of the sides
10432 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10433 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10434 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10435 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10438 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10442 // set nodes to merge
10443 // -------------------
10445 if ( face[0] && face[1] ) {
10446 if ( nbNodes[0] != nbNodes[1] ) {
10447 MESSAGE("Diff nb of face nodes");
10448 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10450 #ifdef DEBUG_MATCHING_NODES
10451 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10452 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10453 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10455 int nbN = nbNodes[0];
10457 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10458 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10459 for ( int i = 0 ; i < nbN - 2; ++i ) {
10460 #ifdef DEBUG_MATCHING_NODES
10461 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10463 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10467 // add other links of the face 1 to linkList
10468 // -----------------------------------------
10470 const SMDS_MeshElement* f0 = face[0];
10471 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10472 for ( int i = 0; i < nbN; i++ )
10474 const SMDS_MeshNode* n2 = f0->GetNode( i );
10475 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10476 linkSet.insert( SMESH_TLink( n1, n2 ));
10477 if ( !iter_isnew.second ) { // already in a set: no need to process
10478 linkSet.erase( iter_isnew.first );
10480 else // new in set == encountered for the first time: add
10482 #ifdef DEBUG_MATCHING_NODES
10483 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10484 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10486 linkList[0].push_back ( NLink( n1, n2 ));
10487 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10492 } // loop on link lists
10497 //================================================================================
10499 * \brief Create elements equal (on same nodes) to given ones
10500 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10501 * elements of the uppest dimension are duplicated.
10503 //================================================================================
10505 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10507 ClearLastCreated();
10508 SMESHDS_Mesh* mesh = GetMeshDS();
10510 // get an element type and an iterator over elements
10512 SMDSAbs_ElementType type = SMDSAbs_All;
10513 SMDS_ElemIteratorPtr elemIt;
10514 vector< const SMDS_MeshElement* > allElems;
10515 if ( theElements.empty() )
10517 if ( mesh->NbNodes() == 0 )
10519 // get most complex type
10520 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10521 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10522 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10524 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10525 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10530 // put all elements in the vector <allElems>
10531 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10532 elemIt = mesh->elementsIterator( type );
10533 while ( elemIt->more() )
10534 allElems.push_back( elemIt->next());
10535 elemIt = elemSetIterator( allElems );
10539 type = (*theElements.begin())->GetType();
10540 elemIt = elemSetIterator( theElements );
10543 // duplicate elements
10545 ElemFeatures elemType;
10547 vector< const SMDS_MeshNode* > nodes;
10548 while ( elemIt->more() )
10550 const SMDS_MeshElement* elem = elemIt->next();
10551 if ( elem->GetType() != type )
10554 elemType.Init( elem, /*basicOnly=*/false );
10555 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10557 AddElement( nodes, elemType );
10561 //================================================================================
10563 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10564 \param theElems - the list of elements (edges or faces) to be replicated
10565 The nodes for duplication could be found from these elements
10566 \param theNodesNot - list of nodes to NOT replicate
10567 \param theAffectedElems - the list of elements (cells and edges) to which the
10568 replicated nodes should be associated to.
10569 \return TRUE if operation has been completed successfully, FALSE otherwise
10571 //================================================================================
10573 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10574 const TIDSortedElemSet& theNodesNot,
10575 const TIDSortedElemSet& theAffectedElems )
10577 myLastCreatedElems.Clear();
10578 myLastCreatedNodes.Clear();
10580 if ( theElems.size() == 0 )
10583 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10588 TNodeNodeMap anOldNodeToNewNode;
10589 // duplicate elements and nodes
10590 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10591 // replce nodes by duplications
10592 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10596 //================================================================================
10598 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10599 \param theMeshDS - mesh instance
10600 \param theElems - the elements replicated or modified (nodes should be changed)
10601 \param theNodesNot - nodes to NOT replicate
10602 \param theNodeNodeMap - relation of old node to new created node
10603 \param theIsDoubleElem - flag os to replicate element or modify
10604 \return TRUE if operation has been completed successfully, FALSE otherwise
10606 //================================================================================
10608 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10609 const TIDSortedElemSet& theElems,
10610 const TIDSortedElemSet& theNodesNot,
10611 TNodeNodeMap& theNodeNodeMap,
10612 const bool theIsDoubleElem )
10614 // iterate through element and duplicate them (by nodes duplication)
10616 std::vector<const SMDS_MeshNode*> newNodes;
10617 ElemFeatures elemType;
10619 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10620 for ( ; elemItr != theElems.end(); ++elemItr )
10622 const SMDS_MeshElement* anElem = *elemItr;
10626 // duplicate nodes to duplicate element
10627 bool isDuplicate = false;
10628 newNodes.resize( anElem->NbNodes() );
10629 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10631 while ( anIter->more() )
10633 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10634 const SMDS_MeshNode* aNewNode = aCurrNode;
10635 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10636 if ( n2n != theNodeNodeMap.end() )
10638 aNewNode = n2n->second;
10640 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10643 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10644 copyPosition( aCurrNode, aNewNode );
10645 theNodeNodeMap[ aCurrNode ] = aNewNode;
10646 myLastCreatedNodes.Append( aNewNode );
10648 isDuplicate |= (aCurrNode != aNewNode);
10649 newNodes[ ind++ ] = aNewNode;
10651 if ( !isDuplicate )
10654 if ( theIsDoubleElem )
10655 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10657 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10664 //================================================================================
10666 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10667 \param theNodes - identifiers of nodes to be doubled
10668 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10669 nodes. If list of element identifiers is empty then nodes are doubled but
10670 they not assigned to elements
10671 \return TRUE if operation has been completed successfully, FALSE otherwise
10673 //================================================================================
10675 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10676 const std::list< int >& theListOfModifiedElems )
10678 myLastCreatedElems.Clear();
10679 myLastCreatedNodes.Clear();
10681 if ( theListOfNodes.size() == 0 )
10684 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10688 // iterate through nodes and duplicate them
10690 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10692 std::list< int >::const_iterator aNodeIter;
10693 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10695 int aCurr = *aNodeIter;
10696 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10702 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10705 copyPosition( aNode, aNewNode );
10706 anOldNodeToNewNode[ aNode ] = aNewNode;
10707 myLastCreatedNodes.Append( aNewNode );
10711 // Create map of new nodes for modified elements
10713 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10715 std::list< int >::const_iterator anElemIter;
10716 for ( anElemIter = theListOfModifiedElems.begin();
10717 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10719 int aCurr = *anElemIter;
10720 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10724 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10726 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10728 while ( anIter->more() )
10730 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10731 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10733 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10734 aNodeArr[ ind++ ] = aNewNode;
10737 aNodeArr[ ind++ ] = aCurrNode;
10739 anElemToNodes[ anElem ] = aNodeArr;
10742 // Change nodes of elements
10744 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10745 anElemToNodesIter = anElemToNodes.begin();
10746 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10748 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10749 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10752 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10761 //================================================================================
10763 \brief Check if element located inside shape
10764 \return TRUE if IN or ON shape, FALSE otherwise
10766 //================================================================================
10768 template<class Classifier>
10769 bool isInside(const SMDS_MeshElement* theElem,
10770 Classifier& theClassifier,
10771 const double theTol)
10773 gp_XYZ centerXYZ (0, 0, 0);
10774 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10775 while (aNodeItr->more())
10776 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10778 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10779 theClassifier.Perform(aPnt, theTol);
10780 TopAbs_State aState = theClassifier.State();
10781 return (aState == TopAbs_IN || aState == TopAbs_ON );
10784 //================================================================================
10786 * \brief Classifier of the 3D point on the TopoDS_Face
10787 * with interaface suitable for isInside()
10789 //================================================================================
10791 struct _FaceClassifier
10793 Extrema_ExtPS _extremum;
10794 BRepAdaptor_Surface _surface;
10795 TopAbs_State _state;
10797 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10799 _extremum.Initialize( _surface,
10800 _surface.FirstUParameter(), _surface.LastUParameter(),
10801 _surface.FirstVParameter(), _surface.LastVParameter(),
10802 _surface.Tolerance(), _surface.Tolerance() );
10804 void Perform(const gp_Pnt& aPnt, double theTol)
10807 _state = TopAbs_OUT;
10808 _extremum.Perform(aPnt);
10809 if ( _extremum.IsDone() )
10810 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10811 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10813 TopAbs_State State() const
10820 //================================================================================
10822 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10823 This method is the first step of DoubleNodeElemGroupsInRegion.
10824 \param theElems - list of groups of elements (edges or faces) to be replicated
10825 \param theNodesNot - list of groups of nodes not to replicated
10826 \param theShape - shape to detect affected elements (element which geometric center
10827 located on or inside shape). If the shape is null, detection is done on faces orientations
10828 (select elements with a gravity center on the side given by faces normals).
10829 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10830 The replicated nodes should be associated to affected elements.
10831 \return groups of affected elements
10832 \sa DoubleNodeElemGroupsInRegion()
10834 //================================================================================
10836 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10837 const TIDSortedElemSet& theNodesNot,
10838 const TopoDS_Shape& theShape,
10839 TIDSortedElemSet& theAffectedElems)
10841 if ( theShape.IsNull() )
10843 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10844 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10845 std::set<const SMDS_MeshElement*> edgesToCheck;
10846 alreadyCheckedNodes.clear();
10847 alreadyCheckedElems.clear();
10848 edgesToCheck.clear();
10850 // --- iterates on elements to be replicated and get elements by back references from their nodes
10852 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10853 for ( ; elemItr != theElems.end(); ++elemItr )
10855 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10856 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10859 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10860 std::set<const SMDS_MeshNode*> nodesElem;
10862 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10863 while ( nodeItr->more() )
10865 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10866 nodesElem.insert(aNode);
10868 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10869 for (; nodit != nodesElem.end(); nodit++)
10871 const SMDS_MeshNode* aNode = *nodit;
10872 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10874 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10876 alreadyCheckedNodes.insert(aNode);
10877 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10878 while ( backElemItr->more() )
10880 const SMDS_MeshElement* curElem = backElemItr->next();
10881 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10883 if (theElems.find(curElem) != theElems.end())
10885 alreadyCheckedElems.insert(curElem);
10886 double x=0, y=0, z=0;
10888 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10889 while ( nodeItr2->more() )
10891 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10892 x += anotherNode->X();
10893 y += anotherNode->Y();
10894 z += anotherNode->Z();
10898 p.SetCoord( x/nb -aNode->X(),
10900 z/nb -aNode->Z() );
10903 theAffectedElems.insert( curElem );
10905 else if (curElem->GetType() == SMDSAbs_Edge)
10906 edgesToCheck.insert(curElem);
10910 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10911 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10912 for( ; eit != edgesToCheck.end(); eit++)
10914 bool onside = true;
10915 const SMDS_MeshElement* anEdge = *eit;
10916 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10917 while ( nodeItr->more() )
10919 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10920 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10928 theAffectedElems.insert(anEdge);
10934 const double aTol = Precision::Confusion();
10935 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10936 auto_ptr<_FaceClassifier> aFaceClassifier;
10937 if ( theShape.ShapeType() == TopAbs_SOLID )
10939 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10940 bsc3d->PerformInfinitePoint(aTol);
10942 else if (theShape.ShapeType() == TopAbs_FACE )
10944 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10947 // iterates on indicated elements and get elements by back references from their nodes
10948 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10949 for ( ; elemItr != theElems.end(); ++elemItr )
10951 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10954 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10955 while ( nodeItr->more() )
10957 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10958 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10960 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10961 while ( backElemItr->more() )
10963 const SMDS_MeshElement* curElem = backElemItr->next();
10964 if ( curElem && theElems.find(curElem) == theElems.end() &&
10966 isInside( curElem, *bsc3d, aTol ) :
10967 isInside( curElem, *aFaceClassifier, aTol )))
10968 theAffectedElems.insert( curElem );
10976 //================================================================================
10978 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10979 \param theElems - group of of elements (edges or faces) to be replicated
10980 \param theNodesNot - group of nodes not to replicate
10981 \param theShape - shape to detect affected elements (element which geometric center
10982 located on or inside shape).
10983 The replicated nodes should be associated to affected elements.
10984 \return TRUE if operation has been completed successfully, FALSE otherwise
10986 //================================================================================
10988 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10989 const TIDSortedElemSet& theNodesNot,
10990 const TopoDS_Shape& theShape )
10992 if ( theShape.IsNull() )
10995 const double aTol = Precision::Confusion();
10996 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10997 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10998 if ( theShape.ShapeType() == TopAbs_SOLID )
11000 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11001 bsc3d->PerformInfinitePoint(aTol);
11003 else if (theShape.ShapeType() == TopAbs_FACE )
11005 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11008 // iterates on indicated elements and get elements by back references from their nodes
11009 TIDSortedElemSet anAffected;
11010 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11011 for ( ; elemItr != theElems.end(); ++elemItr )
11013 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11017 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11018 while ( nodeItr->more() )
11020 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11021 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11023 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11024 while ( backElemItr->more() )
11026 const SMDS_MeshElement* curElem = backElemItr->next();
11027 if ( curElem && theElems.find(curElem) == theElems.end() &&
11029 isInside( curElem, *bsc3d, aTol ) :
11030 isInside( curElem, *aFaceClassifier, aTol )))
11031 anAffected.insert( curElem );
11035 return DoubleNodes( theElems, theNodesNot, anAffected );
11039 * \brief compute an oriented angle between two planes defined by four points.
11040 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11041 * @param p0 base of the rotation axe
11042 * @param p1 extremity of the rotation axe
11043 * @param g1 belongs to the first plane
11044 * @param g2 belongs to the second plane
11046 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11048 gp_Vec vref(p0, p1);
11051 gp_Vec n1 = vref.Crossed(v1);
11052 gp_Vec n2 = vref.Crossed(v2);
11054 return n2.AngleWithRef(n1, vref);
11056 catch ( Standard_Failure ) {
11058 return Max( v1.Magnitude(), v2.Magnitude() );
11062 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11063 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11064 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11065 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11066 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11067 * 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.
11068 * 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.
11069 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11070 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11071 * \param theElems - list of groups of volumes, where a group of volume is a set of
11072 * SMDS_MeshElements sorted by Id.
11073 * \param createJointElems - if TRUE, create the elements
11074 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11075 * the boundary between \a theDomains and the rest mesh
11076 * \return TRUE if operation has been completed successfully, FALSE otherwise
11078 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11079 bool createJointElems,
11080 bool onAllBoundaries)
11082 MESSAGE("----------------------------------------------");
11083 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11084 MESSAGE("----------------------------------------------");
11086 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11087 meshDS->BuildDownWardConnectivity(true);
11089 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11091 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11092 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11093 // build the list of nodes shared by 2 or more domains, with their domain indexes
11095 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11096 std::map<int,int>celldom; // cell vtkId --> domain
11097 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11098 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11099 faceDomains.clear();
11101 cellDomains.clear();
11102 nodeDomains.clear();
11103 std::map<int,int> emptyMap;
11104 std::set<int> emptySet;
11107 MESSAGE(".. Number of domains :"<<theElems.size());
11109 TIDSortedElemSet theRestDomElems;
11110 const int iRestDom = -1;
11111 const int idom0 = onAllBoundaries ? iRestDom : 0;
11112 const int nbDomains = theElems.size();
11114 // Check if the domains do not share an element
11115 for (int idom = 0; idom < nbDomains-1; idom++)
11117 // MESSAGE("... Check of domain #" << idom);
11118 const TIDSortedElemSet& domain = theElems[idom];
11119 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11120 for (; elemItr != domain.end(); ++elemItr)
11122 const SMDS_MeshElement* anElem = *elemItr;
11123 int idombisdeb = idom + 1 ;
11124 // check if the element belongs to a domain further in the list
11125 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11127 const TIDSortedElemSet& domainbis = theElems[idombis];
11128 if ( domainbis.count( anElem ))
11130 MESSAGE(".... Domain #" << idom);
11131 MESSAGE(".... Domain #" << idombis);
11132 throw SALOME_Exception("The domains are not disjoint.");
11139 for (int idom = 0; idom < nbDomains; idom++)
11142 // --- build a map (face to duplicate --> volume to modify)
11143 // with all the faces shared by 2 domains (group of elements)
11144 // and corresponding volume of this domain, for each shared face.
11145 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11147 MESSAGE("... Neighbors of domain #" << idom);
11148 const TIDSortedElemSet& domain = theElems[idom];
11149 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11150 for (; elemItr != domain.end(); ++elemItr)
11152 const SMDS_MeshElement* anElem = *elemItr;
11155 int vtkId = anElem->getVtkId();
11156 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11157 int neighborsVtkIds[NBMAXNEIGHBORS];
11158 int downIds[NBMAXNEIGHBORS];
11159 unsigned char downTypes[NBMAXNEIGHBORS];
11160 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11161 for (int n = 0; n < nbNeighbors; n++)
11163 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11164 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11165 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11168 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11170 // MESSAGE("Domain " << idombis);
11171 const TIDSortedElemSet& domainbis = theElems[idombis];
11172 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11174 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11176 DownIdType face(downIds[n], downTypes[n]);
11177 if (!faceDomains[face].count(idom))
11179 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11180 celldom[vtkId] = idom;
11181 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11185 theRestDomElems.insert( elem );
11186 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11187 celldom[neighborsVtkIds[n]] = iRestDom;
11195 //MESSAGE("Number of shared faces " << faceDomains.size());
11196 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11198 // --- explore the shared faces domain by domain,
11199 // explore the nodes of the face and see if they belong to a cell in the domain,
11200 // which has only a node or an edge on the border (not a shared face)
11202 for (int idomain = idom0; idomain < nbDomains; idomain++)
11204 //MESSAGE("Domain " << idomain);
11205 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11206 itface = faceDomains.begin();
11207 for (; itface != faceDomains.end(); ++itface)
11209 const std::map<int, int>& domvol = itface->second;
11210 if (!domvol.count(idomain))
11212 DownIdType face = itface->first;
11213 //MESSAGE(" --- face " << face.cellId);
11214 std::set<int> oldNodes;
11216 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11217 std::set<int>::iterator itn = oldNodes.begin();
11218 for (; itn != oldNodes.end(); ++itn)
11221 //MESSAGE(" node " << oldId);
11222 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11223 for (int i=0; i<l.ncells; i++)
11225 int vtkId = l.cells[i];
11226 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11227 if (!domain.count(anElem))
11229 int vtkType = grid->GetCellType(vtkId);
11230 int downId = grid->CellIdToDownId(vtkId);
11233 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11234 continue; // not OK at this stage of the algorithm:
11235 //no cells created after BuildDownWardConnectivity
11237 DownIdType aCell(downId, vtkType);
11238 cellDomains[aCell][idomain] = vtkId;
11239 celldom[vtkId] = idomain;
11240 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11246 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11247 // for each shared face, get the nodes
11248 // for each node, for each domain of the face, create a clone of the node
11250 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11251 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11252 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11254 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11255 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11256 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11258 MESSAGE(".. Duplication of the nodes");
11259 for (int idomain = idom0; idomain < nbDomains; idomain++)
11261 itface = faceDomains.begin();
11262 for (; itface != faceDomains.end(); ++itface)
11264 const std::map<int, int>& domvol = itface->second;
11265 if (!domvol.count(idomain))
11267 DownIdType face = itface->first;
11268 //MESSAGE(" --- face " << face.cellId);
11269 std::set<int> oldNodes;
11271 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11272 std::set<int>::iterator itn = oldNodes.begin();
11273 for (; itn != oldNodes.end(); ++itn)
11276 if (nodeDomains[oldId].empty())
11278 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11279 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11281 std::map<int, int>::const_iterator itdom = domvol.begin();
11282 for (; itdom != domvol.end(); ++itdom)
11284 int idom = itdom->first;
11285 //MESSAGE(" domain " << idom);
11286 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11288 if (nodeDomains[oldId].size() >= 2) // a multiple node
11290 vector<int> orderedDoms;
11291 //MESSAGE("multiple node " << oldId);
11292 if (mutipleNodes.count(oldId))
11293 orderedDoms = mutipleNodes[oldId];
11296 map<int,int>::iterator it = nodeDomains[oldId].begin();
11297 for (; it != nodeDomains[oldId].end(); ++it)
11298 orderedDoms.push_back(it->first);
11300 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11301 //stringstream txt;
11302 //for (int i=0; i<orderedDoms.size(); i++)
11303 // txt << orderedDoms[i] << " ";
11304 //MESSAGE("orderedDoms " << txt.str());
11305 mutipleNodes[oldId] = orderedDoms;
11307 double *coords = grid->GetPoint(oldId);
11308 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11309 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11310 int newId = newNode->getVtkId();
11311 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11312 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11319 MESSAGE(".. Creation of elements");
11320 for (int idomain = idom0; idomain < nbDomains; idomain++)
11322 itface = faceDomains.begin();
11323 for (; itface != faceDomains.end(); ++itface)
11325 std::map<int, int> domvol = itface->second;
11326 if (!domvol.count(idomain))
11328 DownIdType face = itface->first;
11329 //MESSAGE(" --- face " << face.cellId);
11330 std::set<int> oldNodes;
11332 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11333 int nbMultipleNodes = 0;
11334 std::set<int>::iterator itn = oldNodes.begin();
11335 for (; itn != oldNodes.end(); ++itn)
11338 if (mutipleNodes.count(oldId))
11341 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11343 //MESSAGE("multiple Nodes detected on a shared face");
11344 int downId = itface->first.cellId;
11345 unsigned char cellType = itface->first.cellType;
11346 // --- shared edge or shared face ?
11347 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11350 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11351 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11352 if (mutipleNodes.count(nodes[i]))
11353 if (!mutipleNodesToFace.count(nodes[i]))
11354 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11356 else // shared face (between two volumes)
11358 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11359 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11360 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11361 for (int ie =0; ie < nbEdges; ie++)
11364 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11365 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11367 vector<int> vn0 = mutipleNodes[nodes[0]];
11368 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11370 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11371 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11372 if ( vn0[i0] == vn1[i1] )
11373 doms.push_back( vn0[ i0 ]);
11374 if ( doms.size() > 2 )
11376 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11377 double *coords = grid->GetPoint(nodes[0]);
11378 gp_Pnt p0(coords[0], coords[1], coords[2]);
11379 coords = grid->GetPoint(nodes[nbNodes - 1]);
11380 gp_Pnt p1(coords[0], coords[1], coords[2]);
11382 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11383 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11384 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11385 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11386 for ( size_t id = 0; id < doms.size(); id++ )
11388 int idom = doms[id];
11389 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11390 for ( int ivol = 0; ivol < nbvol; ivol++ )
11392 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11393 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11394 if (domain.count(elem))
11396 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11397 domvol[idom] = svol;
11398 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11400 vtkIdType npts = 0;
11401 vtkIdType* pts = 0;
11402 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11403 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11406 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11407 angleDom[idom] = 0;
11411 gp_Pnt g(values[0], values[1], values[2]);
11412 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11413 //MESSAGE(" angle=" << angleDom[idom]);
11419 map<double, int> sortedDom; // sort domains by angle
11420 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11421 sortedDom[ia->second] = ia->first;
11422 vector<int> vnodes;
11424 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11426 vdom.push_back(ib->second);
11427 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11429 for (int ino = 0; ino < nbNodes; ino++)
11430 vnodes.push_back(nodes[ino]);
11431 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11440 // --- iterate on shared faces (volumes to modify, face to extrude)
11441 // get node id's of the face (id SMDS = id VTK)
11442 // create flat element with old and new nodes if requested
11444 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11445 // (domain1 X domain2) = domain1 + MAXINT*domain2
11447 std::map<int, std::map<long,int> > nodeQuadDomains;
11448 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11450 MESSAGE(".. Creation of elements: simple junction");
11451 if (createJointElems)
11454 string joints2DName = "joints2D";
11455 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11456 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11457 string joints3DName = "joints3D";
11458 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11459 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11461 itface = faceDomains.begin();
11462 for (; itface != faceDomains.end(); ++itface)
11464 DownIdType face = itface->first;
11465 std::set<int> oldNodes;
11466 std::set<int>::iterator itn;
11468 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11470 std::map<int, int> domvol = itface->second;
11471 std::map<int, int>::iterator itdom = domvol.begin();
11472 int dom1 = itdom->first;
11473 int vtkVolId = itdom->second;
11475 int dom2 = itdom->first;
11476 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11478 stringstream grpname;
11481 grpname << dom1 << "_" << dom2;
11483 grpname << dom2 << "_" << dom1;
11484 string namegrp = grpname.str();
11485 if (!mapOfJunctionGroups.count(namegrp))
11486 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11487 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11489 sgrp->Add(vol->GetID());
11490 if (vol->GetType() == SMDSAbs_Volume)
11491 joints3DGrp->Add(vol->GetID());
11492 else if (vol->GetType() == SMDSAbs_Face)
11493 joints2DGrp->Add(vol->GetID());
11497 // --- create volumes on multiple domain intersection if requested
11498 // iterate on mutipleNodesToFace
11499 // iterate on edgesMultiDomains
11501 MESSAGE(".. Creation of elements: multiple junction");
11502 if (createJointElems)
11504 // --- iterate on mutipleNodesToFace
11506 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11507 for (; itn != mutipleNodesToFace.end(); ++itn)
11509 int node = itn->first;
11510 vector<int> orderDom = itn->second;
11511 vector<vtkIdType> orderedNodes;
11512 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11513 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11514 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11516 stringstream grpname;
11518 grpname << 0 << "_" << 0;
11520 string namegrp = grpname.str();
11521 if (!mapOfJunctionGroups.count(namegrp))
11522 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11523 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11525 sgrp->Add(face->GetID());
11528 // --- iterate on edgesMultiDomains
11530 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11531 for (; ite != edgesMultiDomains.end(); ++ite)
11533 vector<int> nodes = ite->first;
11534 vector<int> orderDom = ite->second;
11535 vector<vtkIdType> orderedNodes;
11536 if (nodes.size() == 2)
11538 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11539 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11540 if ( orderDom.size() == 3 )
11541 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11542 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11544 for (int idom = orderDom.size()-1; idom >=0; idom--)
11545 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11546 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11549 string namegrp = "jointsMultiples";
11550 if (!mapOfJunctionGroups.count(namegrp))
11551 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11552 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11554 sgrp->Add(vol->GetID());
11558 //INFOS("Quadratic multiple joints not implemented");
11559 // TODO quadratic nodes
11564 // --- list the explicit faces and edges of the mesh that need to be modified,
11565 // i.e. faces and edges built with one or more duplicated nodes.
11566 // associate these faces or edges to their corresponding domain.
11567 // only the first domain found is kept when a face or edge is shared
11569 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11570 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11571 faceOrEdgeDom.clear();
11574 MESSAGE(".. Modification of elements");
11575 for (int idomain = idom0; idomain < nbDomains; idomain++)
11577 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11578 for (; itnod != nodeDomains.end(); ++itnod)
11580 int oldId = itnod->first;
11581 //MESSAGE(" node " << oldId);
11582 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11583 for (int i = 0; i < l.ncells; i++)
11585 int vtkId = l.cells[i];
11586 int vtkType = grid->GetCellType(vtkId);
11587 int downId = grid->CellIdToDownId(vtkId);
11589 continue; // new cells: not to be modified
11590 DownIdType aCell(downId, vtkType);
11591 int volParents[1000];
11592 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11593 for (int j = 0; j < nbvol; j++)
11594 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11595 if (!feDom.count(vtkId))
11597 feDom[vtkId] = idomain;
11598 faceOrEdgeDom[aCell] = emptyMap;
11599 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11600 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11601 // << " type " << vtkType << " downId " << downId);
11607 // --- iterate on shared faces (volumes to modify, face to extrude)
11608 // get node id's of the face
11609 // replace old nodes by new nodes in volumes, and update inverse connectivity
11611 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11612 for (int m=0; m<3; m++)
11614 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11615 itface = (*amap).begin();
11616 for (; itface != (*amap).end(); ++itface)
11618 DownIdType face = itface->first;
11619 std::set<int> oldNodes;
11620 std::set<int>::iterator itn;
11622 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11623 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11624 std::map<int, int> localClonedNodeIds;
11626 std::map<int, int> domvol = itface->second;
11627 std::map<int, int>::iterator itdom = domvol.begin();
11628 for (; itdom != domvol.end(); ++itdom)
11630 int idom = itdom->first;
11631 int vtkVolId = itdom->second;
11632 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11633 localClonedNodeIds.clear();
11634 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11637 if (nodeDomains[oldId].count(idom))
11639 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11640 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11643 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11648 // Remove empty groups (issue 0022812)
11649 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11650 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11652 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11653 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11656 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11657 grid->BuildLinks();
11665 * \brief Double nodes on some external faces and create flat elements.
11666 * Flat elements are mainly used by some types of mechanic calculations.
11668 * Each group of the list must be constituted of faces.
11669 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11670 * @param theElems - list of groups of faces, where a group of faces is a set of
11671 * SMDS_MeshElements sorted by Id.
11672 * @return TRUE if operation has been completed successfully, FALSE otherwise
11674 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11676 MESSAGE("-------------------------------------------------");
11677 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11678 MESSAGE("-------------------------------------------------");
11680 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11682 // --- For each group of faces
11683 // duplicate the nodes, create a flat element based on the face
11684 // replace the nodes of the faces by their clones
11686 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11687 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11688 clonedNodes.clear();
11689 intermediateNodes.clear();
11690 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11691 mapOfJunctionGroups.clear();
11693 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11695 const TIDSortedElemSet& domain = theElems[idom];
11696 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11697 for ( ; elemItr != domain.end(); ++elemItr )
11699 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11700 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11703 // MESSAGE("aFace=" << aFace->GetID());
11704 bool isQuad = aFace->IsQuadratic();
11705 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11707 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11709 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11710 while (nodeIt->more())
11712 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11713 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11715 ln2.push_back(node);
11717 ln0.push_back(node);
11719 const SMDS_MeshNode* clone = 0;
11720 if (!clonedNodes.count(node))
11722 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11723 copyPosition( node, clone );
11724 clonedNodes[node] = clone;
11727 clone = clonedNodes[node];
11730 ln3.push_back(clone);
11732 ln1.push_back(clone);
11734 const SMDS_MeshNode* inter = 0;
11735 if (isQuad && (!isMedium))
11737 if (!intermediateNodes.count(node))
11739 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11740 copyPosition( node, inter );
11741 intermediateNodes[node] = inter;
11744 inter = intermediateNodes[node];
11745 ln4.push_back(inter);
11749 // --- extrude the face
11751 vector<const SMDS_MeshNode*> ln;
11752 SMDS_MeshVolume* vol = 0;
11753 vtkIdType aType = aFace->GetVtkType();
11757 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11758 // MESSAGE("vol prism " << vol->GetID());
11759 ln.push_back(ln1[0]);
11760 ln.push_back(ln1[1]);
11761 ln.push_back(ln1[2]);
11764 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11765 // MESSAGE("vol hexa " << vol->GetID());
11766 ln.push_back(ln1[0]);
11767 ln.push_back(ln1[1]);
11768 ln.push_back(ln1[2]);
11769 ln.push_back(ln1[3]);
11771 case VTK_QUADRATIC_TRIANGLE:
11772 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11773 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11774 // MESSAGE("vol quad prism " << vol->GetID());
11775 ln.push_back(ln1[0]);
11776 ln.push_back(ln1[1]);
11777 ln.push_back(ln1[2]);
11778 ln.push_back(ln3[0]);
11779 ln.push_back(ln3[1]);
11780 ln.push_back(ln3[2]);
11782 case VTK_QUADRATIC_QUAD:
11783 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11784 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11785 // ln4[0], ln4[1], ln4[2], ln4[3]);
11786 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11787 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11788 ln4[0], ln4[1], ln4[2], ln4[3]);
11789 // MESSAGE("vol quad hexa " << vol->GetID());
11790 ln.push_back(ln1[0]);
11791 ln.push_back(ln1[1]);
11792 ln.push_back(ln1[2]);
11793 ln.push_back(ln1[3]);
11794 ln.push_back(ln3[0]);
11795 ln.push_back(ln3[1]);
11796 ln.push_back(ln3[2]);
11797 ln.push_back(ln3[3]);
11807 stringstream grpname;
11811 string namegrp = grpname.str();
11812 if (!mapOfJunctionGroups.count(namegrp))
11813 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11814 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11816 sgrp->Add(vol->GetID());
11819 // --- modify the face
11821 aFace->ChangeNodes(&ln[0], ln.size());
11828 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11829 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11830 * groups of faces to remove inside the object, (idem edges).
11831 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11833 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11834 const TopoDS_Shape& theShape,
11835 SMESH_NodeSearcher* theNodeSearcher,
11836 const char* groupName,
11837 std::vector<double>& nodesCoords,
11838 std::vector<std::vector<int> >& listOfListOfNodes)
11840 MESSAGE("--------------------------------");
11841 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11842 MESSAGE("--------------------------------");
11844 // --- zone of volumes to remove is given :
11845 // 1 either by a geom shape (one or more vertices) and a radius,
11846 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11847 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11848 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11849 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11850 // defined by it's name.
11852 SMESHDS_GroupBase* groupDS = 0;
11853 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11854 while ( groupIt->more() )
11857 SMESH_Group * group = groupIt->next();
11858 if ( !group ) continue;
11859 groupDS = group->GetGroupDS();
11860 if ( !groupDS || groupDS->IsEmpty() ) continue;
11861 std::string grpName = group->GetName();
11862 //MESSAGE("grpName=" << grpName);
11863 if (grpName == groupName)
11869 bool isNodeGroup = false;
11870 bool isNodeCoords = false;
11873 if (groupDS->GetType() != SMDSAbs_Node)
11875 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11878 if (nodesCoords.size() > 0)
11879 isNodeCoords = true; // a list o nodes given by their coordinates
11880 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11882 // --- define groups to build
11884 int idg; // --- group of SMDS volumes
11885 string grpvName = groupName;
11886 grpvName += "_vol";
11887 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11890 MESSAGE("group not created " << grpvName);
11893 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11895 int idgs; // --- group of SMDS faces on the skin
11896 string grpsName = groupName;
11897 grpsName += "_skin";
11898 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11901 MESSAGE("group not created " << grpsName);
11904 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11906 int idgi; // --- group of SMDS faces internal (several shapes)
11907 string grpiName = groupName;
11908 grpiName += "_internalFaces";
11909 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11912 MESSAGE("group not created " << grpiName);
11915 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11917 int idgei; // --- group of SMDS faces internal (several shapes)
11918 string grpeiName = groupName;
11919 grpeiName += "_internalEdges";
11920 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11923 MESSAGE("group not created " << grpeiName);
11926 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11928 // --- build downward connectivity
11930 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11931 meshDS->BuildDownWardConnectivity(true);
11932 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11934 // --- set of volumes detected inside
11936 std::set<int> setOfInsideVol;
11937 std::set<int> setOfVolToCheck;
11939 std::vector<gp_Pnt> gpnts;
11942 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11944 MESSAGE("group of nodes provided");
11945 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11946 while ( elemIt->more() )
11948 const SMDS_MeshElement* elem = elemIt->next();
11951 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11954 SMDS_MeshElement* vol = 0;
11955 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11956 while (volItr->more())
11958 vol = (SMDS_MeshElement*)volItr->next();
11959 setOfInsideVol.insert(vol->getVtkId());
11960 sgrp->Add(vol->GetID());
11964 else if (isNodeCoords)
11966 MESSAGE("list of nodes coordinates provided");
11969 while ( i < nodesCoords.size()-2 )
11971 double x = nodesCoords[i++];
11972 double y = nodesCoords[i++];
11973 double z = nodesCoords[i++];
11974 gp_Pnt p = gp_Pnt(x, y ,z);
11975 gpnts.push_back(p);
11976 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11980 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11982 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11983 TopTools_IndexedMapOfShape vertexMap;
11984 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11985 gp_Pnt p = gp_Pnt(0,0,0);
11986 if (vertexMap.Extent() < 1)
11989 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11991 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11992 p = BRep_Tool::Pnt(vertex);
11993 gpnts.push_back(p);
11994 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11998 if (gpnts.size() > 0)
12001 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12003 nodeId = startNode->GetID();
12004 MESSAGE("nodeId " << nodeId);
12006 double radius2 = radius*radius;
12007 MESSAGE("radius2 " << radius2);
12009 // --- volumes on start node
12011 setOfVolToCheck.clear();
12012 SMDS_MeshElement* startVol = 0;
12013 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12014 while (volItr->more())
12016 startVol = (SMDS_MeshElement*)volItr->next();
12017 setOfVolToCheck.insert(startVol->getVtkId());
12019 if (setOfVolToCheck.empty())
12021 MESSAGE("No volumes found");
12025 // --- starting with central volumes then their neighbors, check if they are inside
12026 // or outside the domain, until no more new neighbor volume is inside.
12027 // Fill the group of inside volumes
12029 std::map<int, double> mapOfNodeDistance2;
12030 mapOfNodeDistance2.clear();
12031 std::set<int> setOfOutsideVol;
12032 while (!setOfVolToCheck.empty())
12034 std::set<int>::iterator it = setOfVolToCheck.begin();
12036 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12037 bool volInside = false;
12038 vtkIdType npts = 0;
12039 vtkIdType* pts = 0;
12040 grid->GetCellPoints(vtkId, npts, pts);
12041 for (int i=0; i<npts; i++)
12043 double distance2 = 0;
12044 if (mapOfNodeDistance2.count(pts[i]))
12046 distance2 = mapOfNodeDistance2[pts[i]];
12047 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12051 double *coords = grid->GetPoint(pts[i]);
12052 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12054 for ( size_t j = 0; j < gpnts.size(); j++ )
12056 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12057 if (d2 < distance2)
12060 if (distance2 < radius2)
12064 mapOfNodeDistance2[pts[i]] = distance2;
12065 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12067 if (distance2 < radius2)
12069 volInside = true; // one or more nodes inside the domain
12070 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12076 setOfInsideVol.insert(vtkId);
12077 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12078 int neighborsVtkIds[NBMAXNEIGHBORS];
12079 int downIds[NBMAXNEIGHBORS];
12080 unsigned char downTypes[NBMAXNEIGHBORS];
12081 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12082 for (int n = 0; n < nbNeighbors; n++)
12083 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12084 setOfVolToCheck.insert(neighborsVtkIds[n]);
12088 setOfOutsideVol.insert(vtkId);
12089 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12091 setOfVolToCheck.erase(vtkId);
12095 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12096 // If yes, add the volume to the inside set
12098 bool addedInside = true;
12099 std::set<int> setOfVolToReCheck;
12100 while (addedInside)
12102 MESSAGE(" --------------------------- re check");
12103 addedInside = false;
12104 std::set<int>::iterator itv = setOfInsideVol.begin();
12105 for (; itv != setOfInsideVol.end(); ++itv)
12108 int neighborsVtkIds[NBMAXNEIGHBORS];
12109 int downIds[NBMAXNEIGHBORS];
12110 unsigned char downTypes[NBMAXNEIGHBORS];
12111 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12112 for (int n = 0; n < nbNeighbors; n++)
12113 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12114 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12116 setOfVolToCheck = setOfVolToReCheck;
12117 setOfVolToReCheck.clear();
12118 while (!setOfVolToCheck.empty())
12120 std::set<int>::iterator it = setOfVolToCheck.begin();
12122 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12124 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12125 int countInside = 0;
12126 int neighborsVtkIds[NBMAXNEIGHBORS];
12127 int downIds[NBMAXNEIGHBORS];
12128 unsigned char downTypes[NBMAXNEIGHBORS];
12129 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12130 for (int n = 0; n < nbNeighbors; n++)
12131 if (setOfInsideVol.count(neighborsVtkIds[n]))
12133 MESSAGE("countInside " << countInside);
12134 if (countInside > 1)
12136 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12137 setOfInsideVol.insert(vtkId);
12138 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12139 addedInside = true;
12142 setOfVolToReCheck.insert(vtkId);
12144 setOfVolToCheck.erase(vtkId);
12148 // --- map of Downward faces at the boundary, inside the global volume
12149 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12150 // fill group of SMDS faces inside the volume (when several volume shapes)
12151 // fill group of SMDS faces on the skin of the global volume (if skin)
12153 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12154 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12155 std::set<int>::iterator it = setOfInsideVol.begin();
12156 for (; it != setOfInsideVol.end(); ++it)
12159 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12160 int neighborsVtkIds[NBMAXNEIGHBORS];
12161 int downIds[NBMAXNEIGHBORS];
12162 unsigned char downTypes[NBMAXNEIGHBORS];
12163 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12164 for (int n = 0; n < nbNeighbors; n++)
12166 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12167 if (neighborDim == 3)
12169 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12171 DownIdType face(downIds[n], downTypes[n]);
12172 boundaryFaces[face] = vtkId;
12174 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12175 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12176 if (vtkFaceId >= 0)
12178 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12179 // find also the smds edges on this face
12180 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12181 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12182 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12183 for (int i = 0; i < nbEdges; i++)
12185 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12186 if (vtkEdgeId >= 0)
12187 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12191 else if (neighborDim == 2) // skin of the volume
12193 DownIdType face(downIds[n], downTypes[n]);
12194 skinFaces[face] = vtkId;
12195 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12196 if (vtkFaceId >= 0)
12197 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12202 // --- identify the edges constituting the wire of each subshape on the skin
12203 // define polylines with the nodes of edges, equivalent to wires
12204 // project polylines on subshapes, and partition, to get geom faces
12206 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12207 std::set<int> emptySet;
12209 std::set<int> shapeIds;
12211 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12212 while (itelem->more())
12214 const SMDS_MeshElement *elem = itelem->next();
12215 int shapeId = elem->getshapeId();
12216 int vtkId = elem->getVtkId();
12217 if (!shapeIdToVtkIdSet.count(shapeId))
12219 shapeIdToVtkIdSet[shapeId] = emptySet;
12220 shapeIds.insert(shapeId);
12222 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12225 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12226 std::set<DownIdType, DownIdCompare> emptyEdges;
12227 emptyEdges.clear();
12229 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12230 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12232 int shapeId = itShape->first;
12233 MESSAGE(" --- Shape ID --- "<< shapeId);
12234 shapeIdToEdges[shapeId] = emptyEdges;
12236 std::vector<int> nodesEdges;
12238 std::set<int>::iterator its = itShape->second.begin();
12239 for (; its != itShape->second.end(); ++its)
12242 MESSAGE(" " << vtkId);
12243 int neighborsVtkIds[NBMAXNEIGHBORS];
12244 int downIds[NBMAXNEIGHBORS];
12245 unsigned char downTypes[NBMAXNEIGHBORS];
12246 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12247 for (int n = 0; n < nbNeighbors; n++)
12249 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12251 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12252 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12253 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12255 DownIdType edge(downIds[n], downTypes[n]);
12256 if (!shapeIdToEdges[shapeId].count(edge))
12258 shapeIdToEdges[shapeId].insert(edge);
12260 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12261 nodesEdges.push_back(vtkNodeId[0]);
12262 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12263 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12269 std::list<int> order;
12271 if (nodesEdges.size() > 0)
12273 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12274 nodesEdges[0] = -1;
12275 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12276 nodesEdges[1] = -1; // do not reuse this edge
12280 int nodeTofind = order.back(); // try first to push back
12282 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12283 if (nodesEdges[i] == nodeTofind)
12285 if ( i == (int) nodesEdges.size() )
12286 found = false; // no follower found on back
12289 if (i%2) // odd ==> use the previous one
12290 if (nodesEdges[i-1] < 0)
12294 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12295 nodesEdges[i-1] = -1;
12297 else // even ==> use the next one
12298 if (nodesEdges[i+1] < 0)
12302 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12303 nodesEdges[i+1] = -1;
12308 // try to push front
12310 nodeTofind = order.front(); // try to push front
12311 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12312 if ( nodesEdges[i] == nodeTofind )
12314 if ( i == (int)nodesEdges.size() )
12316 found = false; // no predecessor found on front
12319 if (i%2) // odd ==> use the previous one
12320 if (nodesEdges[i-1] < 0)
12324 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12325 nodesEdges[i-1] = -1;
12327 else // even ==> use the next one
12328 if (nodesEdges[i+1] < 0)
12332 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12333 nodesEdges[i+1] = -1;
12339 std::vector<int> nodes;
12340 nodes.push_back(shapeId);
12341 std::list<int>::iterator itl = order.begin();
12342 for (; itl != order.end(); itl++)
12344 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12345 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12347 listOfListOfNodes.push_back(nodes);
12350 // partition geom faces with blocFissure
12351 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12352 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12358 //================================================================================
12360 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12361 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12362 * \return TRUE if operation has been completed successfully, FALSE otherwise
12364 //================================================================================
12366 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12368 // iterates on volume elements and detect all free faces on them
12369 SMESHDS_Mesh* aMesh = GetMeshDS();
12373 ElemFeatures faceType( SMDSAbs_Face );
12374 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12375 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12378 const SMDS_MeshVolume* volume = vIt->next();
12379 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12380 vTool.SetExternalNormal();
12381 const int iQuad = volume->IsQuadratic();
12382 faceType.SetQuad( iQuad );
12383 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12385 if (!vTool.IsFreeFace(iface))
12388 vector<const SMDS_MeshNode *> nodes;
12389 int nbFaceNodes = vTool.NbFaceNodes(iface);
12390 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12392 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12393 nodes.push_back(faceNodes[inode]);
12395 if (iQuad) // add medium nodes
12397 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12398 nodes.push_back(faceNodes[inode]);
12399 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12400 nodes.push_back(faceNodes[8]);
12402 // add new face based on volume nodes
12403 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12405 nbExisted++; // face already exsist
12409 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12414 return ( nbFree == ( nbExisted + nbCreated ));
12419 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12421 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12423 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12426 //================================================================================
12428 * \brief Creates missing boundary elements
12429 * \param elements - elements whose boundary is to be checked
12430 * \param dimension - defines type of boundary elements to create
12431 * \param group - a group to store created boundary elements in
12432 * \param targetMesh - a mesh to store created boundary elements in
12433 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12434 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12435 * boundary elements will be copied into the targetMesh
12436 * \param toAddExistingBondary - if true, not only new but also pre-existing
12437 * boundary elements will be added into the new group
12438 * \param aroundElements - if true, elements will be created on boundary of given
12439 * elements else, on boundary of the whole mesh.
12440 * \return nb of added boundary elements
12442 //================================================================================
12444 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12445 Bnd_Dimension dimension,
12446 SMESH_Group* group/*=0*/,
12447 SMESH_Mesh* targetMesh/*=0*/,
12448 bool toCopyElements/*=false*/,
12449 bool toCopyExistingBoundary/*=false*/,
12450 bool toAddExistingBondary/*= false*/,
12451 bool aroundElements/*= false*/)
12453 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12454 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12455 // hope that all elements are of the same type, do not check them all
12456 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12457 throw SALOME_Exception(LOCALIZED("wrong element type"));
12460 toCopyElements = toCopyExistingBoundary = false;
12462 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12463 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12464 int nbAddedBnd = 0;
12466 // editor adding present bnd elements and optionally holding elements to add to the group
12467 SMESH_MeshEditor* presentEditor;
12468 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12469 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12471 SMESH_MesherHelper helper( *myMesh );
12472 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12473 SMDS_VolumeTool vTool;
12474 TIDSortedElemSet avoidSet;
12475 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12478 typedef vector<const SMDS_MeshNode*> TConnectivity;
12479 TConnectivity tgtNodes;
12480 ElemFeatures elemKind( missType ), elemToCopy;
12482 vector<const SMDS_MeshElement*> presentBndElems;
12483 vector<TConnectivity> missingBndElems;
12484 vector<int> freeFacets;
12485 TConnectivity nodes, elemNodes;
12487 SMDS_ElemIteratorPtr eIt;
12488 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12489 else eIt = elemSetIterator( elements );
12491 while (eIt->more())
12493 const SMDS_MeshElement* elem = eIt->next();
12494 const int iQuad = elem->IsQuadratic();
12495 elemKind.SetQuad( iQuad );
12497 // ------------------------------------------------------------------------------------
12498 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12499 // ------------------------------------------------------------------------------------
12500 presentBndElems.clear();
12501 missingBndElems.clear();
12502 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12503 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12505 const SMDS_MeshElement* otherVol = 0;
12506 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12508 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12509 ( !aroundElements || elements.count( otherVol )))
12511 freeFacets.push_back( iface );
12513 if ( missType == SMDSAbs_Face )
12514 vTool.SetExternalNormal();
12515 for ( size_t i = 0; i < freeFacets.size(); ++i )
12517 int iface = freeFacets[i];
12518 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12519 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12520 if ( missType == SMDSAbs_Edge ) // boundary edges
12522 nodes.resize( 2+iQuad );
12523 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12525 for ( size_t j = 0; j < nodes.size(); ++j )
12526 nodes[ j ] = nn[ i+j ];
12527 if ( const SMDS_MeshElement* edge =
12528 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12529 presentBndElems.push_back( edge );
12531 missingBndElems.push_back( nodes );
12534 else // boundary face
12537 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12538 nodes.push_back( nn[inode] ); // add corner nodes
12540 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12541 nodes.push_back( nn[inode] ); // add medium nodes
12542 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12544 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12546 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12547 SMDSAbs_Face, /*noMedium=*/false ))
12548 presentBndElems.push_back( f );
12550 missingBndElems.push_back( nodes );
12552 if ( targetMesh != myMesh )
12554 // add 1D elements on face boundary to be added to a new mesh
12555 const SMDS_MeshElement* edge;
12556 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12559 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12561 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12562 if ( edge && avoidSet.insert( edge ).second )
12563 presentBndElems.push_back( edge );
12569 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12571 avoidSet.clear(), avoidSet.insert( elem );
12572 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12573 SMDS_MeshElement::iterator() );
12574 elemNodes.push_back( elemNodes[0] );
12575 nodes.resize( 2 + iQuad );
12576 const int nbLinks = elem->NbCornerNodes();
12577 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12579 nodes[0] = elemNodes[iN];
12580 nodes[1] = elemNodes[iN+1+iQuad];
12581 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12582 continue; // not free link
12584 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12585 if ( const SMDS_MeshElement* edge =
12586 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12587 presentBndElems.push_back( edge );
12589 missingBndElems.push_back( nodes );
12593 // ---------------------------------
12594 // 2. Add missing boundary elements
12595 // ---------------------------------
12596 if ( targetMesh != myMesh )
12597 // instead of making a map of nodes in this mesh and targetMesh,
12598 // we create nodes with same IDs.
12599 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12601 TConnectivity& srcNodes = missingBndElems[i];
12602 tgtNodes.resize( srcNodes.size() );
12603 for ( inode = 0; inode < srcNodes.size(); ++inode )
12604 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12605 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12607 /*noMedium=*/false))
12609 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12613 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12615 TConnectivity& nodes = missingBndElems[ i ];
12616 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12618 /*noMedium=*/false))
12620 SMDS_MeshElement* newElem =
12621 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12622 nbAddedBnd += bool( newElem );
12624 // try to set a new element to a shape
12625 if ( myMesh->HasShapeToMesh() )
12628 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12629 const size_t nbN = nodes.size() / (iQuad+1 );
12630 for ( inode = 0; inode < nbN && ok; ++inode )
12632 pair<int, TopAbs_ShapeEnum> i_stype =
12633 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12634 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12635 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12637 if ( ok && mediumShapes.size() > 1 )
12639 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12640 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12641 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12643 if (( ok = ( stype_i->first != stype_i_0.first )))
12644 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12645 aMesh->IndexToShape( stype_i_0.second ));
12648 if ( ok && mediumShapes.begin()->first == missShapeType )
12649 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12653 // ----------------------------------
12654 // 3. Copy present boundary elements
12655 // ----------------------------------
12656 if ( toCopyExistingBoundary )
12657 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12659 const SMDS_MeshElement* e = presentBndElems[i];
12660 tgtNodes.resize( e->NbNodes() );
12661 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12662 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12663 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12665 else // store present elements to add them to a group
12666 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12668 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12671 } // loop on given elements
12673 // ---------------------------------------------
12674 // 4. Fill group with boundary elements
12675 // ---------------------------------------------
12678 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12679 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12680 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12682 tgtEditor.myLastCreatedElems.Clear();
12683 tgtEditor2.myLastCreatedElems.Clear();
12685 // -----------------------
12686 // 5. Copy given elements
12687 // -----------------------
12688 if ( toCopyElements && targetMesh != myMesh )
12690 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12691 else eIt = elemSetIterator( elements );
12692 while (eIt->more())
12694 const SMDS_MeshElement* elem = eIt->next();
12695 tgtNodes.resize( elem->NbNodes() );
12696 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12697 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12698 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12700 tgtEditor.myLastCreatedElems.Clear();
12706 //================================================================================
12708 * \brief Copy node position and set \a to node on the same geometry
12710 //================================================================================
12712 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12713 const SMDS_MeshNode* to )
12715 if ( !from || !to ) return;
12717 SMDS_PositionPtr pos = from->GetPosition();
12718 if ( !pos || from->getshapeId() < 1 ) return;
12720 switch ( pos->GetTypeOfPosition() )
12722 case SMDS_TOP_3DSPACE: break;
12724 case SMDS_TOP_FACE:
12726 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12727 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12728 fPos->GetUParameter(), fPos->GetVParameter() );
12731 case SMDS_TOP_EDGE:
12733 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12734 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12735 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12738 case SMDS_TOP_VERTEX:
12740 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12743 case SMDS_TOP_UNSPEC: