1 // Copyright (C) 2007-2014 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::CrearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
141 //=======================================================================
145 //=======================================================================
148 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
149 const SMDSAbs_ElementType type,
152 const double ballDiameter)
154 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
155 SMDS_MeshElement* e = 0;
156 int nbnode = node.size();
157 SMESHDS_Mesh* mesh = GetMeshDS();
162 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
163 else e = mesh->AddFace (node[0], node[1], node[2] );
165 else if (nbnode == 4) {
166 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
167 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
169 else if (nbnode == 6) {
170 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171 node[4], node[5], ID);
172 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
175 else if (nbnode == 7) {
176 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177 node[4], node[5], node[6], ID);
178 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
179 node[4], node[5], node[6] );
181 else if (nbnode == 8) {
182 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
183 node[4], node[5], node[6], node[7], ID);
184 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
185 node[4], node[5], node[6], node[7] );
187 else if (nbnode == 9) {
188 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
189 node[4], node[5], node[6], node[7], node[8], ID);
190 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
191 node[4], node[5], node[6], node[7], node[8] );
194 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
195 else e = mesh->AddPolygonalFace (node );
202 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
203 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
205 else if (nbnode == 5) {
206 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
211 else if (nbnode == 6) {
212 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213 node[4], node[5], ID);
214 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
217 else if (nbnode == 8) {
218 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7], ID);
220 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7] );
223 else if (nbnode == 10) {
224 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
226 node[8], node[9], ID);
227 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
228 node[4], node[5], node[6], node[7],
231 else if (nbnode == 12) {
232 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7],
234 node[8], node[9], node[10], node[11], ID);
235 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7],
237 node[8], node[9], node[10], node[11] );
239 else if (nbnode == 13) {
240 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241 node[4], node[5], node[6], node[7],
242 node[8], node[9], node[10],node[11],
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
245 node[4], node[5], node[6], node[7],
246 node[8], node[9], node[10],node[11],
249 else if (nbnode == 15) {
250 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251 node[4], node[5], node[6], node[7],
252 node[8], node[9], node[10],node[11],
253 node[12],node[13],node[14],ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
255 node[4], node[5], node[6], node[7],
256 node[8], node[9], node[10],node[11],
257 node[12],node[13],node[14] );
259 else if (nbnode == 20) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7],
262 node[8], node[9], node[10],node[11],
263 node[12],node[13],node[14],node[15],
264 node[16],node[17],node[18],node[19],ID);
265 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7],
267 node[8], node[9], node[10],node[11],
268 node[12],node[13],node[14],node[15],
269 node[16],node[17],node[18],node[19] );
271 else if (nbnode == 27) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
274 node[8], node[9], node[10],node[11],
275 node[12],node[13],node[14],node[15],
276 node[16],node[17],node[18],node[19],
277 node[20],node[21],node[22],node[23],
278 node[24],node[25],node[26], ID);
279 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
282 node[12],node[13],node[14],node[15],
283 node[16],node[17],node[18],node[19],
284 node[20],node[21],node[22],node[23],
285 node[24],node[25],node[26] );
292 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
293 else e = mesh->AddEdge (node[0], node[1] );
295 else if ( nbnode == 3 ) {
296 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
297 else e = mesh->AddEdge (node[0], node[1], node[2] );
301 case SMDSAbs_0DElement:
303 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
304 else e = mesh->Add0DElement (node[0] );
309 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
310 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
314 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
315 else e = mesh->AddBall (node[0], ballDiameter);
320 if ( e ) myLastCreatedElems.Append( e );
324 //=======================================================================
328 //=======================================================================
330 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
331 const SMDSAbs_ElementType type,
335 vector<const SMDS_MeshNode*> nodes;
336 nodes.reserve( nodeIDs.size() );
337 vector<int>::const_iterator id = nodeIDs.begin();
338 while ( id != nodeIDs.end() ) {
339 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
340 nodes.push_back( node );
344 return AddElement( nodes, type, isPoly, ID );
347 //=======================================================================
349 //purpose : Remove a node or an element.
350 // Modify a compute state of sub-meshes which become empty
351 //=======================================================================
353 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
356 myLastCreatedElems.Clear();
357 myLastCreatedNodes.Clear();
359 SMESHDS_Mesh* aMesh = GetMeshDS();
360 set< SMESH_subMesh *> smmap;
363 list<int>::const_iterator it = theIDs.begin();
364 for ( ; it != theIDs.end(); it++ ) {
365 const SMDS_MeshElement * elem;
367 elem = aMesh->FindNode( *it );
369 elem = aMesh->FindElement( *it );
373 // Notify VERTEX sub-meshes about modification
375 const SMDS_MeshNode* node = cast2Node( elem );
376 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
377 if ( int aShapeID = node->getshapeId() )
378 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
381 // Find sub-meshes to notify about modification
382 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
383 // while ( nodeIt->more() ) {
384 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
385 // const SMDS_PositionPtr& aPosition = node->GetPosition();
386 // if ( aPosition.get() ) {
387 // if ( int aShapeID = aPosition->GetShapeId() ) {
388 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
389 // smmap.insert( sm );
396 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
398 aMesh->RemoveElement( elem );
402 // Notify sub-meshes about modification
403 if ( !smmap.empty() ) {
404 set< SMESH_subMesh *>::iterator smIt;
405 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
406 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
409 // // Check if the whole mesh becomes empty
410 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
411 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
416 //================================================================================
418 * \brief Create 0D elements on all nodes of the given object except those
419 * nodes on which a 0D element already exists.
420 * \param elements - Elements on whose nodes to create 0D elements; if empty,
421 * the all mesh is treated
422 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
424 //================================================================================
426 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
427 TIDSortedElemSet& all0DElems )
429 SMDS_ElemIteratorPtr elemIt;
430 vector< const SMDS_MeshElement* > allNodes;
431 if ( elements.empty() )
433 allNodes.reserve( GetMeshDS()->NbNodes() );
434 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
435 while ( elemIt->more() )
436 allNodes.push_back( elemIt->next() );
438 elemIt = elemSetIterator( allNodes );
442 elemIt = elemSetIterator( elements );
445 while ( elemIt->more() )
447 const SMDS_MeshElement* e = elemIt->next();
448 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
449 while ( nodeIt->more() )
451 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
452 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
454 all0DElems.insert( it0D->next() );
456 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
457 all0DElems.insert( myLastCreatedElems.Last() );
463 //=======================================================================
464 //function : FindShape
465 //purpose : Return an index of the shape theElem is on
466 // or zero if a shape not found
467 //=======================================================================
469 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
471 myLastCreatedElems.Clear();
472 myLastCreatedNodes.Clear();
474 SMESHDS_Mesh * aMesh = GetMeshDS();
475 if ( aMesh->ShapeToMesh().IsNull() )
478 int aShapeID = theElem->getshapeId();
482 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
483 if ( sm->Contains( theElem ))
486 if ( theElem->GetType() == SMDSAbs_Node ) {
487 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
490 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
493 TopoDS_Shape aShape; // the shape a node of theElem is on
494 if ( theElem->GetType() != SMDSAbs_Node )
496 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
497 while ( nodeIt->more() ) {
498 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
499 if ((aShapeID = node->getshapeId()) > 0) {
500 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
501 if ( sm->Contains( theElem ))
503 if ( aShape.IsNull() )
504 aShape = aMesh->IndexToShape( aShapeID );
510 // None of nodes is on a proper shape,
511 // find the shape among ancestors of aShape on which a node is
512 if ( !aShape.IsNull() ) {
513 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
514 for ( ; ancIt.More(); ancIt.Next() ) {
515 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
516 if ( sm && sm->Contains( theElem ))
517 return aMesh->ShapeToIndex( ancIt.Value() );
522 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
523 while ( const SMESHDS_SubMesh* sm = smIt->next() )
524 if ( sm->Contains( theElem ))
531 //=======================================================================
532 //function : IsMedium
534 //=======================================================================
536 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
537 const SMDSAbs_ElementType typeToCheck)
539 bool isMedium = false;
540 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
541 while (it->more() && !isMedium ) {
542 const SMDS_MeshElement* elem = it->next();
543 isMedium = elem->IsMediumNode(node);
548 //=======================================================================
549 //function : shiftNodesQuadTria
550 //purpose : Shift nodes in the array corresponded to quadratic triangle
551 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
552 //=======================================================================
554 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 const SMDS_MeshNode* nd1 = aNodes[0];
557 aNodes[0] = aNodes[1];
558 aNodes[1] = aNodes[2];
560 const SMDS_MeshNode* nd2 = aNodes[3];
561 aNodes[3] = aNodes[4];
562 aNodes[4] = aNodes[5];
566 //=======================================================================
567 //function : nbEdgeConnectivity
568 //purpose : return number of the edges connected with the theNode.
569 // if theEdges has connections with the other type of the
570 // elements, return -1
571 //=======================================================================
573 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577 // while(elemIt->more()) {
582 return theNode->NbInverseElements();
585 //=======================================================================
586 //function : getNodesFromTwoTria
588 //=======================================================================
590 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
591 const SMDS_MeshElement * theTria2,
592 vector< const SMDS_MeshNode*>& N1,
593 vector< const SMDS_MeshNode*>& N2)
595 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
596 if ( N1.size() < 6 ) return false;
597 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
598 if ( N2.size() < 6 ) return false;
600 int sames[3] = {-1,-1,-1};
612 if(nbsames!=2) return false;
614 shiftNodesQuadTria(N1);
616 shiftNodesQuadTria(N1);
619 i = sames[0] + sames[1] + sames[2];
621 shiftNodesQuadTria(N2);
623 // now we receive following N1 and N2 (using numeration as in the image below)
624 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
625 // i.e. first nodes from both arrays form a new diagonal
629 //=======================================================================
630 //function : InverseDiag
631 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
632 // but having other common link.
633 // Return False if args are improper
634 //=======================================================================
636 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
637 const SMDS_MeshElement * theTria2 )
639 MESSAGE("InverseDiag");
640 myLastCreatedElems.Clear();
641 myLastCreatedNodes.Clear();
643 if (!theTria1 || !theTria2)
646 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
647 if (!F1) return false;
648 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
649 if (!F2) return false;
650 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
651 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
654 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
658 // put nodes in array and find out indices of the same ones
659 const SMDS_MeshNode* aNodes [6];
660 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
662 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
663 while ( it->more() ) {
664 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666 if ( i > 2 ) // theTria2
667 // find same node of theTria1
668 for ( int j = 0; j < 3; j++ )
669 if ( aNodes[ i ] == aNodes[ j ]) {
678 return false; // theTria1 is not a triangle
679 it = theTria2->nodesIterator();
681 if ( i == 6 && it->more() )
682 return false; // theTria2 is not a triangle
685 // find indices of 1,2 and of A,B in theTria1
686 int iA = -1, iB = 0, i1 = 0, i2 = 0;
687 for ( i = 0; i < 6; i++ ) {
688 if ( sameInd [ i ] == -1 ) {
693 if ( iA >= 0) iB = i;
697 // nodes 1 and 2 should not be the same
698 if ( aNodes[ i1 ] == aNodes[ i2 ] )
702 aNodes[ iA ] = aNodes[ i2 ];
704 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
707 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
711 } // end if(F1 && F2)
713 // check case of quadratic faces
714 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
715 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
718 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
722 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
723 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
731 vector< const SMDS_MeshNode* > N1;
732 vector< const SMDS_MeshNode* > N2;
733 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735 // now we receive following N1 and N2 (using numeration as above image)
736 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
737 // i.e. first nodes from both arrays determ new diagonal
739 vector< const SMDS_MeshNode*> N1new( N1.size() );
740 vector< const SMDS_MeshNode*> N2new( N2.size() );
741 N1new.back() = N1.back(); // central node of biquadratic
742 N2new.back() = N2.back();
743 N1new[0] = N1[0]; N2new[0] = N1[0];
744 N1new[1] = N2[0]; N2new[1] = N1[1];
745 N1new[2] = N2[1]; N2new[2] = N2[0];
746 N1new[3] = N1[4]; N2new[3] = N1[3];
747 N1new[4] = N2[3]; N2new[4] = N2[5];
748 N1new[5] = N1[5]; N2new[5] = N1[4];
749 // change nodes in faces
750 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
751 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753 // move the central node of biquadratic triangle
754 SMESH_MesherHelper helper( *GetMesh() );
755 for ( int is2nd = 0; is2nd < 2; ++is2nd )
757 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
758 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
759 if ( nodes.size() < 7 )
761 helper.SetSubShape( tria->getshapeId() );
762 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
766 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
767 SMESH_TNodeXYZ( nodes[4] ) +
768 SMESH_TNodeXYZ( nodes[5] )) / 3.;
773 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
774 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
775 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
778 xyz = S->Value( uv.X(), uv.Y() );
779 xyz.Transform( loc );
780 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
781 nodes[6]->getshapeId() > 0 )
782 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
789 //=======================================================================
790 //function : findTriangles
791 //purpose : find triangles sharing theNode1-theNode2 link
792 //=======================================================================
794 static bool findTriangles(const SMDS_MeshNode * theNode1,
795 const SMDS_MeshNode * theNode2,
796 const SMDS_MeshElement*& theTria1,
797 const SMDS_MeshElement*& theTria2)
799 if ( !theNode1 || !theNode2 ) return false;
801 theTria1 = theTria2 = 0;
803 set< const SMDS_MeshElement* > emap;
804 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806 const SMDS_MeshElement* elem = it->next();
807 if ( elem->NbCornerNodes() == 3 )
810 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812 const SMDS_MeshElement* elem = it->next();
813 if ( emap.count( elem )) {
821 // theTria1 must be element with minimum ID
822 if ( theTria2->GetID() < theTria1->GetID() )
823 std::swap( theTria2, theTria1 );
831 //=======================================================================
832 //function : InverseDiag
833 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
834 // with ones built on the same 4 nodes but having other common link.
835 // Return false if proper faces not found
836 //=======================================================================
838 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
839 const SMDS_MeshNode * theNode2)
841 myLastCreatedElems.Clear();
842 myLastCreatedNodes.Clear();
844 MESSAGE( "::InverseDiag()" );
846 const SMDS_MeshElement *tr1, *tr2;
847 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
850 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
851 if (!F1) return false;
852 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
853 if (!F2) return false;
854 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
855 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
858 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
862 // put nodes in array
863 // and find indices of 1,2 and of A in tr1 and of B in tr2
864 int i, iA1 = 0, i1 = 0;
865 const SMDS_MeshNode* aNodes1 [3];
866 SMDS_ElemIteratorPtr it;
867 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
868 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
869 if ( aNodes1[ i ] == theNode1 )
870 iA1 = i; // node A in tr1
871 else if ( aNodes1[ i ] != theNode2 )
875 const SMDS_MeshNode* aNodes2 [3];
876 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
877 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
878 if ( aNodes2[ i ] == theNode2 )
879 iB2 = i; // node B in tr2
880 else if ( aNodes2[ i ] != theNode1 )
884 // nodes 1 and 2 should not be the same
885 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
889 aNodes1[ iA1 ] = aNodes2[ i2 ];
891 aNodes2[ iB2 ] = aNodes1[ i1 ];
893 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
894 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
899 // check case of quadratic faces
900 return InverseDiag(tr1,tr2);
903 //=======================================================================
904 //function : getQuadrangleNodes
905 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
906 // fusion of triangles tr1 and tr2 having shared link on
907 // theNode1 and theNode2
908 //=======================================================================
910 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
911 const SMDS_MeshNode * theNode1,
912 const SMDS_MeshNode * theNode2,
913 const SMDS_MeshElement * tr1,
914 const SMDS_MeshElement * tr2 )
916 if( tr1->NbNodes() != tr2->NbNodes() )
918 // find the 4-th node to insert into tr1
919 const SMDS_MeshNode* n4 = 0;
920 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922 while ( !n4 && i<3 ) {
923 const SMDS_MeshNode * n = cast2Node( it->next() );
925 bool isDiag = ( n == theNode1 || n == theNode2 );
929 // Make an array of nodes to be in a quadrangle
930 int iNode = 0, iFirstDiag = -1;
931 it = tr1->nodesIterator();
934 const SMDS_MeshNode * n = cast2Node( it->next() );
936 bool isDiag = ( n == theNode1 || n == theNode2 );
938 if ( iFirstDiag < 0 )
940 else if ( iNode - iFirstDiag == 1 )
941 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943 else if ( n == n4 ) {
944 return false; // tr1 and tr2 should not have all the same nodes
946 theQuadNodes[ iNode++ ] = n;
948 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
949 theQuadNodes[ iNode ] = n4;
954 //=======================================================================
955 //function : DeleteDiag
956 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
957 // with a quadrangle built on the same 4 nodes.
958 // Return false if proper faces not found
959 //=======================================================================
961 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
962 const SMDS_MeshNode * theNode2)
964 myLastCreatedElems.Clear();
965 myLastCreatedNodes.Clear();
967 MESSAGE( "::DeleteDiag()" );
969 const SMDS_MeshElement *tr1, *tr2;
970 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
973 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
974 if (!F1) return false;
975 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
976 if (!F2) return false;
977 SMESHDS_Mesh * aMesh = GetMeshDS();
979 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
980 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982 const SMDS_MeshNode* aNodes [ 4 ];
983 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
986 const SMDS_MeshElement* newElem = 0;
987 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
988 myLastCreatedElems.Append(newElem);
989 AddToSameGroups( newElem, tr1, aMesh );
990 int aShapeId = tr1->getshapeId();
993 aMesh->SetMeshElementOnShape( newElem, aShapeId );
995 aMesh->RemoveElement( tr1 );
996 aMesh->RemoveElement( tr2 );
1001 // check case of quadratic faces
1002 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1008 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1009 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1017 vector< const SMDS_MeshNode* > N1;
1018 vector< const SMDS_MeshNode* > N2;
1019 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021 // now we receive following N1 and N2 (using numeration as above image)
1022 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1023 // i.e. first nodes from both arrays determ new diagonal
1025 const SMDS_MeshNode* aNodes[8];
1035 const SMDS_MeshElement* newElem = 0;
1036 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1037 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1038 myLastCreatedElems.Append(newElem);
1039 AddToSameGroups( newElem, tr1, aMesh );
1040 int aShapeId = tr1->getshapeId();
1043 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045 aMesh->RemoveElement( tr1 );
1046 aMesh->RemoveElement( tr2 );
1048 // remove middle node (9)
1049 GetMeshDS()->RemoveNode( N1[4] );
1054 //=======================================================================
1055 //function : Reorient
1056 //purpose : Reverse theElement orientation
1057 //=======================================================================
1059 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 MESSAGE("Reorient");
1062 myLastCreatedElems.Clear();
1063 myLastCreatedNodes.Clear();
1067 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1068 if ( !it || !it->more() )
1071 const SMDSAbs_ElementType type = theElem->GetType();
1072 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1075 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1076 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078 const SMDS_VtkVolume* aPolyedre =
1079 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081 MESSAGE("Warning: bad volumic element");
1084 const int nbFaces = aPolyedre->NbFaces();
1085 vector<const SMDS_MeshNode *> poly_nodes;
1086 vector<int> quantities (nbFaces);
1088 // reverse each face of the polyedre
1089 for (int iface = 1; iface <= nbFaces; iface++) {
1090 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1091 quantities[iface - 1] = nbFaceNodes;
1093 for (inode = nbFaceNodes; inode >= 1; inode--) {
1094 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1095 poly_nodes.push_back(curNode);
1098 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100 else // other elements
1102 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1103 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1104 if ( interlace.empty() )
1106 std::reverse( nodes.begin(), nodes.end() ); // polygon
1108 else if ( interlace.size() > 1 )
1110 SMDS_MeshCell::applyInterlace( interlace, nodes );
1112 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1117 //================================================================================
1119 * \brief Reorient faces.
1120 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1121 * \param theDirection - desired direction of normal of \a theFace
1122 * \param theFace - one of \a theFaces that sould be oriented according to
1123 * \a theDirection and whose orientation defines orientation of other faces
1124 * \return number of reoriented faces.
1126 //================================================================================
1128 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1129 const gp_Dir& theDirection,
1130 const SMDS_MeshElement * theFace)
1133 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135 if ( theFaces.empty() )
1137 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1138 while ( fIt->more() )
1139 theFaces.insert( theFaces.end(), fIt->next() );
1142 // orient theFace according to theDirection
1144 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1145 if ( normal * theDirection.XYZ() < 0 )
1146 nbReori += Reorient( theFace );
1148 // Orient other faces
1150 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1151 TIDSortedElemSet avoidSet;
1152 set< SMESH_TLink > checkedLinks;
1153 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1156 theFaces.erase( theFace );
1157 startFaces.insert( theFace );
1159 int nodeInd1, nodeInd2;
1160 const SMDS_MeshElement* otherFace;
1161 vector< const SMDS_MeshElement* > facesNearLink;
1162 vector< std::pair< int, int > > nodeIndsOfFace;
1164 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1165 while ( !startFaces.empty() )
1167 startFace = startFaces.begin();
1168 theFace = *startFace;
1169 startFaces.erase( startFace );
1170 if ( !visitedFaces.insert( theFace ).second )
1174 avoidSet.insert(theFace);
1176 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1178 const int nbNodes = theFace->NbCornerNodes();
1179 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1182 linkIt_isNew = checkedLinks.insert( link );
1183 if ( !linkIt_isNew.second )
1185 // link has already been checked and won't be encountered more
1186 // if the group (theFaces) is manifold
1187 //checkedLinks.erase( linkIt_isNew.first );
1191 facesNearLink.clear();
1192 nodeIndsOfFace.clear();
1193 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195 &nodeInd1, &nodeInd2 )))
1196 if ( otherFace != theFace)
1198 facesNearLink.push_back( otherFace );
1199 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1200 avoidSet.insert( otherFace );
1202 if ( facesNearLink.size() > 1 )
1204 // NON-MANIFOLD mesh shell !
1205 // select a face most co-directed with theFace,
1206 // other faces won't be visited this time
1208 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1209 double proj, maxProj = -1;
1210 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1211 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1212 if (( proj = Abs( NF * NOF )) > maxProj ) {
1214 otherFace = facesNearLink[i];
1215 nodeInd1 = nodeIndsOfFace[i].first;
1216 nodeInd2 = nodeIndsOfFace[i].second;
1219 // not to visit rejected faces
1220 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1221 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1222 visitedFaces.insert( facesNearLink[i] );
1224 else if ( facesNearLink.size() == 1 )
1226 otherFace = facesNearLink[0];
1227 nodeInd1 = nodeIndsOfFace.back().first;
1228 nodeInd2 = nodeIndsOfFace.back().second;
1230 if ( otherFace && otherFace != theFace)
1232 // link must be reverse in otherFace if orientation ot otherFace
1233 // is same as that of theFace
1234 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236 nbReori += Reorient( otherFace );
1238 startFaces.insert( otherFace );
1241 std::swap( link.first, link.second ); // reverse the link
1247 //================================================================================
1249 * \brief Reorient faces basing on orientation of adjacent volumes.
1250 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1251 * \param theVolumes - reference volumes.
1252 * \param theOutsideNormal - to orient faces to have their normal
1253 * pointing either \a outside or \a inside the adjacent volumes.
1254 * \return number of reoriented faces.
1256 //================================================================================
1258 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1259 TIDSortedElemSet & theVolumes,
1260 const bool theOutsideNormal)
1264 SMDS_ElemIteratorPtr faceIt;
1265 if ( theFaces.empty() )
1266 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1268 faceIt = elemSetIterator( theFaces );
1270 vector< const SMDS_MeshNode* > faceNodes;
1271 TIDSortedElemSet checkedVolumes;
1272 set< const SMDS_MeshNode* > faceNodesSet;
1273 SMDS_VolumeTool volumeTool;
1275 while ( faceIt->more() ) // loop on given faces
1277 const SMDS_MeshElement* face = faceIt->next();
1278 if ( face->GetType() != SMDSAbs_Face )
1281 const int nbCornersNodes = face->NbCornerNodes();
1282 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1284 checkedVolumes.clear();
1285 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1286 while ( vIt->more() )
1288 const SMDS_MeshElement* volume = vIt->next();
1290 if ( !checkedVolumes.insert( volume ).second )
1292 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1295 // is volume adjacent?
1296 bool allNodesCommon = true;
1297 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1298 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1299 if ( !allNodesCommon )
1302 // get nodes of a corresponding volume facet
1303 faceNodesSet.clear();
1304 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1305 volumeTool.Set( volume );
1306 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1307 if ( facetID < 0 ) continue;
1308 volumeTool.SetExternalNormal();
1309 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1311 // compare order of faceNodes and facetNodes
1312 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1314 for ( int i = 0; i < 2; ++i )
1316 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1317 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1318 if ( faceNodes[ iN ] == n )
1324 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1325 if ( isOutside != theOutsideNormal )
1326 nbReori += Reorient( face );
1328 } // loop on given faces
1333 //=======================================================================
1334 //function : getBadRate
1336 //=======================================================================
1338 static double getBadRate (const SMDS_MeshElement* theElem,
1339 SMESH::Controls::NumericalFunctorPtr& theCrit)
1341 SMESH::Controls::TSequenceOfXYZ P;
1342 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1344 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1345 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1348 //=======================================================================
1349 //function : QuadToTri
1350 //purpose : Cut quadrangles into triangles.
1351 // theCrit is used to select a diagonal to cut
1352 //=======================================================================
1354 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1355 SMESH::Controls::NumericalFunctorPtr theCrit)
1357 myLastCreatedElems.Clear();
1358 myLastCreatedNodes.Clear();
1360 if ( !theCrit.get() )
1363 SMESHDS_Mesh * aMesh = GetMeshDS();
1365 Handle(Geom_Surface) surface;
1366 SMESH_MesherHelper helper( *GetMesh() );
1368 TIDSortedElemSet::iterator itElem;
1369 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1371 const SMDS_MeshElement* elem = *itElem;
1372 if ( !elem || elem->GetType() != SMDSAbs_Face )
1374 if ( elem->NbCornerNodes() != 4 )
1377 // retrieve element nodes
1378 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1380 // compare two sets of possible triangles
1381 double aBadRate1, aBadRate2; // to what extent a set is bad
1382 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1383 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1384 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1386 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1387 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1388 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1390 const int aShapeId = FindShape( elem );
1391 const SMDS_MeshElement* newElem1 = 0;
1392 const SMDS_MeshElement* newElem2 = 0;
1394 if ( !elem->IsQuadratic() ) // split liner quadrangle
1396 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1397 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1398 if ( aBadRate1 <= aBadRate2 ) {
1399 // tr1 + tr2 is better
1400 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1401 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1404 // tr3 + tr4 is better
1405 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1406 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1409 else // split quadratic quadrangle
1411 helper.SetIsQuadratic( true );
1412 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1414 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1415 if ( aNodes.size() == 9 )
1417 helper.SetIsBiQuadratic( true );
1418 if ( aBadRate1 <= aBadRate2 )
1419 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1421 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1423 // create a new element
1424 if ( aBadRate1 <= aBadRate2 ) {
1425 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1426 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1429 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1430 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1434 // care of a new element
1436 myLastCreatedElems.Append(newElem1);
1437 myLastCreatedElems.Append(newElem2);
1438 AddToSameGroups( newElem1, elem, aMesh );
1439 AddToSameGroups( newElem2, elem, aMesh );
1441 // put a new triangle on the same shape
1443 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1444 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1446 aMesh->RemoveElement( elem );
1451 //=======================================================================
1453 * \brief Split each of given quadrangles into 4 triangles.
1454 * \param theElems - The faces to be splitted. If empty all faces are split.
1456 //=======================================================================
1458 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1460 myLastCreatedElems.Clear();
1461 myLastCreatedNodes.Clear();
1463 SMESH_MesherHelper helper( *GetMesh() );
1464 helper.SetElementsOnShape( true );
1466 SMDS_ElemIteratorPtr faceIt;
1467 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1468 else faceIt = elemSetIterator( theElems );
1471 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1473 vector< const SMDS_MeshNode* > nodes;
1474 SMESHDS_SubMesh* subMeshDS;
1476 Handle(Geom_Surface) surface;
1477 TopLoc_Location loc;
1479 while ( faceIt->more() )
1481 const SMDS_MeshElement* quad = faceIt->next();
1482 if ( !quad || quad->NbCornerNodes() != 4 )
1485 // get a surface the quad is on
1487 if ( quad->getshapeId() < 1 )
1490 helper.SetSubShape( 0 );
1493 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1495 helper.SetSubShape( quad->getshapeId() );
1496 if ( !helper.GetSubShape().IsNull() &&
1497 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1499 F = TopoDS::Face( helper.GetSubShape() );
1500 surface = BRep_Tool::Surface( F, loc );
1501 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1505 helper.SetSubShape( 0 );
1510 // create a central node
1512 const SMDS_MeshNode* nCentral;
1513 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1515 if ( nodes.size() == 9 )
1517 nCentral = nodes.back();
1524 for ( ; iN < nodes.size(); ++iN )
1525 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1527 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1528 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1530 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1531 xyz[0], xyz[1], xyz[2], xyz[3],
1532 xyz[4], xyz[5], xyz[6], xyz[7] );
1536 for ( ; iN < nodes.size(); ++iN )
1537 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1539 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1540 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1542 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543 uv[0], uv[1], uv[2], uv[3],
1544 uv[4], uv[5], uv[6], uv[7] );
1546 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1550 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1551 uv[8].X(), uv[8].Y() );
1552 myLastCreatedNodes.Append( nCentral );
1555 // create 4 triangles
1557 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1559 helper.SetIsQuadratic ( nodes.size() > 4 );
1560 helper.SetIsBiQuadratic( nodes.size() == 9 );
1561 if ( helper.GetIsQuadratic() )
1562 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1564 for ( int i = 0; i < 4; ++i )
1566 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1569 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1570 myLastCreatedElems.Append( tria );
1575 //=======================================================================
1576 //function : BestSplit
1577 //purpose : Find better diagonal for cutting.
1578 //=======================================================================
1580 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1581 SMESH::Controls::NumericalFunctorPtr theCrit)
1583 myLastCreatedElems.Clear();
1584 myLastCreatedNodes.Clear();
1589 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1592 if( theQuad->NbNodes()==4 ||
1593 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1595 // retrieve element nodes
1596 const SMDS_MeshNode* aNodes [4];
1597 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1599 //while (itN->more())
1601 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1603 // compare two sets of possible triangles
1604 double aBadRate1, aBadRate2; // to what extent a set is bad
1605 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1606 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1607 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1609 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1610 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1611 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1612 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1613 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1614 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1615 return 1; // diagonal 1-3
1617 return 2; // diagonal 2-4
1624 // Methods of splitting volumes into tetra
1626 const int theHexTo5_1[5*4+1] =
1628 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1630 const int theHexTo5_2[5*4+1] =
1632 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1634 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1636 const int theHexTo6_1[6*4+1] =
1638 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
1640 const int theHexTo6_2[6*4+1] =
1642 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
1644 const int theHexTo6_3[6*4+1] =
1646 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
1648 const int theHexTo6_4[6*4+1] =
1650 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
1652 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1654 const int thePyraTo2_1[2*4+1] =
1656 0, 1, 2, 4, 0, 2, 3, 4, -1
1658 const int thePyraTo2_2[2*4+1] =
1660 1, 2, 3, 4, 1, 3, 0, 4, -1
1662 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1664 const int thePentaTo3_1[3*4+1] =
1666 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1668 const int thePentaTo3_2[3*4+1] =
1670 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1672 const int thePentaTo3_3[3*4+1] =
1674 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1676 const int thePentaTo3_4[3*4+1] =
1678 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1680 const int thePentaTo3_5[3*4+1] =
1682 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1684 const int thePentaTo3_6[3*4+1] =
1686 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1688 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1689 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1691 // Methods of splitting hexahedron into prisms
1693 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1695 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
1697 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1699 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
1701 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1703 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
1706 const int theHexTo2Prisms_BT_1[6*2+1] =
1708 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1710 const int theHexTo2Prisms_BT_2[6*2+1] =
1712 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1714 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1716 const int theHexTo2Prisms_LR_1[6*2+1] =
1718 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1720 const int theHexTo2Prisms_LR_2[6*2+1] =
1722 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1724 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1726 const int theHexTo2Prisms_FB_1[6*2+1] =
1728 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1730 const int theHexTo2Prisms_FB_2[6*2+1] =
1732 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1734 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1737 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1740 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1741 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1742 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1743 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1749 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1750 bool _baryNode; //!< additional node is to be created at cell barycenter
1751 bool _ownConn; //!< to delete _connectivity in destructor
1752 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1754 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1755 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1756 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1757 bool hasFacet( const TTriangleFacet& facet ) const
1759 if ( _nbCorners == 4 )
1761 const int* tetConn = _connectivity;
1762 for ( ; tetConn[0] >= 0; tetConn += 4 )
1763 if (( facet.contains( tetConn[0] ) +
1764 facet.contains( tetConn[1] ) +
1765 facet.contains( tetConn[2] ) +
1766 facet.contains( tetConn[3] )) == 3 )
1769 else // prism, _nbCorners == 6
1771 const int* prismConn = _connectivity;
1772 for ( ; prismConn[0] >= 0; prismConn += 6 )
1774 if (( facet.contains( prismConn[0] ) &&
1775 facet.contains( prismConn[1] ) &&
1776 facet.contains( prismConn[2] ))
1778 ( facet.contains( prismConn[3] ) &&
1779 facet.contains( prismConn[4] ) &&
1780 facet.contains( prismConn[5] )))
1788 //=======================================================================
1790 * \brief return TSplitMethod for the given element to split into tetrahedra
1792 //=======================================================================
1794 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1796 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1798 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1799 // an edge and a face barycenter; tertaherdons are based on triangles and
1800 // a volume barycenter
1801 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1803 // Find out how adjacent volumes are split
1805 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1806 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1807 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1809 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1810 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1811 if ( nbNodes < 4 ) continue;
1813 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1814 const int* nInd = vol.GetFaceNodesIndices( iF );
1817 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1818 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1819 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1820 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1824 int iCom = 0; // common node of triangle faces to split into
1825 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1827 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1828 nInd[ iQ * ( (iCom+1)%nbNodes )],
1829 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1830 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1831 nInd[ iQ * ( (iCom+2)%nbNodes )],
1832 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1833 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1835 triaSplits.push_back( t012 );
1836 triaSplits.push_back( t023 );
1841 if ( !triaSplits.empty() )
1842 hasAdjacentSplits = true;
1845 // Among variants of split method select one compliant with adjacent volumes
1847 TSplitMethod method;
1848 if ( !vol.Element()->IsPoly() && !is24TetMode )
1850 int nbVariants = 2, nbTet = 0;
1851 const int** connVariants = 0;
1852 switch ( vol.Element()->GetEntityType() )
1854 case SMDSEntity_Hexa:
1855 case SMDSEntity_Quad_Hexa:
1856 case SMDSEntity_TriQuad_Hexa:
1857 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1858 connVariants = theHexTo5, nbTet = 5;
1860 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1862 case SMDSEntity_Pyramid:
1863 case SMDSEntity_Quad_Pyramid:
1864 connVariants = thePyraTo2; nbTet = 2;
1866 case SMDSEntity_Penta:
1867 case SMDSEntity_Quad_Penta:
1868 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1873 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1875 // check method compliancy with adjacent tetras,
1876 // all found splits must be among facets of tetras described by this method
1877 method = TSplitMethod( nbTet, connVariants[variant] );
1878 if ( hasAdjacentSplits && method._nbSplits > 0 )
1880 bool facetCreated = true;
1881 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1883 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1884 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1885 facetCreated = method.hasFacet( *facet );
1887 if ( !facetCreated )
1888 method = TSplitMethod(0); // incompatible method
1892 if ( method._nbSplits < 1 )
1894 // No standard method is applicable, use a generic solution:
1895 // each facet of a volume is split into triangles and
1896 // each of triangles and a volume barycenter form a tetrahedron.
1898 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1900 int* connectivity = new int[ maxTetConnSize + 1 ];
1901 method._connectivity = connectivity;
1902 method._ownConn = true;
1903 method._baryNode = !isHex27; // to create central node or not
1906 int baryCenInd = vol.NbNodes() - int( isHex27 );
1907 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1909 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1910 const int* nInd = vol.GetFaceNodesIndices( iF );
1911 // find common node of triangle facets of tetra to create
1912 int iCommon = 0; // index in linear numeration
1913 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1914 if ( !triaSplits.empty() )
1917 const TTriangleFacet* facet = &triaSplits.front();
1918 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1919 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1920 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1923 else if ( nbNodes > 3 && !is24TetMode )
1925 // find the best method of splitting into triangles by aspect ratio
1926 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1927 map< double, int > badness2iCommon;
1928 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1929 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1930 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1933 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1935 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1936 nodes[ iQ*((iLast-1)%nbNodes)],
1937 nodes[ iQ*((iLast )%nbNodes)]);
1938 badness += getBadRate( &tria, aspectRatio );
1940 badness2iCommon.insert( make_pair( badness, iCommon ));
1942 // use iCommon with lowest badness
1943 iCommon = badness2iCommon.begin()->second;
1945 if ( iCommon >= nbNodes )
1946 iCommon = 0; // something wrong
1948 // fill connectivity of tetrahedra based on a current face
1949 int nbTet = nbNodes - 2;
1950 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1955 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1956 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1960 method._faceBaryNode[ iF ] = 0;
1961 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1964 for ( int i = 0; i < nbTet; ++i )
1966 int i1 = i, i2 = (i+1) % nbNodes;
1967 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1968 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1969 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1970 connectivity[ connSize++ ] = faceBaryCenInd;
1971 connectivity[ connSize++ ] = baryCenInd;
1976 for ( int i = 0; i < nbTet; ++i )
1978 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1979 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1981 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1982 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1983 connectivity[ connSize++ ] = baryCenInd;
1986 method._nbSplits += nbTet;
1988 } // loop on volume faces
1990 connectivity[ connSize++ ] = -1;
1992 } // end of generic solution
1996 //=======================================================================
1998 * \brief return TSplitMethod to split haxhedron into prisms
2000 //=======================================================================
2002 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2003 const int methodFlags,
2004 const int facetToSplit)
2006 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2008 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2010 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2012 static TSplitMethod to4methods[4]; // order BT, LR, FB
2013 if ( to4methods[iF]._nbSplits == 0 )
2017 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2018 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2019 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2022 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2023 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2024 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2027 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2028 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2029 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2031 default: return to4methods[3];
2033 to4methods[iF]._nbSplits = 4;
2034 to4methods[iF]._nbCorners = 6;
2036 return to4methods[iF];
2038 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2040 TSplitMethod method;
2042 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2044 const int nbVariants = 2, nbSplits = 2;
2045 const int** connVariants = 0;
2047 case 0: connVariants = theHexTo2Prisms_BT; break;
2048 case 1: connVariants = theHexTo2Prisms_LR; break;
2049 case 2: connVariants = theHexTo2Prisms_FB; break;
2050 default: return method;
2053 // look for prisms adjacent via facetToSplit and an opposite one
2054 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2056 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2057 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2058 if ( nbNodes != 4 ) return method;
2060 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2061 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2062 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2064 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2066 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2071 // there are adjacent prism
2072 for ( int variant = 0; variant < nbVariants; ++variant )
2074 // check method compliancy with adjacent prisms,
2075 // the found prism facets must be among facets of prisms described by current method
2076 method._nbSplits = nbSplits;
2077 method._nbCorners = 6;
2078 method._connectivity = connVariants[ variant ];
2079 if ( method.hasFacet( *t ))
2084 // No adjacent prisms. Select a variant with a best aspect ratio.
2086 double badness[2] = { 0, 0 };
2087 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2088 const SMDS_MeshNode** nodes = vol.GetNodes();
2089 for ( int variant = 0; variant < nbVariants; ++variant )
2090 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2092 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2093 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2095 method._connectivity = connVariants[ variant ];
2096 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2097 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2100 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2103 badness[ variant ] += getBadRate( &tria, aspectRatio );
2105 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2107 method._nbSplits = nbSplits;
2108 method._nbCorners = 6;
2109 method._connectivity = connVariants[ iBetter ];
2114 //================================================================================
2116 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2118 //================================================================================
2120 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2121 const SMDSAbs_GeometryType geom ) const
2123 // find the tetrahedron including the three nodes of facet
2124 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2125 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2126 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2127 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2128 while ( volIt1->more() )
2130 const SMDS_MeshElement* v = volIt1->next();
2131 if ( v->GetGeomType() != geom )
2133 const int lastCornerInd = v->NbCornerNodes() - 1;
2134 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2135 continue; // medium node not allowed
2136 const int ind2 = v->GetNodeIndex( n2 );
2137 if ( ind2 < 0 || lastCornerInd < ind2 )
2139 const int ind3 = v->GetNodeIndex( n3 );
2140 if ( ind3 < 0 || lastCornerInd < ind3 )
2147 //=======================================================================
2149 * \brief A key of a face of volume
2151 //=======================================================================
2153 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2155 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2157 TIDSortedNodeSet sortedNodes;
2158 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2159 int nbNodes = vol.NbFaceNodes( iF );
2160 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2161 for ( int i = 0; i < nbNodes; i += iQ )
2162 sortedNodes.insert( fNodes[i] );
2163 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2164 first.first = (*(n++))->GetID();
2165 first.second = (*(n++))->GetID();
2166 second.first = (*(n++))->GetID();
2167 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2172 //=======================================================================
2173 //function : SplitVolumes
2174 //purpose : Split volume elements into tetrahedra or prisms.
2175 // If facet ID < 0, element is split into tetrahedra,
2176 // else a hexahedron is split into prisms so that the given facet is
2177 // split into triangles
2178 //=======================================================================
2180 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2181 const int theMethodFlags)
2183 // std-like iterator on coordinates of nodes of mesh element
2184 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2185 NXyzIterator xyzEnd;
2187 SMDS_VolumeTool volTool;
2188 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2189 fHelper.ToFixNodeParameters( true );
2191 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2192 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2194 SMESH_SequenceOfElemPtr newNodes, newElems;
2196 // map face of volume to it's baricenrtic node
2197 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2200 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2201 for ( ; elem2facet != theElems.end(); ++elem2facet )
2203 const SMDS_MeshElement* elem = elem2facet->first;
2204 const int facetToSplit = elem2facet->second;
2205 if ( elem->GetType() != SMDSAbs_Volume )
2207 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2208 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2211 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2213 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2214 getTetraSplitMethod( volTool, theMethodFlags ) :
2215 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2216 if ( splitMethod._nbSplits < 1 ) continue;
2218 // find submesh to add new tetras to
2219 if ( !subMesh || !subMesh->Contains( elem ))
2221 int shapeID = FindShape( elem );
2222 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2223 subMesh = GetMeshDS()->MeshElements( shapeID );
2226 if ( elem->IsQuadratic() )
2229 // add quadratic links to the helper
2230 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2232 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2233 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2234 for ( int iN = 0; iN < nbN; iN += iQ )
2235 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2237 helper.SetIsQuadratic( true );
2242 helper.SetIsQuadratic( false );
2244 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2245 volTool.GetNodes() + elem->NbNodes() );
2246 helper.SetElementsOnShape( true );
2247 if ( splitMethod._baryNode )
2249 // make a node at barycenter
2250 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2251 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2252 nodes.push_back( gcNode );
2253 newNodes.Append( gcNode );
2255 if ( !splitMethod._faceBaryNode.empty() )
2257 // make or find baricentric nodes of faces
2258 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2259 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2261 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2262 volFace2BaryNode.insert
2263 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2266 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2267 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2269 nodes.push_back( iF_n->second = f_n->second );
2274 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2275 const int* volConn = splitMethod._connectivity;
2276 if ( splitMethod._nbCorners == 4 ) // tetra
2277 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2278 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2279 nodes[ volConn[1] ],
2280 nodes[ volConn[2] ],
2281 nodes[ volConn[3] ]));
2283 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2284 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2285 nodes[ volConn[1] ],
2286 nodes[ volConn[2] ],
2287 nodes[ volConn[3] ],
2288 nodes[ volConn[4] ],
2289 nodes[ volConn[5] ]));
2291 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2293 // Split faces on sides of the split volume
2295 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2296 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2298 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2299 if ( nbNodes < 4 ) continue;
2301 // find an existing face
2302 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2303 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2304 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2305 /*noMedium=*/false))
2308 helper.SetElementsOnShape( false );
2309 vector< const SMDS_MeshElement* > triangles;
2311 // find submesh to add new triangles in
2312 if ( !fSubMesh || !fSubMesh->Contains( face ))
2314 int shapeID = FindShape( face );
2315 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2317 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2318 if ( iF_n != splitMethod._faceBaryNode.end() )
2320 const SMDS_MeshNode *baryNode = iF_n->second;
2321 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2323 const SMDS_MeshNode* n1 = fNodes[iN];
2324 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2325 const SMDS_MeshNode *n3 = baryNode;
2326 if ( !volTool.IsFaceExternal( iF ))
2328 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2330 if ( fSubMesh ) // update position of the bary node on geometry
2333 subMesh->RemoveNode( baryNode, false );
2334 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2335 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2336 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2338 fHelper.SetSubShape( s );
2339 gp_XY uv( 1e100, 1e100 );
2341 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2342 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2345 // node is too far from the surface
2346 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2347 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2348 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2355 // among possible triangles create ones discribed by split method
2356 const int* nInd = volTool.GetFaceNodesIndices( iF );
2357 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2358 int iCom = 0; // common node of triangle faces to split into
2359 list< TTriangleFacet > facets;
2360 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2362 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2363 nInd[ iQ * ( (iCom+1)%nbNodes )],
2364 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2365 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2366 nInd[ iQ * ( (iCom+2)%nbNodes )],
2367 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2368 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2370 facets.push_back( t012 );
2371 facets.push_back( t023 );
2372 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2373 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2374 nInd[ iQ * ((iLast-1)%nbNodes )],
2375 nInd[ iQ * ((iLast )%nbNodes )]));
2379 list< TTriangleFacet >::iterator facet = facets.begin();
2380 if ( facet == facets.end() )
2382 for ( ; facet != facets.end(); ++facet )
2384 if ( !volTool.IsFaceExternal( iF ))
2385 swap( facet->_n2, facet->_n3 );
2386 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2387 volNodes[ facet->_n2 ],
2388 volNodes[ facet->_n3 ]));
2391 for ( int i = 0; i < triangles.size(); ++i )
2393 if ( !triangles[i] ) continue;
2395 fSubMesh->AddElement( triangles[i]);
2396 newElems.Append( triangles[i] );
2398 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2399 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2401 } // while a face based on facet nodes exists
2402 } // loop on volume faces to split them into triangles
2404 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2406 if ( geomType == SMDSEntity_TriQuad_Hexa )
2408 // remove medium nodes that could become free
2409 for ( int i = 20; i < volTool.NbNodes(); ++i )
2410 if ( volNodes[i]->NbInverseElements() == 0 )
2411 GetMeshDS()->RemoveNode( volNodes[i] );
2413 } // loop on volumes to split
2415 myLastCreatedNodes = newNodes;
2416 myLastCreatedElems = newElems;
2419 //=======================================================================
2420 //function : GetHexaFacetsToSplit
2421 //purpose : For hexahedra that will be split into prisms, finds facets to
2422 // split into triangles. Only hexahedra adjacent to the one closest
2423 // to theFacetNormal.Location() are returned.
2424 //param [in,out] theHexas - the hexahedra
2425 //param [in] theFacetNormal - facet normal
2426 //param [out] theFacets - the hexahedra and found facet IDs
2427 //=======================================================================
2429 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2430 const gp_Ax1& theFacetNormal,
2431 TFacetOfElem & theFacets)
2433 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2435 // Find a hexa closest to the location of theFacetNormal
2437 const SMDS_MeshElement* startHex;
2439 // get SMDS_ElemIteratorPtr on theHexas
2440 typedef const SMDS_MeshElement* TValue;
2441 typedef TIDSortedElemSet::iterator TSetIterator;
2442 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2443 typedef SMDS_MeshElement::GeomFilter TFilter;
2444 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2445 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2446 ( new TElemSetIter( theHexas.begin(),
2448 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2450 SMESH_ElementSearcher* searcher =
2451 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2453 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2458 throw SALOME_Exception( THIS_METHOD "startHex not found");
2461 // Select a facet of startHex by theFacetNormal
2463 SMDS_VolumeTool vTool( startHex );
2464 double norm[3], dot, maxDot = 0;
2466 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2467 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2469 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2477 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2479 // Fill theFacets starting from facetID of startHex
2481 // facets used for seach of volumes adjacent to already treated ones
2482 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2483 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2484 TFacetMap facetsToCheck;
2486 set<const SMDS_MeshNode*> facetNodes;
2487 const SMDS_MeshElement* curHex;
2489 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2493 // move in two directions from startHex via facetID
2494 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2497 int curFacet = facetID;
2498 if ( is2nd ) // do not treat startHex twice
2500 vTool.Set( curHex );
2501 if ( vTool.IsFreeFace( curFacet, &curHex ))
2507 vTool.GetFaceNodes( curFacet, facetNodes );
2508 vTool.Set( curHex );
2509 curFacet = vTool.GetFaceIndex( facetNodes );
2514 // store a facet to split
2515 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2517 theFacets.insert( make_pair( curHex, -1 ));
2520 if ( !allHex && !theHexas.count( curHex ))
2523 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2524 theFacets.insert( make_pair( curHex, curFacet ));
2525 if ( !facetIt2isNew.second )
2528 // remember not-to-split facets in facetsToCheck
2529 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2530 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2532 if ( iF == curFacet && iF == oppFacet )
2534 TVolumeFaceKey facetKey ( vTool, iF );
2535 TElemFacets elemFacet( facetIt2isNew.first, iF );
2536 pair< TFacetMap::iterator, bool > it2isnew =
2537 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2538 if ( !it2isnew.second )
2539 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2541 // pass to a volume adjacent via oppFacet
2542 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2548 // get a new curFacet
2549 vTool.GetFaceNodes( oppFacet, facetNodes );
2550 vTool.Set( curHex );
2551 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2554 } // move in two directions from startHex via facetID
2556 // Find a new startHex by facetsToCheck
2560 TFacetMap::iterator fIt = facetsToCheck.begin();
2561 while ( !startHex && fIt != facetsToCheck.end() )
2563 const TElemFacets& elemFacets = fIt->second;
2564 const SMDS_MeshElement* hex = elemFacets.first->first;
2565 int splitFacet = elemFacets.first->second;
2566 int lateralFacet = elemFacets.second;
2567 facetsToCheck.erase( fIt );
2568 fIt = facetsToCheck.begin();
2571 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2572 curHex->GetGeomType() != SMDSGeom_HEXA )
2574 if ( !allHex && !theHexas.count( curHex ))
2579 // find a facet of startHex to split
2581 set<const SMDS_MeshNode*> lateralNodes;
2582 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2583 vTool.GetFaceNodes( splitFacet, facetNodes );
2584 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2585 vTool.Set( startHex );
2586 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2588 // look for a facet of startHex having common nodes with facetNodes
2589 // but not lateralFacet
2590 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2592 if ( iF == lateralFacet )
2594 int nbCommonNodes = 0;
2595 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2596 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2597 nbCommonNodes += facetNodes.count( nn[ iN ]);
2599 if ( nbCommonNodes >= 2 )
2606 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2608 } // while ( startHex )
2611 //=======================================================================
2612 //function : AddToSameGroups
2613 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2614 //=======================================================================
2616 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2617 const SMDS_MeshElement* elemInGroups,
2618 SMESHDS_Mesh * aMesh)
2620 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2621 if (!groups.empty()) {
2622 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2623 for ( ; grIt != groups.end(); grIt++ ) {
2624 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2625 if ( group && group->Contains( elemInGroups ))
2626 group->SMDSGroup().Add( elemToAdd );
2632 //=======================================================================
2633 //function : RemoveElemFromGroups
2634 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2635 //=======================================================================
2636 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2637 SMESHDS_Mesh * aMesh)
2639 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2640 if (!groups.empty())
2642 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2643 for (; GrIt != groups.end(); GrIt++)
2645 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2646 if (!grp || grp->IsEmpty()) continue;
2647 grp->SMDSGroup().Remove(removeelem);
2652 //================================================================================
2654 * \brief Replace elemToRm by elemToAdd in the all groups
2656 //================================================================================
2658 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2659 const SMDS_MeshElement* elemToAdd,
2660 SMESHDS_Mesh * aMesh)
2662 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2663 if (!groups.empty()) {
2664 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2665 for ( ; grIt != groups.end(); grIt++ ) {
2666 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2667 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2668 group->SMDSGroup().Add( elemToAdd );
2673 //================================================================================
2675 * \brief Replace elemToRm by elemToAdd in the all groups
2677 //================================================================================
2679 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2680 const vector<const SMDS_MeshElement*>& elemToAdd,
2681 SMESHDS_Mesh * aMesh)
2683 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2684 if (!groups.empty())
2686 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2687 for ( ; grIt != groups.end(); grIt++ ) {
2688 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2689 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2690 for ( int i = 0; i < elemToAdd.size(); ++i )
2691 group->SMDSGroup().Add( elemToAdd[ i ] );
2696 //=======================================================================
2697 //function : QuadToTri
2698 //purpose : Cut quadrangles into triangles.
2699 // theCrit is used to select a diagonal to cut
2700 //=======================================================================
2702 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2703 const bool the13Diag)
2705 myLastCreatedElems.Clear();
2706 myLastCreatedNodes.Clear();
2708 MESSAGE( "::QuadToTri()" );
2710 SMESHDS_Mesh * aMesh = GetMeshDS();
2712 Handle(Geom_Surface) surface;
2713 SMESH_MesherHelper helper( *GetMesh() );
2715 TIDSortedElemSet::iterator itElem;
2716 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2717 const SMDS_MeshElement* elem = *itElem;
2718 if ( !elem || elem->GetType() != SMDSAbs_Face )
2720 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2721 if(!isquad) continue;
2723 if(elem->NbNodes()==4) {
2724 // retrieve element nodes
2725 const SMDS_MeshNode* aNodes [4];
2726 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2728 while ( itN->more() )
2729 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2731 int aShapeId = FindShape( elem );
2732 const SMDS_MeshElement* newElem1 = 0;
2733 const SMDS_MeshElement* newElem2 = 0;
2735 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2736 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2739 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2740 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2742 myLastCreatedElems.Append(newElem1);
2743 myLastCreatedElems.Append(newElem2);
2744 // put a new triangle on the same shape and add to the same groups
2747 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2748 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2750 AddToSameGroups( newElem1, elem, aMesh );
2751 AddToSameGroups( newElem2, elem, aMesh );
2752 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2753 aMesh->RemoveElement( elem );
2756 // Quadratic quadrangle
2758 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2760 // get surface elem is on
2761 int aShapeId = FindShape( elem );
2762 if ( aShapeId != helper.GetSubShapeID() ) {
2766 shape = aMesh->IndexToShape( aShapeId );
2767 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2768 TopoDS_Face face = TopoDS::Face( shape );
2769 surface = BRep_Tool::Surface( face );
2770 if ( !surface.IsNull() )
2771 helper.SetSubShape( shape );
2775 const SMDS_MeshNode* aNodes [8];
2776 const SMDS_MeshNode* inFaceNode = 0;
2777 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2779 while ( itN->more() ) {
2780 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2781 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2782 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2784 inFaceNode = aNodes[ i-1 ];
2788 // find middle point for (0,1,2,3)
2789 // and create a node in this point;
2791 if ( surface.IsNull() ) {
2793 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2797 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2800 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2802 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2804 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2805 myLastCreatedNodes.Append(newN);
2807 // create a new element
2808 const SMDS_MeshElement* newElem1 = 0;
2809 const SMDS_MeshElement* newElem2 = 0;
2811 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2812 aNodes[6], aNodes[7], newN );
2813 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2814 newN, aNodes[4], aNodes[5] );
2817 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2818 aNodes[7], aNodes[4], newN );
2819 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2820 newN, aNodes[5], aNodes[6] );
2822 myLastCreatedElems.Append(newElem1);
2823 myLastCreatedElems.Append(newElem2);
2824 // put a new triangle on the same shape and add to the same groups
2827 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2828 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2830 AddToSameGroups( newElem1, elem, aMesh );
2831 AddToSameGroups( newElem2, elem, aMesh );
2832 aMesh->RemoveElement( elem );
2839 //=======================================================================
2840 //function : getAngle
2842 //=======================================================================
2844 double getAngle(const SMDS_MeshElement * tr1,
2845 const SMDS_MeshElement * tr2,
2846 const SMDS_MeshNode * n1,
2847 const SMDS_MeshNode * n2)
2849 double angle = 2. * M_PI; // bad angle
2852 SMESH::Controls::TSequenceOfXYZ P1, P2;
2853 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2854 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2857 if(!tr1->IsQuadratic())
2858 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2860 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2861 if ( N1.SquareMagnitude() <= gp::Resolution() )
2863 if(!tr2->IsQuadratic())
2864 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2866 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2867 if ( N2.SquareMagnitude() <= gp::Resolution() )
2870 // find the first diagonal node n1 in the triangles:
2871 // take in account a diagonal link orientation
2872 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2873 for ( int t = 0; t < 2; t++ ) {
2874 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2875 int i = 0, iDiag = -1;
2876 while ( it->more()) {
2877 const SMDS_MeshElement *n = it->next();
2878 if ( n == n1 || n == n2 ) {
2882 if ( i - iDiag == 1 )
2883 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2892 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2895 angle = N1.Angle( N2 );
2900 // =================================================
2901 // class generating a unique ID for a pair of nodes
2902 // and able to return nodes by that ID
2903 // =================================================
2907 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2908 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2911 long GetLinkID (const SMDS_MeshNode * n1,
2912 const SMDS_MeshNode * n2) const
2914 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2917 bool GetNodes (const long theLinkID,
2918 const SMDS_MeshNode* & theNode1,
2919 const SMDS_MeshNode* & theNode2) const
2921 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2922 if ( !theNode1 ) return false;
2923 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2924 if ( !theNode2 ) return false;
2930 const SMESHDS_Mesh* myMesh;
2935 //=======================================================================
2936 //function : TriToQuad
2937 //purpose : Fuse neighbour triangles into quadrangles.
2938 // theCrit is used to select a neighbour to fuse with.
2939 // theMaxAngle is a max angle between element normals at which
2940 // fusion is still performed.
2941 //=======================================================================
2943 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2944 SMESH::Controls::NumericalFunctorPtr theCrit,
2945 const double theMaxAngle)
2947 myLastCreatedElems.Clear();
2948 myLastCreatedNodes.Clear();
2950 MESSAGE( "::TriToQuad()" );
2952 if ( !theCrit.get() )
2955 SMESHDS_Mesh * aMesh = GetMeshDS();
2957 // Prepare data for algo: build
2958 // 1. map of elements with their linkIDs
2959 // 2. map of linkIDs with their elements
2961 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2962 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2963 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2964 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2966 TIDSortedElemSet::iterator itElem;
2967 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2969 const SMDS_MeshElement* elem = *itElem;
2970 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2971 bool IsTria = ( elem->NbCornerNodes()==3 );
2972 if (!IsTria) continue;
2974 // retrieve element nodes
2975 const SMDS_MeshNode* aNodes [4];
2976 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2979 aNodes[ i++ ] = itN->next();
2980 aNodes[ 3 ] = aNodes[ 0 ];
2983 for ( i = 0; i < 3; i++ ) {
2984 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2985 // check if elements sharing a link can be fused
2986 itLE = mapLi_listEl.find( link );
2987 if ( itLE != mapLi_listEl.end() ) {
2988 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2990 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2991 //if ( FindShape( elem ) != FindShape( elem2 ))
2992 // continue; // do not fuse triangles laying on different shapes
2993 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2994 continue; // avoid making badly shaped quads
2995 (*itLE).second.push_back( elem );
2998 mapLi_listEl[ link ].push_back( elem );
3000 mapEl_setLi [ elem ].insert( link );
3003 // Clean the maps from the links shared by a sole element, ie
3004 // links to which only one element is bound in mapLi_listEl
3006 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3007 int nbElems = (*itLE).second.size();
3008 if ( nbElems < 2 ) {
3009 const SMDS_MeshElement* elem = (*itLE).second.front();
3010 SMESH_TLink link = (*itLE).first;
3011 mapEl_setLi[ elem ].erase( link );
3012 if ( mapEl_setLi[ elem ].empty() )
3013 mapEl_setLi.erase( elem );
3017 // Algo: fuse triangles into quadrangles
3019 while ( ! mapEl_setLi.empty() ) {
3020 // Look for the start element:
3021 // the element having the least nb of shared links
3022 const SMDS_MeshElement* startElem = 0;
3024 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3025 int nbLinks = (*itEL).second.size();
3026 if ( nbLinks < minNbLinks ) {
3027 startElem = (*itEL).first;
3028 minNbLinks = nbLinks;
3029 if ( minNbLinks == 1 )
3034 // search elements to fuse starting from startElem or links of elements
3035 // fused earlyer - startLinks
3036 list< SMESH_TLink > startLinks;
3037 while ( startElem || !startLinks.empty() ) {
3038 while ( !startElem && !startLinks.empty() ) {
3039 // Get an element to start, by a link
3040 SMESH_TLink linkId = startLinks.front();
3041 startLinks.pop_front();
3042 itLE = mapLi_listEl.find( linkId );
3043 if ( itLE != mapLi_listEl.end() ) {
3044 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3045 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3046 for ( ; itE != listElem.end() ; itE++ )
3047 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3049 mapLi_listEl.erase( itLE );
3054 // Get candidates to be fused
3055 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3056 const SMESH_TLink *link12, *link13;
3058 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3059 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3060 ASSERT( !setLi.empty() );
3061 set< SMESH_TLink >::iterator itLi;
3062 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3064 const SMESH_TLink & link = (*itLi);
3065 itLE = mapLi_listEl.find( link );
3066 if ( itLE == mapLi_listEl.end() )
3069 const SMDS_MeshElement* elem = (*itLE).second.front();
3071 elem = (*itLE).second.back();
3072 mapLi_listEl.erase( itLE );
3073 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3084 // add other links of elem to list of links to re-start from
3085 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3086 set< SMESH_TLink >::iterator it;
3087 for ( it = links.begin(); it != links.end(); it++ ) {
3088 const SMESH_TLink& link2 = (*it);
3089 if ( link2 != link )
3090 startLinks.push_back( link2 );
3094 // Get nodes of possible quadrangles
3095 const SMDS_MeshNode *n12 [4], *n13 [4];
3096 bool Ok12 = false, Ok13 = false;
3097 const SMDS_MeshNode *linkNode1, *linkNode2;
3099 linkNode1 = link12->first;
3100 linkNode2 = link12->second;
3101 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3105 linkNode1 = link13->first;
3106 linkNode2 = link13->second;
3107 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3111 // Choose a pair to fuse
3112 if ( Ok12 && Ok13 ) {
3113 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3114 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3115 double aBadRate12 = getBadRate( &quad12, theCrit );
3116 double aBadRate13 = getBadRate( &quad13, theCrit );
3117 if ( aBadRate13 < aBadRate12 )
3124 // and remove fused elems and remove links from the maps
3125 mapEl_setLi.erase( tr1 );
3128 mapEl_setLi.erase( tr2 );
3129 mapLi_listEl.erase( *link12 );
3130 if ( tr1->NbNodes() == 3 )
3132 const SMDS_MeshElement* newElem = 0;
3133 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3134 myLastCreatedElems.Append(newElem);
3135 AddToSameGroups( newElem, tr1, aMesh );
3136 int aShapeId = tr1->getshapeId();
3138 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3139 aMesh->RemoveElement( tr1 );
3140 aMesh->RemoveElement( tr2 );
3143 vector< const SMDS_MeshNode* > N1;
3144 vector< const SMDS_MeshNode* > N2;
3145 getNodesFromTwoTria(tr1,tr2,N1,N2);
3146 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3147 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3148 // i.e. first nodes from both arrays form a new diagonal
3149 const SMDS_MeshNode* aNodes[8];
3158 const SMDS_MeshElement* newElem = 0;
3159 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3160 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3161 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3163 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3164 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3165 myLastCreatedElems.Append(newElem);
3166 AddToSameGroups( newElem, tr1, aMesh );
3167 int aShapeId = tr1->getshapeId();
3169 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3170 aMesh->RemoveElement( tr1 );
3171 aMesh->RemoveElement( tr2 );
3172 // remove middle node (9)
3173 if ( N1[4]->NbInverseElements() == 0 )
3174 aMesh->RemoveNode( N1[4] );
3175 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3176 aMesh->RemoveNode( N1[6] );
3177 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3178 aMesh->RemoveNode( N2[6] );
3183 mapEl_setLi.erase( tr3 );
3184 mapLi_listEl.erase( *link13 );
3185 if ( tr1->NbNodes() == 3 ) {
3186 const SMDS_MeshElement* newElem = 0;
3187 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3188 myLastCreatedElems.Append(newElem);
3189 AddToSameGroups( newElem, tr1, aMesh );
3190 int aShapeId = tr1->getshapeId();
3192 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3193 aMesh->RemoveElement( tr1 );
3194 aMesh->RemoveElement( tr3 );
3197 vector< const SMDS_MeshNode* > N1;
3198 vector< const SMDS_MeshNode* > N2;
3199 getNodesFromTwoTria(tr1,tr3,N1,N2);
3200 // now we receive following N1 and N2 (using numeration as above image)
3201 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3202 // i.e. first nodes from both arrays form a new diagonal
3203 const SMDS_MeshNode* aNodes[8];
3212 const SMDS_MeshElement* newElem = 0;
3213 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3214 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3215 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3217 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3218 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3219 myLastCreatedElems.Append(newElem);
3220 AddToSameGroups( newElem, tr1, aMesh );
3221 int aShapeId = tr1->getshapeId();
3223 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3224 aMesh->RemoveElement( tr1 );
3225 aMesh->RemoveElement( tr3 );
3226 // remove middle node (9)
3227 if ( N1[4]->NbInverseElements() == 0 )
3228 aMesh->RemoveNode( N1[4] );
3229 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3230 aMesh->RemoveNode( N1[6] );
3231 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3232 aMesh->RemoveNode( N2[6] );
3236 // Next element to fuse: the rejected one
3238 startElem = Ok12 ? tr3 : tr2;
3240 } // if ( startElem )
3241 } // while ( startElem || !startLinks.empty() )
3242 } // while ( ! mapEl_setLi.empty() )
3248 /*#define DUMPSO(txt) \
3249 // cout << txt << endl;
3250 //=============================================================================
3254 //=============================================================================
3255 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3259 int tmp = idNodes[ i1 ];
3260 idNodes[ i1 ] = idNodes[ i2 ];
3261 idNodes[ i2 ] = tmp;
3262 gp_Pnt Ptmp = P[ i1 ];
3265 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3268 //=======================================================================
3269 //function : SortQuadNodes
3270 //purpose : Set 4 nodes of a quadrangle face in a good order.
3271 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3273 //=======================================================================
3275 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3280 for ( i = 0; i < 4; i++ ) {
3281 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3283 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3286 gp_Vec V1(P[0], P[1]);
3287 gp_Vec V2(P[0], P[2]);
3288 gp_Vec V3(P[0], P[3]);
3290 gp_Vec Cross1 = V1 ^ V2;
3291 gp_Vec Cross2 = V2 ^ V3;
3294 if (Cross1.Dot(Cross2) < 0)
3299 if (Cross1.Dot(Cross2) < 0)
3303 swap ( i, i + 1, idNodes, P );
3305 // for ( int ii = 0; ii < 4; ii++ ) {
3306 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3307 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3313 //=======================================================================
3314 //function : SortHexaNodes
3315 //purpose : Set 8 nodes of a hexahedron in a good order.
3316 // Return success status
3317 //=======================================================================
3319 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3324 DUMPSO( "INPUT: ========================================");
3325 for ( i = 0; i < 8; i++ ) {
3326 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3327 if ( !n ) return false;
3328 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3329 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3331 DUMPSO( "========================================");
3334 set<int> faceNodes; // ids of bottom face nodes, to be found
3335 set<int> checkedId1; // ids of tried 2-nd nodes
3336 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3337 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3338 int iMin, iLoop1 = 0;
3340 // Loop to try the 2-nd nodes
3342 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3344 // Find not checked 2-nd node
3345 for ( i = 1; i < 8; i++ )
3346 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3347 int id1 = idNodes[i];
3348 swap ( 1, i, idNodes, P );
3349 checkedId1.insert ( id1 );
3353 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3354 // ie that all but meybe one (id3 which is on the same face) nodes
3355 // lay on the same side from the triangle plane.
3357 bool manyInPlane = false; // more than 4 nodes lay in plane
3359 while ( ++iLoop2 < 6 ) {
3361 // get 1-2-3 plane coeffs
3362 Standard_Real A, B, C, D;
3363 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3364 if ( N.SquareMagnitude() > gp::Resolution() )
3366 gp_Pln pln ( P[0], N );
3367 pln.Coefficients( A, B, C, D );
3369 // find the node (iMin) closest to pln
3370 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3372 for ( i = 3; i < 8; i++ ) {
3373 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3374 if ( fabs( dist[i] ) < minDist ) {
3375 minDist = fabs( dist[i] );
3378 if ( fabs( dist[i] ) <= tol )
3379 idInPln.insert( idNodes[i] );
3382 // there should not be more than 4 nodes in bottom plane
3383 if ( idInPln.size() > 1 )
3385 DUMPSO( "### idInPln.size() = " << idInPln.size());
3386 // idInPlane does not contain the first 3 nodes
3387 if ( manyInPlane || idInPln.size() == 5)
3388 return false; // all nodes in one plane
3391 // set the 1-st node to be not in plane
3392 for ( i = 3; i < 8; i++ ) {
3393 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3394 DUMPSO( "### Reset 0-th node");
3395 swap( 0, i, idNodes, P );
3400 // reset to re-check second nodes
3401 leastDist = DBL_MAX;
3405 break; // from iLoop2;
3408 // check that the other 4 nodes are on the same side
3409 bool sameSide = true;
3410 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3411 for ( i = 3; sameSide && i < 8; i++ ) {
3413 sameSide = ( isNeg == dist[i] <= 0.);
3416 // keep best solution
3417 if ( sameSide && minDist < leastDist ) {
3418 leastDist = minDist;
3420 faceNodes.insert( idNodes[ 1 ] );
3421 faceNodes.insert( idNodes[ 2 ] );
3422 faceNodes.insert( idNodes[ iMin ] );
3423 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3424 << " leastDist = " << leastDist);
3425 if ( leastDist <= DBL_MIN )
3430 // set next 3-d node to check
3431 int iNext = 2 + iLoop2;
3433 DUMPSO( "Try 2-nd");
3434 swap ( 2, iNext, idNodes, P );
3436 } // while ( iLoop2 < 6 )
3439 if ( faceNodes.empty() ) return false;
3441 // Put the faceNodes in proper places
3442 for ( i = 4; i < 8; i++ ) {
3443 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3444 // find a place to put
3446 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3448 DUMPSO( "Set faceNodes");
3449 swap ( iTo, i, idNodes, P );
3454 // Set nodes of the found bottom face in good order
3455 DUMPSO( " Found bottom face: ");
3456 i = SortQuadNodes( theMesh, idNodes );
3458 gp_Pnt Ptmp = P[ i ];
3463 // for ( int ii = 0; ii < 4; ii++ ) {
3464 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3465 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3468 // Gravity center of the top and bottom faces
3469 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3470 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3472 // Get direction from the bottom to the top face
3473 gp_Vec upDir ( aGCb, aGCt );
3474 Standard_Real upDirSize = upDir.Magnitude();
3475 if ( upDirSize <= gp::Resolution() ) return false;
3478 // Assure that the bottom face normal points up
3479 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3480 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3481 if ( Nb.Dot( upDir ) < 0 ) {
3482 DUMPSO( "Reverse bottom face");
3483 swap( 1, 3, idNodes, P );
3486 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3487 Standard_Real minDist = DBL_MAX;
3488 for ( i = 4; i < 8; i++ ) {
3489 // projection of P[i] to the plane defined by P[0] and upDir
3490 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3491 Standard_Real sqDist = P[0].SquareDistance( Pp );
3492 if ( sqDist < minDist ) {
3497 DUMPSO( "Set 4-th");
3498 swap ( 4, iMin, idNodes, P );
3500 // Set nodes of the top face in good order
3501 DUMPSO( "Sort top face");
3502 i = SortQuadNodes( theMesh, &idNodes[4] );
3505 gp_Pnt Ptmp = P[ i ];
3510 // Assure that direction of the top face normal is from the bottom face
3511 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3512 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3513 if ( Nt.Dot( upDir ) < 0 ) {
3514 DUMPSO( "Reverse top face");
3515 swap( 5, 7, idNodes, P );
3518 // DUMPSO( "OUTPUT: ========================================");
3519 // for ( i = 0; i < 8; i++ ) {
3520 // float *p = ugrid->GetPoint(idNodes[i]);
3521 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3527 //================================================================================
3529 * \brief Return nodes linked to the given one
3530 * \param theNode - the node
3531 * \param linkedNodes - the found nodes
3532 * \param type - the type of elements to check
3534 * Medium nodes are ignored
3536 //================================================================================
3538 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3539 TIDSortedElemSet & linkedNodes,
3540 SMDSAbs_ElementType type )
3542 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3543 while ( elemIt->more() )
3545 const SMDS_MeshElement* elem = elemIt->next();
3546 if(elem->GetType() == SMDSAbs_0DElement)
3549 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3550 if ( elem->GetType() == SMDSAbs_Volume )
3552 SMDS_VolumeTool vol( elem );
3553 while ( nodeIt->more() ) {
3554 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3555 if ( theNode != n && vol.IsLinked( theNode, n ))
3556 linkedNodes.insert( n );
3561 for ( int i = 0; nodeIt->more(); ++i ) {
3562 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3563 if ( n == theNode ) {
3564 int iBefore = i - 1;
3566 if ( elem->IsQuadratic() ) {
3567 int nb = elem->NbNodes() / 2;
3568 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3569 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3571 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3572 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3579 //=======================================================================
3580 //function : laplacianSmooth
3581 //purpose : pulls theNode toward the center of surrounding nodes directly
3582 // connected to that node along an element edge
3583 //=======================================================================
3585 void laplacianSmooth(const SMDS_MeshNode* theNode,
3586 const Handle(Geom_Surface)& theSurface,
3587 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3589 // find surrounding nodes
3591 TIDSortedElemSet nodeSet;
3592 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3594 // compute new coodrs
3596 double coord[] = { 0., 0., 0. };
3597 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3598 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3599 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3600 if ( theSurface.IsNull() ) { // smooth in 3D
3601 coord[0] += node->X();
3602 coord[1] += node->Y();
3603 coord[2] += node->Z();
3605 else { // smooth in 2D
3606 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3607 gp_XY* uv = theUVMap[ node ];
3608 coord[0] += uv->X();
3609 coord[1] += uv->Y();
3612 int nbNodes = nodeSet.size();
3615 coord[0] /= nbNodes;
3616 coord[1] /= nbNodes;
3618 if ( !theSurface.IsNull() ) {
3619 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3620 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3621 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3627 coord[2] /= nbNodes;
3631 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3634 //=======================================================================
3635 //function : centroidalSmooth
3636 //purpose : pulls theNode toward the element-area-weighted centroid of the
3637 // surrounding elements
3638 //=======================================================================
3640 void centroidalSmooth(const SMDS_MeshNode* theNode,
3641 const Handle(Geom_Surface)& theSurface,
3642 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3644 gp_XYZ aNewXYZ(0.,0.,0.);
3645 SMESH::Controls::Area anAreaFunc;
3646 double totalArea = 0.;
3651 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3652 while ( elemIt->more() )
3654 const SMDS_MeshElement* elem = elemIt->next();
3657 gp_XYZ elemCenter(0.,0.,0.);
3658 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3659 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3660 int nn = elem->NbNodes();
3661 if(elem->IsQuadratic()) nn = nn/2;
3663 //while ( itN->more() ) {
3665 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3667 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3668 aNodePoints.push_back( aP );
3669 if ( !theSurface.IsNull() ) { // smooth in 2D
3670 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3671 gp_XY* uv = theUVMap[ aNode ];
3672 aP.SetCoord( uv->X(), uv->Y(), 0. );
3676 double elemArea = anAreaFunc.GetValue( aNodePoints );
3677 totalArea += elemArea;
3679 aNewXYZ += elemCenter * elemArea;
3681 aNewXYZ /= totalArea;
3682 if ( !theSurface.IsNull() ) {
3683 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3684 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3689 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3692 //=======================================================================
3693 //function : getClosestUV
3694 //purpose : return UV of closest projection
3695 //=======================================================================
3697 static bool getClosestUV (Extrema_GenExtPS& projector,
3698 const gp_Pnt& point,
3701 projector.Perform( point );
3702 if ( projector.IsDone() ) {
3703 double u, v, minVal = DBL_MAX;
3704 for ( int i = projector.NbExt(); i > 0; i-- )
3705 if ( projector.SquareDistance( i ) < minVal ) {
3706 minVal = projector.SquareDistance( i );
3707 projector.Point( i ).Parameter( u, v );
3709 result.SetCoord( u, v );
3715 //=======================================================================
3717 //purpose : Smooth theElements during theNbIterations or until a worst
3718 // element has aspect ratio <= theTgtAspectRatio.
3719 // Aspect Ratio varies in range [1.0, inf].
3720 // If theElements is empty, the whole mesh is smoothed.
3721 // theFixedNodes contains additionally fixed nodes. Nodes built
3722 // on edges and boundary nodes are always fixed.
3723 //=======================================================================
3725 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3726 set<const SMDS_MeshNode*> & theFixedNodes,
3727 const SmoothMethod theSmoothMethod,
3728 const int theNbIterations,
3729 double theTgtAspectRatio,
3732 myLastCreatedElems.Clear();
3733 myLastCreatedNodes.Clear();
3735 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3737 if ( theTgtAspectRatio < 1.0 )
3738 theTgtAspectRatio = 1.0;
3740 const double disttol = 1.e-16;
3742 SMESH::Controls::AspectRatio aQualityFunc;
3744 SMESHDS_Mesh* aMesh = GetMeshDS();
3746 if ( theElems.empty() ) {
3747 // add all faces to theElems
3748 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3749 while ( fIt->more() ) {
3750 const SMDS_MeshElement* face = fIt->next();
3751 theElems.insert( theElems.end(), face );
3754 // get all face ids theElems are on
3755 set< int > faceIdSet;
3756 TIDSortedElemSet::iterator itElem;
3758 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3759 int fId = FindShape( *itElem );
3760 // check that corresponding submesh exists and a shape is face
3762 faceIdSet.find( fId ) == faceIdSet.end() &&
3763 aMesh->MeshElements( fId )) {
3764 TopoDS_Shape F = aMesh->IndexToShape( fId );
3765 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3766 faceIdSet.insert( fId );
3769 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3771 // ===============================================
3772 // smooth elements on each TopoDS_Face separately
3773 // ===============================================
3775 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3776 for ( ; fId != faceIdSet.rend(); ++fId ) {
3777 // get face surface and submesh
3778 Handle(Geom_Surface) surface;
3779 SMESHDS_SubMesh* faceSubMesh = 0;
3781 double fToler2 = 0, f,l;
3782 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3783 bool isUPeriodic = false, isVPeriodic = false;
3785 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3786 surface = BRep_Tool::Surface( face );
3787 faceSubMesh = aMesh->MeshElements( *fId );
3788 fToler2 = BRep_Tool::Tolerance( face );
3789 fToler2 *= fToler2 * 10.;
3790 isUPeriodic = surface->IsUPeriodic();
3793 isVPeriodic = surface->IsVPeriodic();
3796 surface->Bounds( u1, u2, v1, v2 );
3798 // ---------------------------------------------------------
3799 // for elements on a face, find movable and fixed nodes and
3800 // compute UV for them
3801 // ---------------------------------------------------------
3802 bool checkBoundaryNodes = false;
3803 bool isQuadratic = false;
3804 set<const SMDS_MeshNode*> setMovableNodes;
3805 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3806 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3807 list< const SMDS_MeshElement* > elemsOnFace;
3809 Extrema_GenExtPS projector;
3810 GeomAdaptor_Surface surfAdaptor;
3811 if ( !surface.IsNull() ) {
3812 surfAdaptor.Load( surface );
3813 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3815 int nbElemOnFace = 0;
3816 itElem = theElems.begin();
3817 // loop on not yet smoothed elements: look for elems on a face
3818 while ( itElem != theElems.end() ) {
3819 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3820 break; // all elements found
3822 const SMDS_MeshElement* elem = *itElem;
3823 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3824 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3828 elemsOnFace.push_back( elem );
3829 theElems.erase( itElem++ );
3833 isQuadratic = elem->IsQuadratic();
3835 // get movable nodes of elem
3836 const SMDS_MeshNode* node;
3837 SMDS_TypeOfPosition posType;
3838 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3839 int nn = 0, nbn = elem->NbNodes();
3840 if(elem->IsQuadratic())
3842 while ( nn++ < nbn ) {
3843 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3844 const SMDS_PositionPtr& pos = node->GetPosition();
3845 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3846 if (posType != SMDS_TOP_EDGE &&
3847 posType != SMDS_TOP_VERTEX &&
3848 theFixedNodes.find( node ) == theFixedNodes.end())
3850 // check if all faces around the node are on faceSubMesh
3851 // because a node on edge may be bound to face
3852 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3854 if ( faceSubMesh ) {
3855 while ( eIt->more() && all ) {
3856 const SMDS_MeshElement* e = eIt->next();
3857 all = faceSubMesh->Contains( e );
3861 setMovableNodes.insert( node );
3863 checkBoundaryNodes = true;
3865 if ( posType == SMDS_TOP_3DSPACE )
3866 checkBoundaryNodes = true;
3869 if ( surface.IsNull() )
3872 // get nodes to check UV
3873 list< const SMDS_MeshNode* > uvCheckNodes;
3874 itN = elem->nodesIterator();
3875 nn = 0; nbn = elem->NbNodes();
3876 if(elem->IsQuadratic())
3878 while ( nn++ < nbn ) {
3879 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3880 if ( uvMap.find( node ) == uvMap.end() )
3881 uvCheckNodes.push_back( node );
3882 // add nodes of elems sharing node
3883 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3884 // while ( eIt->more() ) {
3885 // const SMDS_MeshElement* e = eIt->next();
3886 // if ( e != elem ) {
3887 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3888 // while ( nIt->more() ) {
3889 // const SMDS_MeshNode* n =
3890 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3891 // if ( uvMap.find( n ) == uvMap.end() )
3892 // uvCheckNodes.push_back( n );
3898 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3899 for ( ; n != uvCheckNodes.end(); ++n ) {
3902 const SMDS_PositionPtr& pos = node->GetPosition();
3903 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3905 switch ( posType ) {
3906 case SMDS_TOP_FACE: {
3907 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3908 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3911 case SMDS_TOP_EDGE: {
3912 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3913 Handle(Geom2d_Curve) pcurve;
3914 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3915 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3916 if ( !pcurve.IsNull() ) {
3917 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3918 uv = pcurve->Value( u ).XY();
3922 case SMDS_TOP_VERTEX: {
3923 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3924 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3925 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3930 // check existing UV
3931 bool project = true;
3932 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3933 double dist1 = DBL_MAX, dist2 = 0;
3934 if ( posType != SMDS_TOP_3DSPACE ) {
3935 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3936 project = dist1 > fToler2;
3938 if ( project ) { // compute new UV
3940 if ( !getClosestUV( projector, pNode, newUV )) {
3941 MESSAGE("Node Projection Failed " << node);
3945 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3947 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3949 if ( posType != SMDS_TOP_3DSPACE )
3950 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3951 if ( dist2 < dist1 )
3955 // store UV in the map
3956 listUV.push_back( uv );
3957 uvMap.insert( make_pair( node, &listUV.back() ));
3959 } // loop on not yet smoothed elements
3961 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3962 checkBoundaryNodes = true;
3964 // fix nodes on mesh boundary
3966 if ( checkBoundaryNodes ) {
3967 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3968 map< SMESH_TLink, int >::iterator link_nb;
3969 // put all elements links to linkNbMap
3970 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3971 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3972 const SMDS_MeshElement* elem = (*elemIt);
3973 int nbn = elem->NbCornerNodes();
3974 // loop on elem links: insert them in linkNbMap
3975 for ( int iN = 0; iN < nbn; ++iN ) {
3976 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3977 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3978 SMESH_TLink link( n1, n2 );
3979 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3983 // remove nodes that are in links encountered only once from setMovableNodes
3984 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3985 if ( link_nb->second == 1 ) {
3986 setMovableNodes.erase( link_nb->first.node1() );
3987 setMovableNodes.erase( link_nb->first.node2() );
3992 // -----------------------------------------------------
3993 // for nodes on seam edge, compute one more UV ( uvMap2 );
3994 // find movable nodes linked to nodes on seam and which
3995 // are to be smoothed using the second UV ( uvMap2 )
3996 // -----------------------------------------------------
3998 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3999 if ( !surface.IsNull() ) {
4000 TopExp_Explorer eExp( face, TopAbs_EDGE );
4001 for ( ; eExp.More(); eExp.Next() ) {
4002 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4003 if ( !BRep_Tool::IsClosed( edge, face ))
4005 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4006 if ( !sm ) continue;
4007 // find out which parameter varies for a node on seam
4010 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4011 if ( pcurve.IsNull() ) continue;
4012 uv1 = pcurve->Value( f );
4014 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4015 if ( pcurve.IsNull() ) continue;
4016 uv2 = pcurve->Value( f );
4017 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4019 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4020 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4022 // get nodes on seam and its vertices
4023 list< const SMDS_MeshNode* > seamNodes;
4024 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4025 while ( nSeamIt->more() ) {
4026 const SMDS_MeshNode* node = nSeamIt->next();
4027 if ( !isQuadratic || !IsMedium( node ))
4028 seamNodes.push_back( node );
4030 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4031 for ( ; vExp.More(); vExp.Next() ) {
4032 sm = aMesh->MeshElements( vExp.Current() );
4034 nSeamIt = sm->GetNodes();
4035 while ( nSeamIt->more() )
4036 seamNodes.push_back( nSeamIt->next() );
4039 // loop on nodes on seam
4040 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4041 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4042 const SMDS_MeshNode* nSeam = *noSeIt;
4043 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4044 if ( n_uv == uvMap.end() )
4047 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4048 // set the second UV
4049 listUV.push_back( *n_uv->second );
4050 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4051 if ( uvMap2.empty() )
4052 uvMap2 = uvMap; // copy the uvMap contents
4053 uvMap2[ nSeam ] = &listUV.back();
4055 // collect movable nodes linked to ones on seam in nodesNearSeam
4056 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4057 while ( eIt->more() ) {
4058 const SMDS_MeshElement* e = eIt->next();
4059 int nbUseMap1 = 0, nbUseMap2 = 0;
4060 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4061 int nn = 0, nbn = e->NbNodes();
4062 if(e->IsQuadratic()) nbn = nbn/2;
4063 while ( nn++ < nbn )
4065 const SMDS_MeshNode* n =
4066 static_cast<const SMDS_MeshNode*>( nIt->next() );
4068 setMovableNodes.find( n ) == setMovableNodes.end() )
4070 // add only nodes being closer to uv2 than to uv1
4071 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4072 0.5 * ( n->Y() + nSeam->Y() ),
4073 0.5 * ( n->Z() + nSeam->Z() ));
4075 getClosestUV( projector, pMid, uv );
4076 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4077 nodesNearSeam.insert( n );
4083 // for centroidalSmooth all element nodes must
4084 // be on one side of a seam
4085 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4086 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4088 while ( nn++ < nbn ) {
4089 const SMDS_MeshNode* n =
4090 static_cast<const SMDS_MeshNode*>( nIt->next() );
4091 setMovableNodes.erase( n );
4095 } // loop on nodes on seam
4096 } // loop on edge of a face
4097 } // if ( !face.IsNull() )
4099 if ( setMovableNodes.empty() ) {
4100 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4101 continue; // goto next face
4109 double maxRatio = -1., maxDisplacement = -1.;
4110 set<const SMDS_MeshNode*>::iterator nodeToMove;
4111 for ( it = 0; it < theNbIterations; it++ ) {
4112 maxDisplacement = 0.;
4113 nodeToMove = setMovableNodes.begin();
4114 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4115 const SMDS_MeshNode* node = (*nodeToMove);
4116 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4119 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4120 if ( theSmoothMethod == LAPLACIAN )
4121 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4123 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4125 // node displacement
4126 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4127 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4128 if ( aDispl > maxDisplacement )
4129 maxDisplacement = aDispl;
4131 // no node movement => exit
4132 //if ( maxDisplacement < 1.e-16 ) {
4133 if ( maxDisplacement < disttol ) {
4134 MESSAGE("-- no node movement --");
4138 // check elements quality
4140 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4141 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4142 const SMDS_MeshElement* elem = (*elemIt);
4143 if ( !elem || elem->GetType() != SMDSAbs_Face )
4145 SMESH::Controls::TSequenceOfXYZ aPoints;
4146 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4147 double aValue = aQualityFunc.GetValue( aPoints );
4148 if ( aValue > maxRatio )
4152 if ( maxRatio <= theTgtAspectRatio ) {
4153 MESSAGE("-- quality achived --");
4156 if (it+1 == theNbIterations) {
4157 MESSAGE("-- Iteration limit exceeded --");
4159 } // smoothing iterations
4161 MESSAGE(" Face id: " << *fId <<
4162 " Nb iterstions: " << it <<
4163 " Displacement: " << maxDisplacement <<
4164 " Aspect Ratio " << maxRatio);
4166 // ---------------------------------------
4167 // new nodes positions are computed,
4168 // record movement in DS and set new UV
4169 // ---------------------------------------
4170 nodeToMove = setMovableNodes.begin();
4171 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4172 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4173 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4174 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4175 if ( node_uv != uvMap.end() ) {
4176 gp_XY* uv = node_uv->second;
4178 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4182 // move medium nodes of quadratic elements
4185 SMESH_MesherHelper helper( *GetMesh() );
4186 helper.SetSubShape( face );
4187 vector<const SMDS_MeshNode*> nodes;
4189 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4190 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4192 const SMDS_MeshElement* QF = *elemIt;
4193 if ( QF->IsQuadratic() )
4195 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4196 SMDS_MeshElement::iterator() );
4197 nodes.push_back( nodes[0] );
4199 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4201 if ( !surface.IsNull() )
4203 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4204 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4205 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4206 xyz = surface->Value( uv.X(), uv.Y() );
4209 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4211 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4212 // we have to move a medium node
4213 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4219 } // loop on face ids
4223 //=======================================================================
4224 //function : isReverse
4225 //purpose : Return true if normal of prevNodes is not co-directied with
4226 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4227 // iNotSame is where prevNodes and nextNodes are different.
4228 // If result is true then future volume orientation is OK
4229 //=======================================================================
4231 static bool isReverse(const SMDS_MeshElement* face,
4232 const vector<const SMDS_MeshNode*>& prevNodes,
4233 const vector<const SMDS_MeshNode*>& nextNodes,
4237 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4238 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4239 gp_XYZ extrDir( pN - pP ), faceNorm;
4240 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4242 return faceNorm * extrDir < 0.0;
4245 //=======================================================================
4247 * \brief Create elements by sweeping an element
4248 * \param elem - element to sweep
4249 * \param newNodesItVec - nodes generated from each node of the element
4250 * \param newElems - generated elements
4251 * \param nbSteps - number of sweeping steps
4252 * \param srcElements - to append elem for each generated element
4254 //=======================================================================
4256 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4257 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4258 list<const SMDS_MeshElement*>& newElems,
4260 SMESH_SequenceOfElemPtr& srcElements)
4262 //MESSAGE("sweepElement " << nbSteps);
4263 SMESHDS_Mesh* aMesh = GetMeshDS();
4265 const int nbNodes = elem->NbNodes();
4266 const int nbCorners = elem->NbCornerNodes();
4267 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4268 polyhedron creation !!! */
4269 // Loop on elem nodes:
4270 // find new nodes and detect same nodes indices
4271 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4272 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4273 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4274 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4276 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4277 vector<int> sames(nbNodes);
4278 vector<bool> isSingleNode(nbNodes);
4280 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4281 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4282 const SMDS_MeshNode* node = nnIt->first;
4283 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4284 if ( listNewNodes.empty() )
4287 itNN [ iNode ] = listNewNodes.begin();
4288 prevNod[ iNode ] = node;
4289 nextNod[ iNode ] = listNewNodes.front();
4291 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4292 corner node of linear */
4293 if ( prevNod[ iNode ] != nextNod [ iNode ])
4294 nbDouble += !isSingleNode[iNode];
4296 if( iNode < nbCorners ) { // check corners only
4297 if ( prevNod[ iNode ] == nextNod [ iNode ])
4298 sames[nbSame++] = iNode;
4300 iNotSameNode = iNode;
4304 if ( nbSame == nbNodes || nbSame > 2) {
4305 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4309 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4311 // fix nodes order to have bottom normal external
4312 if ( baseType == SMDSEntity_Polygon )
4314 std::reverse( itNN.begin(), itNN.end() );
4315 std::reverse( prevNod.begin(), prevNod.end() );
4316 std::reverse( midlNod.begin(), midlNod.end() );
4317 std::reverse( nextNod.begin(), nextNod.end() );
4318 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4322 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4323 SMDS_MeshCell::applyInterlace( ind, itNN );
4324 SMDS_MeshCell::applyInterlace( ind, prevNod );
4325 SMDS_MeshCell::applyInterlace( ind, nextNod );
4326 SMDS_MeshCell::applyInterlace( ind, midlNod );
4327 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4330 sames[nbSame] = iNotSameNode;
4331 for ( int j = 0; j <= nbSame; ++j )
4332 for ( size_t i = 0; i < ind.size(); ++i )
4333 if ( ind[i] == sames[j] )
4338 iNotSameNode = sames[nbSame];
4343 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4345 iSameNode = sames[ nbSame-1 ];
4346 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4347 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4348 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4351 // make new elements
4352 for (int iStep = 0; iStep < nbSteps; iStep++ )
4355 for ( iNode = 0; iNode < nbNodes; iNode++ )
4357 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4358 nextNod[ iNode ] = *itNN[ iNode ]++;
4361 SMDS_MeshElement* aNewElem = 0;
4362 /*if(!elem->IsPoly())*/ {
4363 switch ( baseType ) {
4365 case SMDSEntity_Node: { // sweep NODE
4366 if ( nbSame == 0 ) {
4367 if ( isSingleNode[0] )
4368 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4370 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4376 case SMDSEntity_Edge: { // sweep EDGE
4377 if ( nbDouble == 0 )
4379 if ( nbSame == 0 ) // ---> quadrangle
4380 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4381 nextNod[ 1 ], nextNod[ 0 ] );
4382 else // ---> triangle
4383 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4384 nextNod[ iNotSameNode ] );
4386 else // ---> polygon
4388 vector<const SMDS_MeshNode*> poly_nodes;
4389 poly_nodes.push_back( prevNod[0] );
4390 poly_nodes.push_back( prevNod[1] );
4391 if ( prevNod[1] != nextNod[1] )
4393 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4394 poly_nodes.push_back( nextNod[1] );
4396 if ( prevNod[0] != nextNod[0] )
4398 poly_nodes.push_back( nextNod[0] );
4399 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4401 switch ( poly_nodes.size() ) {
4403 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4406 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4407 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4410 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4415 case SMDSEntity_Triangle: // TRIANGLE --->
4417 if ( nbDouble > 0 ) break;
4418 if ( nbSame == 0 ) // ---> pentahedron
4419 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4420 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4422 else if ( nbSame == 1 ) // ---> pyramid
4423 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4424 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4425 nextNod[ iSameNode ]);
4427 else // 2 same nodes: ---> tetrahedron
4428 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4429 nextNod[ iNotSameNode ]);
4432 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4436 if ( nbDouble+nbSame == 2 )
4438 if(nbSame==0) { // ---> quadratic quadrangle
4439 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4440 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4442 else { //(nbSame==1) // ---> quadratic triangle
4444 return; // medium node on axis
4446 else if(sames[0]==0)
4447 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4448 nextNod[2], midlNod[1], prevNod[2]);
4450 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4451 midlNod[0], nextNod[2], prevNod[2]);
4454 else if ( nbDouble == 3 )
4456 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4457 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4458 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4465 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4466 if ( nbDouble > 0 ) break;
4468 if ( nbSame == 0 ) // ---> hexahedron
4469 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4470 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4472 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4473 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4474 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4475 nextNod[ iSameNode ]);
4476 newElems.push_back( aNewElem );
4477 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4478 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4479 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4481 else if ( nbSame == 2 ) { // ---> pentahedron
4482 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4483 // iBeforeSame is same too
4484 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4485 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4486 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4488 // iAfterSame is same too
4489 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4490 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4491 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4495 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4496 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4497 if ( nbDouble+nbSame != 3 ) break;
4499 // ---> pentahedron with 15 nodes
4500 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4501 nextNod[0], nextNod[1], nextNod[2],
4502 prevNod[3], prevNod[4], prevNod[5],
4503 nextNod[3], nextNod[4], nextNod[5],
4504 midlNod[0], midlNod[1], midlNod[2]);
4506 else if(nbSame==1) {
4507 // ---> 2d order pyramid of 13 nodes
4508 int apex = iSameNode;
4509 int i0 = ( apex + 1 ) % nbCorners;
4510 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4514 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4515 nextNod[i0], nextNod[i1], prevNod[apex],
4516 prevNod[i01], midlNod[i0],
4517 nextNod[i01], midlNod[i1],
4518 prevNod[i1a], prevNod[i0a],
4519 nextNod[i0a], nextNod[i1a]);
4521 else if(nbSame==2) {
4522 // ---> 2d order tetrahedron of 10 nodes
4523 int n1 = iNotSameNode;
4524 int n2 = ( n1 + 1 ) % nbCorners;
4525 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4529 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4530 prevNod[n12], prevNod[n23], prevNod[n31],
4531 midlNod[n1], nextNod[n12], nextNod[n31]);
4535 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4537 if ( nbDouble != 4 ) break;
4538 // ---> hexahedron with 20 nodes
4539 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4540 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4541 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4542 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4543 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4545 else if(nbSame==1) {
4546 // ---> pyramid + pentahedron - can not be created since it is needed
4547 // additional middle node at the center of face
4548 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4551 else if( nbSame == 2 ) {
4552 if ( nbDouble != 2 ) break;
4553 // ---> 2d order Pentahedron with 15 nodes
4555 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4556 // iBeforeSame is same too
4563 // iAfterSame is same too
4573 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4574 prevNod[n4], prevNod[n5], nextNod[n5],
4575 prevNod[n12], midlNod[n2], nextNod[n12],
4576 prevNod[n45], midlNod[n5], nextNod[n45],
4577 prevNod[n14], prevNod[n25], nextNod[n25]);
4581 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4583 if( nbSame == 0 && nbDouble == 9 ) {
4584 // ---> tri-quadratic hexahedron with 27 nodes
4585 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4586 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4587 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4588 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4589 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4590 prevNod[8], // bottom center
4591 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4592 nextNod[8], // top center
4593 midlNod[8]);// elem center
4601 case SMDSEntity_Polygon: { // sweep POLYGON
4603 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4604 // ---> hexagonal prism
4605 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4606 prevNod[3], prevNod[4], prevNod[5],
4607 nextNod[0], nextNod[1], nextNod[2],
4608 nextNod[3], nextNod[4], nextNod[5]);
4612 case SMDSEntity_Ball:
4620 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4622 if ( baseType != SMDSEntity_Polygon )
4624 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4625 SMDS_MeshCell::applyInterlace( ind, prevNod );
4626 SMDS_MeshCell::applyInterlace( ind, nextNod );
4627 SMDS_MeshCell::applyInterlace( ind, midlNod );
4628 SMDS_MeshCell::applyInterlace( ind, itNN );
4629 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4630 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4632 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4633 vector<int> quantities (nbNodes + 2);
4634 polyedre_nodes.clear();
4638 for (int inode = 0; inode < nbNodes; inode++)
4639 polyedre_nodes.push_back( prevNod[inode] );
4640 quantities.push_back( nbNodes );
4643 polyedre_nodes.push_back( nextNod[0] );
4644 for (int inode = nbNodes; inode-1; --inode )
4645 polyedre_nodes.push_back( nextNod[inode-1] );
4646 quantities.push_back( nbNodes );
4649 for (int iface = 0; iface < nbNodes; iface++)
4651 const int prevNbNodes = polyedre_nodes.size();
4652 int inextface = (iface+1) % nbNodes;
4653 polyedre_nodes.push_back( prevNod[inextface] );
4654 polyedre_nodes.push_back( prevNod[iface] );
4655 if ( prevNod[iface] != nextNod[iface] )
4657 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4658 polyedre_nodes.push_back( nextNod[iface] );
4660 if ( prevNod[inextface] != nextNod[inextface] )
4662 polyedre_nodes.push_back( nextNod[inextface] );
4663 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4665 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4666 if ( nbFaceNodes > 2 )
4667 quantities.push_back( nbFaceNodes );
4668 else // degenerated face
4669 polyedre_nodes.resize( prevNbNodes );
4671 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4675 newElems.push_back( aNewElem );
4676 myLastCreatedElems.Append(aNewElem);
4677 srcElements.Append( elem );
4680 // set new prev nodes
4681 for ( iNode = 0; iNode < nbNodes; iNode++ )
4682 prevNod[ iNode ] = nextNod[ iNode ];
4687 //=======================================================================
4689 * \brief Create 1D and 2D elements around swept elements
4690 * \param mapNewNodes - source nodes and ones generated from them
4691 * \param newElemsMap - source elements and ones generated from them
4692 * \param elemNewNodesMap - nodes generated from each node of each element
4693 * \param elemSet - all swept elements
4694 * \param nbSteps - number of sweeping steps
4695 * \param srcElements - to append elem for each generated element
4697 //=======================================================================
4699 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4700 TTElemOfElemListMap & newElemsMap,
4701 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4702 TIDSortedElemSet& elemSet,
4704 SMESH_SequenceOfElemPtr& srcElements)
4706 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4707 SMESHDS_Mesh* aMesh = GetMeshDS();
4709 // Find nodes belonging to only one initial element - sweep them into edges.
4711 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4712 for ( ; nList != mapNewNodes.end(); nList++ )
4714 const SMDS_MeshNode* node =
4715 static_cast<const SMDS_MeshNode*>( nList->first );
4716 if ( newElemsMap.count( node ))
4717 continue; // node was extruded into edge
4718 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4719 int nbInitElems = 0;
4720 const SMDS_MeshElement* el = 0;
4721 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4722 while ( eIt->more() && nbInitElems < 2 ) {
4724 SMDSAbs_ElementType type = el->GetType();
4725 if ( type == SMDSAbs_Volume || type < highType ) continue;
4726 if ( type > highType ) {
4730 nbInitElems += elemSet.count(el);
4732 if ( nbInitElems < 2 ) {
4733 bool NotCreateEdge = el && el->IsMediumNode(node);
4734 if(!NotCreateEdge) {
4735 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4736 list<const SMDS_MeshElement*> newEdges;
4737 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4742 // Make a ceiling for each element ie an equal element of last new nodes.
4743 // Find free links of faces - make edges and sweep them into faces.
4745 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4746 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4747 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4749 const SMDS_MeshElement* elem = itElem->first;
4750 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4752 if(itElem->second.size()==0) continue;
4754 const bool isQuadratic = elem->IsQuadratic();
4756 if ( elem->GetType() == SMDSAbs_Edge ) {
4757 // create a ceiling edge
4758 if ( !isQuadratic ) {
4759 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4760 vecNewNodes[ 1 ]->second.back())) {
4761 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4762 vecNewNodes[ 1 ]->second.back()));
4763 srcElements.Append( elem );
4767 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4768 vecNewNodes[ 1 ]->second.back(),
4769 vecNewNodes[ 2 ]->second.back())) {
4770 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4771 vecNewNodes[ 1 ]->second.back(),
4772 vecNewNodes[ 2 ]->second.back()));
4773 srcElements.Append( elem );
4777 if ( elem->GetType() != SMDSAbs_Face )
4780 bool hasFreeLinks = false;
4782 TIDSortedElemSet avoidSet;
4783 avoidSet.insert( elem );
4785 set<const SMDS_MeshNode*> aFaceLastNodes;
4786 int iNode, nbNodes = vecNewNodes.size();
4787 if ( !isQuadratic ) {
4788 // loop on the face nodes
4789 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4790 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4791 // look for free links of the face
4792 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4793 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4794 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4795 // check if a link n1-n2 is free
4796 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4797 hasFreeLinks = true;
4798 // make a new edge and a ceiling for a new edge
4799 const SMDS_MeshElement* edge;
4800 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4801 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4802 srcElements.Append( myLastCreatedElems.Last() );
4804 n1 = vecNewNodes[ iNode ]->second.back();
4805 n2 = vecNewNodes[ iNext ]->second.back();
4806 if ( !aMesh->FindEdge( n1, n2 )) {
4807 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4808 srcElements.Append( edge );
4813 else { // elem is quadratic face
4814 int nbn = nbNodes/2;
4815 for ( iNode = 0; iNode < nbn; iNode++ ) {
4816 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4817 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4818 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4819 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4820 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4821 // check if a link is free
4822 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4823 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4824 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4825 hasFreeLinks = true;
4826 // make an edge and a ceiling for a new edge
4828 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4829 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4830 srcElements.Append( elem );
4832 n1 = vecNewNodes[ iNode ]->second.back();
4833 n2 = vecNewNodes[ iNext ]->second.back();
4834 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4835 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4836 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4837 srcElements.Append( elem );
4841 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4842 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4846 // sweep free links into faces
4848 if ( hasFreeLinks ) {
4849 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4850 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4852 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4853 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4854 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4855 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4856 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4858 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4859 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4860 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4862 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4863 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4864 std::advance( v, volNb );
4865 // find indices of free faces of a volume and their source edges
4866 list< int > freeInd;
4867 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4868 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4869 int iF, nbF = vTool.NbFaces();
4870 for ( iF = 0; iF < nbF; iF ++ ) {
4871 if (vTool.IsFreeFace( iF ) &&
4872 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4873 initNodeSet != faceNodeSet) // except an initial face
4875 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4877 if ( faceNodeSet == initNodeSetNoCenter )
4879 freeInd.push_back( iF );
4880 // find source edge of a free face iF
4881 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4882 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4883 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4884 initNodeSet.begin(), initNodeSet.end(),
4885 commonNodes.begin());
4886 if ( (*v)->IsQuadratic() )
4887 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4889 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4891 if ( !srcEdges.back() )
4893 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4894 << iF << " of volume #" << vTool.ID() << endl;
4899 if ( freeInd.empty() )
4902 // create faces for all steps;
4903 // if such a face has been already created by sweep of edge,
4904 // assure that its orientation is OK
4905 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4906 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4907 vTool.SetExternalNormal();
4908 const int nextShift = vTool.IsForward() ? +1 : -1;
4909 list< int >::iterator ind = freeInd.begin();
4910 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4911 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4913 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4914 int nbn = vTool.NbFaceNodes( *ind );
4915 const SMDS_MeshElement * f = 0;
4916 if ( nbn == 3 ) ///// triangle
4918 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4920 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4922 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4924 nodes[ 1 + nextShift ] };
4926 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4928 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4932 else if ( nbn == 4 ) ///// quadrangle
4934 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4936 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4938 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4939 nodes[ 2 ], nodes[ 2+nextShift ] };
4941 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4943 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4944 newOrder[ 2 ], newOrder[ 3 ]));
4947 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4949 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4951 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4953 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4955 nodes[2 + 2*nextShift],
4956 nodes[3 - 2*nextShift],
4958 nodes[3 + 2*nextShift]};
4960 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4962 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4970 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4972 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4973 nodes[1], nodes[3], nodes[5], nodes[7] );
4975 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4977 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4978 nodes[4 - 2*nextShift],
4980 nodes[4 + 2*nextShift],
4982 nodes[5 - 2*nextShift],
4984 nodes[5 + 2*nextShift] };
4986 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4988 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4989 newOrder[ 2 ], newOrder[ 3 ],
4990 newOrder[ 4 ], newOrder[ 5 ],
4991 newOrder[ 6 ], newOrder[ 7 ]));
4994 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4996 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4997 SMDSAbs_Face, /*noMedium=*/false);
4999 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5001 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5002 nodes[4 - 2*nextShift],
5004 nodes[4 + 2*nextShift],
5006 nodes[5 - 2*nextShift],
5008 nodes[5 + 2*nextShift],
5011 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5013 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5014 newOrder[ 2 ], newOrder[ 3 ],
5015 newOrder[ 4 ], newOrder[ 5 ],
5016 newOrder[ 6 ], newOrder[ 7 ],
5020 else //////// polygon
5022 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5023 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5025 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5027 if ( !vTool.IsForward() )
5028 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5030 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5032 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5036 while ( srcElements.Length() < myLastCreatedElems.Length() )
5037 srcElements.Append( *srcEdge );
5039 } // loop on free faces
5041 // go to the next volume
5043 while ( iVol++ < nbVolumesByStep ) v++;
5046 } // loop on volumes of one step
5047 } // sweep free links into faces
5049 // Make a ceiling face with a normal external to a volume
5051 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5052 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5053 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5055 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5056 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5057 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5060 lastVol.SetExternalNormal();
5061 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5062 int nbn = lastVol.NbFaceNodes( iF );
5063 // we do not use this->AddElement() because nodes are interlaced
5064 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5065 if ( !hasFreeLinks ||
5066 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5069 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5071 else if ( nbn == 4 )
5072 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5074 else if ( nbn == 6 && isQuadratic )
5075 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5076 nodes[1], nodes[3], nodes[5]));
5077 else if ( nbn == 7 && isQuadratic )
5078 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5079 nodes[1], nodes[3], nodes[5], nodes[6]));
5080 else if ( nbn == 8 && isQuadratic )
5081 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5082 nodes[1], nodes[3], nodes[5], nodes[7]));
5083 else if ( nbn == 9 && isQuadratic )
5084 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5085 nodes[1], nodes[3], nodes[5], nodes[7],
5088 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5090 while ( srcElements.Length() < myLastCreatedElems.Length() )
5091 srcElements.Append( elem );
5094 } // loop on swept elements
5097 //=======================================================================
5098 //function : RotationSweep
5100 //=======================================================================
5102 SMESH_MeshEditor::PGroupIDs
5103 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5104 const gp_Ax1& theAxis,
5105 const double theAngle,
5106 const int theNbSteps,
5107 const double theTol,
5108 const bool theMakeGroups,
5109 const bool theMakeWalls)
5111 myLastCreatedElems.Clear();
5112 myLastCreatedNodes.Clear();
5114 // source elements for each generated one
5115 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5117 MESSAGE( "RotationSweep()");
5119 aTrsf.SetRotation( theAxis, theAngle );
5121 aTrsf2.SetRotation( theAxis, theAngle/2. );
5123 gp_Lin aLine( theAxis );
5124 double aSqTol = theTol * theTol;
5126 SMESHDS_Mesh* aMesh = GetMeshDS();
5128 TNodeOfNodeListMap mapNewNodes;
5129 TElemOfVecOfNnlmiMap mapElemNewNodes;
5130 TTElemOfElemListMap newElemsMap;
5132 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5133 myMesh->NbFaces(ORDER_QUADRATIC) +
5134 myMesh->NbVolumes(ORDER_QUADRATIC) );
5136 TIDSortedElemSet::iterator itElem;
5137 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5138 const SMDS_MeshElement* elem = *itElem;
5139 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5141 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5142 newNodesItVec.reserve( elem->NbNodes() );
5144 // loop on elem nodes
5145 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5146 while ( itN->more() )
5148 // check if a node has been already sweeped
5149 const SMDS_MeshNode* node = cast2Node( itN->next() );
5151 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5153 aXYZ.Coord( coord[0], coord[1], coord[2] );
5154 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5156 TNodeOfNodeListMapItr nIt =
5157 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5158 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5159 if ( listNewNodes.empty() )
5161 // check if we are to create medium nodes between corner ones
5162 bool needMediumNodes = false;
5163 if ( isQuadraticMesh )
5165 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5166 while (it->more() && !needMediumNodes )
5168 const SMDS_MeshElement* invElem = it->next();
5169 if ( invElem != elem && !theElems.count( invElem )) continue;
5170 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5171 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5172 needMediumNodes = true;
5177 const SMDS_MeshNode * newNode = node;
5178 for ( int i = 0; i < theNbSteps; i++ ) {
5180 if ( needMediumNodes ) // create a medium node
5182 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5183 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5184 myLastCreatedNodes.Append(newNode);
5185 srcNodes.Append( node );
5186 listNewNodes.push_back( newNode );
5187 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5190 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5192 // create a corner node
5193 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5194 myLastCreatedNodes.Append(newNode);
5195 srcNodes.Append( node );
5196 listNewNodes.push_back( newNode );
5199 listNewNodes.push_back( newNode );
5200 // if ( needMediumNodes )
5201 // listNewNodes.push_back( newNode );
5205 newNodesItVec.push_back( nIt );
5207 // make new elements
5208 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5212 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5214 PGroupIDs newGroupIDs;
5215 if ( theMakeGroups )
5216 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5222 //=======================================================================
5223 //function : CreateNode
5225 //=======================================================================
5226 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5229 const double tolnode,
5230 SMESH_SequenceOfNode& aNodes)
5232 // myLastCreatedElems.Clear();
5233 // myLastCreatedNodes.Clear();
5236 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5238 // try to search in sequence of existing nodes
5239 // if aNodes.Length()>0 we 'nave to use given sequence
5240 // else - use all nodes of mesh
5241 if(aNodes.Length()>0) {
5243 for(i=1; i<=aNodes.Length(); i++) {
5244 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5245 if(P1.Distance(P2)<tolnode)
5246 return aNodes.Value(i);
5250 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5251 while(itn->more()) {
5252 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5253 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5254 if(P1.Distance(P2)<tolnode)
5259 // create new node and return it
5260 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5261 //myLastCreatedNodes.Append(NewNode);
5266 //=======================================================================
5267 //function : ExtrusionSweep
5269 //=======================================================================
5271 SMESH_MeshEditor::PGroupIDs
5272 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5273 const gp_Vec& theStep,
5274 const int theNbSteps,
5275 TTElemOfElemListMap& newElemsMap,
5276 const bool theMakeGroups,
5278 const double theTolerance)
5280 ExtrusParam aParams;
5281 aParams.myDir = gp_Dir(theStep);
5282 aParams.myNodes.Clear();
5283 aParams.mySteps = new TColStd_HSequenceOfReal;
5285 for(i=1; i<=theNbSteps; i++)
5286 aParams.mySteps->Append(theStep.Magnitude());
5289 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5293 //=======================================================================
5294 //function : ExtrusionSweep
5296 //=======================================================================
5298 SMESH_MeshEditor::PGroupIDs
5299 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5300 ExtrusParam& theParams,
5301 TTElemOfElemListMap& newElemsMap,
5302 const bool theMakeGroups,
5304 const double theTolerance)
5306 myLastCreatedElems.Clear();
5307 myLastCreatedNodes.Clear();
5309 // source elements for each generated one
5310 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5312 SMESHDS_Mesh* aMesh = GetMeshDS();
5314 int nbsteps = theParams.mySteps->Length();
5316 TNodeOfNodeListMap mapNewNodes;
5317 //TNodeOfNodeVecMap mapNewNodes;
5318 TElemOfVecOfNnlmiMap mapElemNewNodes;
5319 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5321 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5322 myMesh->NbFaces(ORDER_QUADRATIC) +
5323 myMesh->NbVolumes(ORDER_QUADRATIC) );
5325 TIDSortedElemSet::iterator itElem;
5326 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5327 // check element type
5328 const SMDS_MeshElement* elem = *itElem;
5329 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5332 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5333 newNodesItVec.reserve( elem->NbNodes() );
5335 // loop on elem nodes
5336 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5337 while ( itN->more() )
5339 // check if a node has been already sweeped
5340 const SMDS_MeshNode* node = cast2Node( itN->next() );
5341 TNodeOfNodeListMap::iterator nIt =
5342 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5343 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5344 if ( listNewNodes.empty() )
5348 // check if we are to create medium nodes between corner ones
5349 bool needMediumNodes = false;
5350 if ( isQuadraticMesh )
5352 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5353 while (it->more() && !needMediumNodes )
5355 const SMDS_MeshElement* invElem = it->next();
5356 if ( invElem != elem && !theElems.count( invElem )) continue;
5357 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5358 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5359 needMediumNodes = true;
5363 double coord[] = { node->X(), node->Y(), node->Z() };
5364 for ( int i = 0; i < nbsteps; i++ )
5366 if ( needMediumNodes ) // create a medium node
5368 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5369 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5370 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5371 if( theFlags & EXTRUSION_FLAG_SEW ) {
5372 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5373 theTolerance, theParams.myNodes);
5374 listNewNodes.push_back( newNode );
5377 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5378 myLastCreatedNodes.Append(newNode);
5379 srcNodes.Append( node );
5380 listNewNodes.push_back( newNode );
5383 // create a corner node
5384 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5385 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5386 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5387 if( theFlags & EXTRUSION_FLAG_SEW ) {
5388 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5389 theTolerance, theParams.myNodes);
5390 listNewNodes.push_back( newNode );
5393 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5394 myLastCreatedNodes.Append(newNode);
5395 srcNodes.Append( node );
5396 listNewNodes.push_back( newNode );
5400 newNodesItVec.push_back( nIt );
5402 // make new elements
5403 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5406 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5407 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5409 PGroupIDs newGroupIDs;
5410 if ( theMakeGroups )
5411 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5416 //=======================================================================
5417 //function : ExtrusionAlongTrack
5419 //=======================================================================
5420 SMESH_MeshEditor::Extrusion_Error
5421 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5422 SMESH_subMesh* theTrack,
5423 const SMDS_MeshNode* theN1,
5424 const bool theHasAngles,
5425 list<double>& theAngles,
5426 const bool theLinearVariation,
5427 const bool theHasRefPoint,
5428 const gp_Pnt& theRefPoint,
5429 const bool theMakeGroups)
5431 MESSAGE("ExtrusionAlongTrack");
5432 myLastCreatedElems.Clear();
5433 myLastCreatedNodes.Clear();
5436 std::list<double> aPrms;
5437 TIDSortedElemSet::iterator itElem;
5440 TopoDS_Edge aTrackEdge;
5441 TopoDS_Vertex aV1, aV2;
5443 SMDS_ElemIteratorPtr aItE;
5444 SMDS_NodeIteratorPtr aItN;
5445 SMDSAbs_ElementType aTypeE;
5447 TNodeOfNodeListMap mapNewNodes;
5450 aNbE = theElements.size();
5453 return EXTR_NO_ELEMENTS;
5455 // 1.1 Track Pattern
5458 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5460 aItE = pSubMeshDS->GetElements();
5461 while ( aItE->more() ) {
5462 const SMDS_MeshElement* pE = aItE->next();
5463 aTypeE = pE->GetType();
5464 // Pattern must contain links only
5465 if ( aTypeE != SMDSAbs_Edge )
5466 return EXTR_PATH_NOT_EDGE;
5469 list<SMESH_MeshEditor_PathPoint> fullList;
5471 const TopoDS_Shape& aS = theTrack->GetSubShape();
5472 // Sub-shape for the Pattern must be an Edge or Wire
5473 if( aS.ShapeType() == TopAbs_EDGE ) {
5474 aTrackEdge = TopoDS::Edge( aS );
5475 // the Edge must not be degenerated
5476 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5477 return EXTR_BAD_PATH_SHAPE;
5478 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5479 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5480 const SMDS_MeshNode* aN1 = aItN->next();
5481 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5482 const SMDS_MeshNode* aN2 = aItN->next();
5483 // starting node must be aN1 or aN2
5484 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5485 return EXTR_BAD_STARTING_NODE;
5486 aItN = pSubMeshDS->GetNodes();
5487 while ( aItN->more() ) {
5488 const SMDS_MeshNode* pNode = aItN->next();
5489 const SMDS_EdgePosition* pEPos =
5490 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5491 double aT = pEPos->GetUParameter();
5492 aPrms.push_back( aT );
5494 //Extrusion_Error err =
5495 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5496 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5497 list< SMESH_subMesh* > LSM;
5498 TopTools_SequenceOfShape Edges;
5499 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5500 while(itSM->more()) {
5501 SMESH_subMesh* SM = itSM->next();
5503 const TopoDS_Shape& aS = SM->GetSubShape();
5506 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5507 int startNid = theN1->GetID();
5508 TColStd_MapOfInteger UsedNums;
5510 int NbEdges = Edges.Length();
5512 for(; i<=NbEdges; i++) {
5514 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5515 for(; itLSM!=LSM.end(); itLSM++) {
5517 if(UsedNums.Contains(k)) continue;
5518 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5519 SMESH_subMesh* locTrack = *itLSM;
5520 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5521 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5522 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5523 const SMDS_MeshNode* aN1 = aItN->next();
5524 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5525 const SMDS_MeshNode* aN2 = aItN->next();
5526 // starting node must be aN1 or aN2
5527 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5528 // 2. Collect parameters on the track edge
5530 aItN = locMeshDS->GetNodes();
5531 while ( aItN->more() ) {
5532 const SMDS_MeshNode* pNode = aItN->next();
5533 const SMDS_EdgePosition* pEPos =
5534 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5535 double aT = pEPos->GetUParameter();
5536 aPrms.push_back( aT );
5538 list<SMESH_MeshEditor_PathPoint> LPP;
5539 //Extrusion_Error err =
5540 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5541 LLPPs.push_back(LPP);
5543 // update startN for search following egde
5544 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5545 else startNid = aN1->GetID();
5549 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5550 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5551 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5552 for(; itPP!=firstList.end(); itPP++) {
5553 fullList.push_back( *itPP );
5555 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5556 fullList.pop_back();
5558 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5559 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5560 itPP = currList.begin();
5561 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5562 gp_Dir D1 = PP1.Tangent();
5563 gp_Dir D2 = PP2.Tangent();
5564 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5565 (D1.Z()+D2.Z())/2 ) );
5566 PP1.SetTangent(Dnew);
5567 fullList.push_back(PP1);
5569 for(; itPP!=firstList.end(); itPP++) {
5570 fullList.push_back( *itPP );
5572 PP1 = fullList.back();
5573 fullList.pop_back();
5575 // if wire not closed
5576 fullList.push_back(PP1);
5580 return EXTR_BAD_PATH_SHAPE;
5583 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5584 theHasRefPoint, theRefPoint, theMakeGroups);
5588 //=======================================================================
5589 //function : ExtrusionAlongTrack
5591 //=======================================================================
5592 SMESH_MeshEditor::Extrusion_Error
5593 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5594 SMESH_Mesh* theTrack,
5595 const SMDS_MeshNode* theN1,
5596 const bool theHasAngles,
5597 list<double>& theAngles,
5598 const bool theLinearVariation,
5599 const bool theHasRefPoint,
5600 const gp_Pnt& theRefPoint,
5601 const bool theMakeGroups)
5603 myLastCreatedElems.Clear();
5604 myLastCreatedNodes.Clear();
5607 std::list<double> aPrms;
5608 TIDSortedElemSet::iterator itElem;
5611 TopoDS_Edge aTrackEdge;
5612 TopoDS_Vertex aV1, aV2;
5614 SMDS_ElemIteratorPtr aItE;
5615 SMDS_NodeIteratorPtr aItN;
5616 SMDSAbs_ElementType aTypeE;
5618 TNodeOfNodeListMap mapNewNodes;
5621 aNbE = theElements.size();
5624 return EXTR_NO_ELEMENTS;
5626 // 1.1 Track Pattern
5629 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5631 aItE = pMeshDS->elementsIterator();
5632 while ( aItE->more() ) {
5633 const SMDS_MeshElement* pE = aItE->next();
5634 aTypeE = pE->GetType();
5635 // Pattern must contain links only
5636 if ( aTypeE != SMDSAbs_Edge )
5637 return EXTR_PATH_NOT_EDGE;
5640 list<SMESH_MeshEditor_PathPoint> fullList;
5642 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5644 if ( !theTrack->HasShapeToMesh() ) {
5645 //Mesh without shape
5646 const SMDS_MeshNode* currentNode = NULL;
5647 const SMDS_MeshNode* prevNode = theN1;
5648 std::vector<const SMDS_MeshNode*> aNodesList;
5649 aNodesList.push_back(theN1);
5650 int nbEdges = 0, conn=0;
5651 const SMDS_MeshElement* prevElem = NULL;
5652 const SMDS_MeshElement* currentElem = NULL;
5653 int totalNbEdges = theTrack->NbEdges();
5654 SMDS_ElemIteratorPtr nIt;
5657 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5658 return EXTR_BAD_STARTING_NODE;
5661 conn = nbEdgeConnectivity(theN1);
5663 return EXTR_PATH_NOT_EDGE;
5665 aItE = theN1->GetInverseElementIterator();
5666 prevElem = aItE->next();
5667 currentElem = prevElem;
5669 if(totalNbEdges == 1 ) {
5670 nIt = currentElem->nodesIterator();
5671 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5672 if(currentNode == prevNode)
5673 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5674 aNodesList.push_back(currentNode);
5676 nIt = currentElem->nodesIterator();
5677 while( nIt->more() ) {
5678 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5679 if(currentNode == prevNode)
5680 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5681 aNodesList.push_back(currentNode);
5683 //case of the closed mesh
5684 if(currentNode == theN1) {
5689 conn = nbEdgeConnectivity(currentNode);
5691 return EXTR_PATH_NOT_EDGE;
5692 }else if( conn == 1 && nbEdges > 0 ) {
5697 prevNode = currentNode;
5698 aItE = currentNode->GetInverseElementIterator();
5699 currentElem = aItE->next();
5700 if( currentElem == prevElem)
5701 currentElem = aItE->next();
5702 nIt = currentElem->nodesIterator();
5703 prevElem = currentElem;
5709 if(nbEdges != totalNbEdges)
5710 return EXTR_PATH_NOT_EDGE;
5712 TopTools_SequenceOfShape Edges;
5713 double x1,x2,y1,y2,z1,z2;
5714 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5715 int startNid = theN1->GetID();
5716 for(int i = 1; i < aNodesList.size(); i++) {
5717 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5718 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5719 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5720 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5721 list<SMESH_MeshEditor_PathPoint> LPP;
5723 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5724 LLPPs.push_back(LPP);
5725 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5726 else startNid = aNodesList[i-1]->GetID();
5730 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5731 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5732 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5733 for(; itPP!=firstList.end(); itPP++) {
5734 fullList.push_back( *itPP );
5737 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5738 SMESH_MeshEditor_PathPoint PP2;
5739 fullList.pop_back();
5741 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5742 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5743 itPP = currList.begin();
5744 PP2 = currList.front();
5745 gp_Dir D1 = PP1.Tangent();
5746 gp_Dir D2 = PP2.Tangent();
5747 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5748 (D1.Z()+D2.Z())/2 ) );
5749 PP1.SetTangent(Dnew);
5750 fullList.push_back(PP1);
5752 for(; itPP!=currList.end(); itPP++) {
5753 fullList.push_back( *itPP );
5755 PP1 = fullList.back();
5756 fullList.pop_back();
5758 fullList.push_back(PP1);
5760 } // Sub-shape for the Pattern must be an Edge or Wire
5761 else if( aS.ShapeType() == TopAbs_EDGE ) {
5762 aTrackEdge = TopoDS::Edge( aS );
5763 // the Edge must not be degenerated
5764 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5765 return EXTR_BAD_PATH_SHAPE;
5766 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5767 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5768 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5769 // starting node must be aN1 or aN2
5770 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5771 return EXTR_BAD_STARTING_NODE;
5772 aItN = pMeshDS->nodesIterator();
5773 while ( aItN->more() ) {
5774 const SMDS_MeshNode* pNode = aItN->next();
5775 if( pNode==aN1 || pNode==aN2 ) continue;
5776 const SMDS_EdgePosition* pEPos =
5777 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5778 double aT = pEPos->GetUParameter();
5779 aPrms.push_back( aT );
5781 //Extrusion_Error err =
5782 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5784 else if( aS.ShapeType() == TopAbs_WIRE ) {
5785 list< SMESH_subMesh* > LSM;
5786 TopTools_SequenceOfShape Edges;
5787 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5788 for(; eExp.More(); eExp.Next()) {
5789 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5790 if( SMESH_Algo::isDegenerated(E) ) continue;
5791 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5797 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5798 TopoDS_Vertex aVprev;
5799 TColStd_MapOfInteger UsedNums;
5800 int NbEdges = Edges.Length();
5802 for(; i<=NbEdges; i++) {
5804 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5805 for(; itLSM!=LSM.end(); itLSM++) {
5807 if(UsedNums.Contains(k)) continue;
5808 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5809 SMESH_subMesh* locTrack = *itLSM;
5810 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5811 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5812 bool aN1isOK = false, aN2isOK = false;
5813 if ( aVprev.IsNull() ) {
5814 // if previous vertex is not yet defined, it means that we in the beginning of wire
5815 // and we have to find initial vertex corresponding to starting node theN1
5816 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5817 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5818 // starting node must be aN1 or aN2
5819 aN1isOK = ( aN1 && aN1 == theN1 );
5820 aN2isOK = ( aN2 && aN2 == theN1 );
5823 // we have specified ending vertex of the previous edge on the previous iteration
5824 // and we have just to check that it corresponds to any vertex in current segment
5825 aN1isOK = aVprev.IsSame( aV1 );
5826 aN2isOK = aVprev.IsSame( aV2 );
5828 if ( !aN1isOK && !aN2isOK ) continue;
5829 // 2. Collect parameters on the track edge
5831 aItN = locMeshDS->GetNodes();
5832 while ( aItN->more() ) {
5833 const SMDS_MeshNode* pNode = aItN->next();
5834 const SMDS_EdgePosition* pEPos =
5835 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5836 double aT = pEPos->GetUParameter();
5837 aPrms.push_back( aT );
5839 list<SMESH_MeshEditor_PathPoint> LPP;
5840 //Extrusion_Error err =
5841 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5842 LLPPs.push_back(LPP);
5844 // update startN for search following egde
5845 if ( aN1isOK ) aVprev = aV2;
5850 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5851 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5852 fullList.splice( fullList.end(), firstList );
5854 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5855 fullList.pop_back();
5857 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5858 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5859 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5860 gp_Dir D1 = PP1.Tangent();
5861 gp_Dir D2 = PP2.Tangent();
5862 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5863 PP1.SetTangent(Dnew);
5864 fullList.push_back(PP1);
5865 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5866 PP1 = fullList.back();
5867 fullList.pop_back();
5869 // if wire not closed
5870 fullList.push_back(PP1);
5874 return EXTR_BAD_PATH_SHAPE;
5877 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5878 theHasRefPoint, theRefPoint, theMakeGroups);
5882 //=======================================================================
5883 //function : MakeEdgePathPoints
5884 //purpose : auxilary for ExtrusionAlongTrack
5885 //=======================================================================
5886 SMESH_MeshEditor::Extrusion_Error
5887 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5888 const TopoDS_Edge& aTrackEdge,
5890 list<SMESH_MeshEditor_PathPoint>& LPP)
5892 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5894 aTolVec2=aTolVec*aTolVec;
5896 TopoDS_Vertex aV1, aV2;
5897 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5898 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5899 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5900 // 2. Collect parameters on the track edge
5901 aPrms.push_front( aT1 );
5902 aPrms.push_back( aT2 );
5905 if( FirstIsStart ) {
5916 SMESH_MeshEditor_PathPoint aPP;
5917 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5918 std::list<double>::iterator aItD = aPrms.begin();
5919 for(; aItD != aPrms.end(); ++aItD) {
5923 aC3D->D1( aT, aP3D, aVec );
5924 aL2 = aVec.SquareMagnitude();
5925 if ( aL2 < aTolVec2 )
5926 return EXTR_CANT_GET_TANGENT;
5927 gp_Dir aTgt( aVec );
5929 aPP.SetTangent( aTgt );
5930 aPP.SetParameter( aT );
5937 //=======================================================================
5938 //function : MakeExtrElements
5939 //purpose : auxilary for ExtrusionAlongTrack
5940 //=======================================================================
5941 SMESH_MeshEditor::Extrusion_Error
5942 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5943 list<SMESH_MeshEditor_PathPoint>& fullList,
5944 const bool theHasAngles,
5945 list<double>& theAngles,
5946 const bool theLinearVariation,
5947 const bool theHasRefPoint,
5948 const gp_Pnt& theRefPoint,
5949 const bool theMakeGroups)
5951 const int aNbTP = fullList.size();
5953 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5954 LinearAngleVariation(aNbTP-1, theAngles);
5955 // fill vector of path points with angles
5956 vector<SMESH_MeshEditor_PathPoint> aPPs;
5957 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5958 list<double>::iterator itAngles = theAngles.begin();
5959 aPPs.push_back( *itPP++ );
5960 for( ; itPP != fullList.end(); itPP++) {
5961 aPPs.push_back( *itPP );
5962 if ( theHasAngles && itAngles != theAngles.end() )
5963 aPPs.back().SetAngle( *itAngles++ );
5966 TNodeOfNodeListMap mapNewNodes;
5967 TElemOfVecOfNnlmiMap mapElemNewNodes;
5968 TTElemOfElemListMap newElemsMap;
5969 TIDSortedElemSet::iterator itElem;
5970 // source elements for each generated one
5971 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5973 // 3. Center of rotation aV0
5974 gp_Pnt aV0 = theRefPoint;
5975 if ( !theHasRefPoint )
5977 gp_XYZ aGC( 0.,0.,0. );
5978 TIDSortedElemSet newNodes;
5980 itElem = theElements.begin();
5981 for ( ; itElem != theElements.end(); itElem++ ) {
5982 const SMDS_MeshElement* elem = *itElem;
5984 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5985 while ( itN->more() ) {
5986 const SMDS_MeshElement* node = itN->next();
5987 if ( newNodes.insert( node ).second )
5988 aGC += SMESH_TNodeXYZ( node );
5991 aGC /= newNodes.size();
5993 } // if (!theHasRefPoint) {
5995 // 4. Processing the elements
5996 SMESHDS_Mesh* aMesh = GetMeshDS();
5998 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5999 // check element type
6000 const SMDS_MeshElement* elem = *itElem;
6001 SMDSAbs_ElementType aTypeE = elem->GetType();
6002 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6005 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6006 newNodesItVec.reserve( elem->NbNodes() );
6008 // loop on elem nodes
6010 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6011 while ( itN->more() )
6014 // check if a node has been already processed
6015 const SMDS_MeshNode* node =
6016 static_cast<const SMDS_MeshNode*>( itN->next() );
6017 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6018 if ( nIt == mapNewNodes.end() ) {
6019 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6020 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6023 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6024 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6025 gp_Ax1 anAx1, anAxT1T0;
6026 gp_Dir aDT1x, aDT0x, aDT1T0;
6031 aPN0 = SMESH_TNodeXYZ( node );
6033 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6035 aDT0x= aPP0.Tangent();
6036 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6038 for ( int j = 1; j < aNbTP; ++j ) {
6039 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6041 aDT1x = aPP1.Tangent();
6042 aAngle1x = aPP1.Angle();
6044 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6046 gp_Vec aV01x( aP0x, aP1x );
6047 aTrsf.SetTranslation( aV01x );
6050 aV1x = aV0x.Transformed( aTrsf );
6051 aPN1 = aPN0.Transformed( aTrsf );
6053 // rotation 1 [ T1,T0 ]
6054 aAngleT1T0=-aDT1x.Angle( aDT0x );
6055 if (fabs(aAngleT1T0) > aTolAng) {
6057 anAxT1T0.SetLocation( aV1x );
6058 anAxT1T0.SetDirection( aDT1T0 );
6059 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6061 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6065 if ( theHasAngles ) {
6066 anAx1.SetLocation( aV1x );
6067 anAx1.SetDirection( aDT1x );
6068 aTrsfRot.SetRotation( anAx1, aAngle1x );
6070 aPN1 = aPN1.Transformed( aTrsfRot );
6074 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6075 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6076 // create additional node
6077 double x = ( aPN1.X() + aPN0.X() )/2.;
6078 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6079 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6080 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6081 myLastCreatedNodes.Append(newNode);
6082 srcNodes.Append( node );
6083 listNewNodes.push_back( newNode );
6085 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6086 myLastCreatedNodes.Append(newNode);
6087 srcNodes.Append( node );
6088 listNewNodes.push_back( newNode );
6098 // if current elem is quadratic and current node is not medium
6099 // we have to check - may be it is needed to insert additional nodes
6100 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6101 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6102 if(listNewNodes.size()==aNbTP-1) {
6103 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6104 gp_XYZ P(node->X(), node->Y(), node->Z());
6105 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6107 for(i=0; i<aNbTP-1; i++) {
6108 const SMDS_MeshNode* N = *it;
6109 double x = ( N->X() + P.X() )/2.;
6110 double y = ( N->Y() + P.Y() )/2.;
6111 double z = ( N->Z() + P.Z() )/2.;
6112 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6113 srcNodes.Append( node );
6114 myLastCreatedNodes.Append(newN);
6117 P = gp_XYZ(N->X(),N->Y(),N->Z());
6119 listNewNodes.clear();
6120 for(i=0; i<2*(aNbTP-1); i++) {
6121 listNewNodes.push_back(aNodes[i]);
6127 newNodesItVec.push_back( nIt );
6129 // make new elements
6130 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6131 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6132 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6135 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6137 if ( theMakeGroups )
6138 generateGroups( srcNodes, srcElems, "extruded");
6144 //=======================================================================
6145 //function : LinearAngleVariation
6146 //purpose : auxilary for ExtrusionAlongTrack
6147 //=======================================================================
6148 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6149 list<double>& Angles)
6151 int nbAngles = Angles.size();
6152 if( nbSteps > nbAngles ) {
6153 vector<double> theAngles(nbAngles);
6154 list<double>::iterator it = Angles.begin();
6156 for(; it!=Angles.end(); it++) {
6158 theAngles[i] = (*it);
6161 double rAn2St = double( nbAngles ) / double( nbSteps );
6162 double angPrev = 0, angle;
6163 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6164 double angCur = rAn2St * ( iSt+1 );
6165 double angCurFloor = floor( angCur );
6166 double angPrevFloor = floor( angPrev );
6167 if ( angPrevFloor == angCurFloor )
6168 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6170 int iP = int( angPrevFloor );
6171 double angPrevCeil = ceil(angPrev);
6172 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6174 int iC = int( angCurFloor );
6175 if ( iC < nbAngles )
6176 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6178 iP = int( angPrevCeil );
6180 angle += theAngles[ iC ];
6182 res.push_back(angle);
6187 for(; it!=res.end(); it++)
6188 Angles.push_back( *it );
6193 //================================================================================
6195 * \brief Move or copy theElements applying theTrsf to their nodes
6196 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6197 * \param theTrsf - transformation to apply
6198 * \param theCopy - if true, create translated copies of theElems
6199 * \param theMakeGroups - if true and theCopy, create translated groups
6200 * \param theTargetMesh - mesh to copy translated elements into
6201 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6203 //================================================================================
6205 SMESH_MeshEditor::PGroupIDs
6206 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6207 const gp_Trsf& theTrsf,
6209 const bool theMakeGroups,
6210 SMESH_Mesh* theTargetMesh)
6212 myLastCreatedElems.Clear();
6213 myLastCreatedNodes.Clear();
6215 bool needReverse = false;
6216 string groupPostfix;
6217 switch ( theTrsf.Form() ) {
6219 MESSAGE("gp_PntMirror");
6221 groupPostfix = "mirrored";
6224 MESSAGE("gp_Ax1Mirror");
6225 groupPostfix = "mirrored";
6228 MESSAGE("gp_Ax2Mirror");
6230 groupPostfix = "mirrored";
6233 MESSAGE("gp_Rotation");
6234 groupPostfix = "rotated";
6236 case gp_Translation:
6237 MESSAGE("gp_Translation");
6238 groupPostfix = "translated";
6241 MESSAGE("gp_Scale");
6242 groupPostfix = "scaled";
6244 case gp_CompoundTrsf: // different scale by axis
6245 MESSAGE("gp_CompoundTrsf");
6246 groupPostfix = "scaled";
6250 needReverse = false;
6251 groupPostfix = "transformed";
6254 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6255 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6256 SMESHDS_Mesh* aMesh = GetMeshDS();
6259 // map old node to new one
6260 TNodeNodeMap nodeMap;
6262 // elements sharing moved nodes; those of them which have all
6263 // nodes mirrored but are not in theElems are to be reversed
6264 TIDSortedElemSet inverseElemSet;
6266 // source elements for each generated one
6267 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6269 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6270 TIDSortedElemSet orphanNode;
6272 if ( theElems.empty() ) // transform the whole mesh
6275 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6276 while ( eIt->more() ) theElems.insert( eIt->next() );
6278 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6279 while ( nIt->more() )
6281 const SMDS_MeshNode* node = nIt->next();
6282 if ( node->NbInverseElements() == 0)
6283 orphanNode.insert( node );
6287 // loop on elements to transform nodes : first orphan nodes then elems
6288 TIDSortedElemSet::iterator itElem;
6289 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6290 for (int i=0; i<2; i++)
6291 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6292 const SMDS_MeshElement* elem = *itElem;
6296 // loop on elem nodes
6297 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6298 while ( itN->more() ) {
6300 const SMDS_MeshNode* node = cast2Node( itN->next() );
6301 // check if a node has been already transformed
6302 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6303 nodeMap.insert( make_pair ( node, node ));
6304 if ( !n2n_isnew.second )
6308 coord[0] = node->X();
6309 coord[1] = node->Y();
6310 coord[2] = node->Z();
6311 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6312 if ( theTargetMesh ) {
6313 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6314 n2n_isnew.first->second = newNode;
6315 myLastCreatedNodes.Append(newNode);
6316 srcNodes.Append( node );
6318 else if ( theCopy ) {
6319 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6320 n2n_isnew.first->second = newNode;
6321 myLastCreatedNodes.Append(newNode);
6322 srcNodes.Append( node );
6325 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6326 // node position on shape becomes invalid
6327 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6328 ( SMDS_SpacePosition::originSpacePosition() );
6331 // keep inverse elements
6332 if ( !theCopy && !theTargetMesh && needReverse ) {
6333 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6334 while ( invElemIt->more() ) {
6335 const SMDS_MeshElement* iel = invElemIt->next();
6336 inverseElemSet.insert( iel );
6342 // either create new elements or reverse mirrored ones
6343 if ( !theCopy && !needReverse && !theTargetMesh )
6346 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6347 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6348 theElems.insert( *invElemIt );
6350 // Replicate or reverse elements
6352 std::vector<int> iForw;
6353 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6355 const SMDS_MeshElement* elem = *itElem;
6356 if ( !elem ) continue;
6358 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6359 int nbNodes = elem->NbNodes();
6360 if ( geomType == SMDSGeom_NONE ) continue; // node
6362 switch ( geomType ) {
6364 case SMDSGeom_POLYGON: // ---------------------- polygon
6366 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6368 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6369 while (itN->more()) {
6370 const SMDS_MeshNode* node =
6371 static_cast<const SMDS_MeshNode*>(itN->next());
6372 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6373 if (nodeMapIt == nodeMap.end())
6374 break; // not all nodes transformed
6376 // reverse mirrored faces and volumes
6377 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6379 poly_nodes[iNode] = (*nodeMapIt).second;
6383 if ( iNode != nbNodes )
6384 continue; // not all nodes transformed
6386 if ( theTargetMesh ) {
6387 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6388 srcElems.Append( elem );
6390 else if ( theCopy ) {
6391 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6392 srcElems.Append( elem );
6395 aMesh->ChangePolygonNodes(elem, poly_nodes);
6400 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6402 const SMDS_VtkVolume* aPolyedre =
6403 dynamic_cast<const SMDS_VtkVolume*>( elem );
6405 MESSAGE("Warning: bad volumic element");
6409 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6410 vector<int> quantities; quantities.reserve( nbNodes );
6412 bool allTransformed = true;
6413 int nbFaces = aPolyedre->NbFaces();
6414 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6415 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6416 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6417 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6418 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6419 if (nodeMapIt == nodeMap.end()) {
6420 allTransformed = false; // not all nodes transformed
6422 poly_nodes.push_back((*nodeMapIt).second);
6424 if ( needReverse && allTransformed )
6425 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6427 quantities.push_back(nbFaceNodes);
6429 if ( !allTransformed )
6430 continue; // not all nodes transformed
6432 if ( theTargetMesh ) {
6433 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6434 srcElems.Append( elem );
6436 else if ( theCopy ) {
6437 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6438 srcElems.Append( elem );
6441 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6446 case SMDSGeom_BALL: // -------------------- Ball
6448 if ( !theCopy && !theTargetMesh ) continue;
6450 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6451 if (nodeMapIt == nodeMap.end())
6452 continue; // not all nodes transformed
6454 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6455 if ( theTargetMesh ) {
6456 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6457 srcElems.Append( elem );
6460 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6461 srcElems.Append( elem );
6466 default: // ----------------------- Regular elements
6468 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6469 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6470 const std::vector<int>& i = needReverse ? iRev : iForw;
6472 // find transformed nodes
6473 vector<const SMDS_MeshNode*> nodes(nbNodes);
6475 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6476 while ( itN->more() ) {
6477 const SMDS_MeshNode* node =
6478 static_cast<const SMDS_MeshNode*>( itN->next() );
6479 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6480 if ( nodeMapIt == nodeMap.end() )
6481 break; // not all nodes transformed
6482 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6484 if ( iNode != nbNodes )
6485 continue; // not all nodes transformed
6487 if ( theTargetMesh ) {
6488 if ( SMDS_MeshElement* copy =
6489 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6490 myLastCreatedElems.Append( copy );
6491 srcElems.Append( elem );
6494 else if ( theCopy ) {
6495 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6496 srcElems.Append( elem );
6499 // reverse element as it was reversed by transformation
6501 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6503 } // switch ( geomType )
6505 } // loop on elements
6507 PGroupIDs newGroupIDs;
6509 if ( ( theMakeGroups && theCopy ) ||
6510 ( theMakeGroups && theTargetMesh ) )
6511 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6516 //=======================================================================
6518 * \brief Create groups of elements made during transformation
6519 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6520 * \param elemGens - elements making corresponding myLastCreatedElems
6521 * \param postfix - to append to names of new groups
6522 * \param targetMesh - mesh to create groups in
6523 * \param topPresent - is there "top" elements that are created by sweeping
6525 //=======================================================================
6527 SMESH_MeshEditor::PGroupIDs
6528 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6529 const SMESH_SequenceOfElemPtr& elemGens,
6530 const std::string& postfix,
6531 SMESH_Mesh* targetMesh,
6532 const bool topPresent)
6534 PGroupIDs newGroupIDs( new list<int> );
6535 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6537 // Sort existing groups by types and collect their names
6539 // containers to store an old group and generated new ones;
6540 // 1st new group is for result elems of different type than a source one;
6541 // 2nd new group is for same type result elems ("top" group at extrusion)
6543 using boost::make_tuple;
6544 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6545 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6546 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6548 set< string > groupNames;
6550 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6551 if ( !groupIt->more() ) return newGroupIDs;
6553 int newGroupID = mesh->GetGroupIds().back()+1;
6554 while ( groupIt->more() )
6556 SMESH_Group * group = groupIt->next();
6557 if ( !group ) continue;
6558 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6559 if ( !groupDS || groupDS->IsEmpty() ) continue;
6560 groupNames.insert ( group->GetName() );
6561 groupDS->SetStoreName( group->GetName() );
6562 const SMDSAbs_ElementType type = groupDS->GetType();
6563 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6564 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6565 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6566 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6569 // Loop on nodes and elements to add them in new groups
6571 vector< const SMDS_MeshElement* > resultElems;
6572 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6574 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6575 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6576 if ( gens.Length() != elems.Length() )
6577 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6579 // loop on created elements
6580 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6582 const SMDS_MeshElement* sourceElem = gens( iElem );
6583 if ( !sourceElem ) {
6584 MESSAGE("generateGroups(): NULL source element");
6587 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6588 if ( groupsOldNew.empty() ) { // no groups of this type at all
6589 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6590 ++iElem; // skip all elements made by sourceElem
6593 // collect all elements made by the iElem-th sourceElem
6594 resultElems.clear();
6595 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6596 if ( resElem != sourceElem )
6597 resultElems.push_back( resElem );
6598 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6599 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6600 if ( resElem != sourceElem )
6601 resultElems.push_back( resElem );
6603 const SMDS_MeshElement* topElem = 0;
6604 if ( isNodes ) // there must be a top element
6606 topElem = resultElems.back();
6607 resultElems.pop_back();
6611 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6612 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6613 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6615 topElem = *resElemIt;
6616 *resElemIt = 0; // erase *resElemIt
6620 // add resultElems to groups originted from ones the sourceElem belongs to
6621 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6622 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6624 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6625 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6627 // fill in a new group
6628 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6629 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6630 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6632 newGroup.Add( *resElemIt );
6634 // fill a "top" group
6637 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6638 newTopGroup.Add( topElem );
6642 } // loop on created elements
6643 }// loop on nodes and elements
6645 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6647 list<int> topGrouIds;
6648 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6650 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6651 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6652 orderedOldNewGroups[i]->get<2>() };
6653 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6655 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6656 if ( newGroupDS->IsEmpty() )
6658 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6663 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6666 const bool isTop = ( topPresent &&
6667 newGroupDS->GetType() == oldGroupDS->GetType() &&
6670 string name = oldGroupDS->GetStoreName();
6671 { // remove trailing whitespaces (issue 22599)
6672 size_t size = name.size();
6673 while ( size > 1 && isspace( name[ size-1 ]))
6675 if ( size != name.size() )
6677 name.resize( size );
6678 oldGroupDS->SetStoreName( name.c_str() );
6681 if ( !targetMesh ) {
6682 string suffix = ( isTop ? "top": postfix.c_str() );
6686 while ( !groupNames.insert( name ).second ) // name exists
6687 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6692 newGroupDS->SetStoreName( name.c_str() );
6694 // make a SMESH_Groups
6695 mesh->AddGroup( newGroupDS );
6697 topGrouIds.push_back( newGroupDS->GetID() );
6699 newGroupIDs->push_back( newGroupDS->GetID() );
6703 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6708 //================================================================================
6710 * \brief Return list of group of nodes close to each other within theTolerance
6711 * Search among theNodes or in the whole mesh if theNodes is empty using
6712 * an Octree algorithm
6714 //================================================================================
6716 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6717 const double theTolerance,
6718 TListOfListOfNodes & theGroupsOfNodes)
6720 myLastCreatedElems.Clear();
6721 myLastCreatedNodes.Clear();
6723 if ( theNodes.empty() )
6724 { // get all nodes in the mesh
6725 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6726 while ( nIt->more() )
6727 theNodes.insert( theNodes.end(),nIt->next());
6730 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6733 //=======================================================================
6734 //function : SimplifyFace
6736 //=======================================================================
6738 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6739 vector<const SMDS_MeshNode *>& poly_nodes,
6740 vector<int>& quantities) const
6742 int nbNodes = faceNodes.size();
6747 set<const SMDS_MeshNode*> nodeSet;
6749 // get simple seq of nodes
6750 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6751 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6752 int iSimple = 0, nbUnique = 0;
6754 simpleNodes[iSimple++] = faceNodes[0];
6756 for (int iCur = 1; iCur < nbNodes; iCur++) {
6757 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6758 simpleNodes[iSimple++] = faceNodes[iCur];
6759 if (nodeSet.insert( faceNodes[iCur] ).second)
6763 int nbSimple = iSimple;
6764 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6774 bool foundLoop = (nbSimple > nbUnique);
6777 set<const SMDS_MeshNode*> loopSet;
6778 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6779 const SMDS_MeshNode* n = simpleNodes[iSimple];
6780 if (!loopSet.insert( n ).second) {
6784 int iC = 0, curLast = iSimple;
6785 for (; iC < curLast; iC++) {
6786 if (simpleNodes[iC] == n) break;
6788 int loopLen = curLast - iC;
6790 // create sub-element
6792 quantities.push_back(loopLen);
6793 for (; iC < curLast; iC++) {
6794 poly_nodes.push_back(simpleNodes[iC]);
6797 // shift the rest nodes (place from the first loop position)
6798 for (iC = curLast + 1; iC < nbSimple; iC++) {
6799 simpleNodes[iC - loopLen] = simpleNodes[iC];
6801 nbSimple -= loopLen;
6804 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6805 } // while (foundLoop)
6809 quantities.push_back(iSimple);
6810 for (int i = 0; i < iSimple; i++)
6811 poly_nodes.push_back(simpleNodes[i]);
6817 //=======================================================================
6818 //function : MergeNodes
6819 //purpose : In each group, the cdr of nodes are substituted by the first one
6821 //=======================================================================
6823 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6825 MESSAGE("MergeNodes");
6826 myLastCreatedElems.Clear();
6827 myLastCreatedNodes.Clear();
6829 SMESHDS_Mesh* aMesh = GetMeshDS();
6831 TNodeNodeMap nodeNodeMap; // node to replace - new node
6832 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6833 list< int > rmElemIds, rmNodeIds;
6835 // Fill nodeNodeMap and elems
6837 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6838 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6839 list<const SMDS_MeshNode*>& nodes = *grIt;
6840 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6841 const SMDS_MeshNode* nToKeep = *nIt;
6842 //MESSAGE("node to keep " << nToKeep->GetID());
6843 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6844 const SMDS_MeshNode* nToRemove = *nIt;
6845 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6846 if ( nToRemove != nToKeep ) {
6847 //MESSAGE(" node to remove " << nToRemove->GetID());
6848 rmNodeIds.push_back( nToRemove->GetID() );
6849 AddToSameGroups( nToKeep, nToRemove, aMesh );
6850 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6851 // after MergeNodes() w/o creating node in place of merged ones.
6852 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6853 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6854 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6855 sm->SetIsAlwaysComputed( true );
6858 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6859 while ( invElemIt->more() ) {
6860 const SMDS_MeshElement* elem = invElemIt->next();
6865 // Change element nodes or remove an element
6867 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6868 for ( ; eIt != elems.end(); eIt++ ) {
6869 const SMDS_MeshElement* elem = *eIt;
6870 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6871 int nbNodes = elem->NbNodes();
6872 int aShapeId = FindShape( elem );
6874 set<const SMDS_MeshNode*> nodeSet;
6875 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6876 int iUnique = 0, iCur = 0, nbRepl = 0;
6877 vector<int> iRepl( nbNodes );
6879 // get new seq of nodes
6880 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6881 while ( itN->more() ) {
6882 const SMDS_MeshNode* n =
6883 static_cast<const SMDS_MeshNode*>( itN->next() );
6885 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6886 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6888 // BUG 0020185: begin
6890 bool stopRecur = false;
6891 set<const SMDS_MeshNode*> nodesRecur;
6892 nodesRecur.insert(n);
6893 while (!stopRecur) {
6894 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6895 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6896 n = (*nnIt_i).second;
6897 if (!nodesRecur.insert(n).second) {
6898 // error: recursive dependancy
6908 curNodes[ iCur ] = n;
6909 bool isUnique = nodeSet.insert( n ).second;
6911 uniqueNodes[ iUnique++ ] = n;
6913 iRepl[ nbRepl++ ] = iCur;
6917 // Analyse element topology after replacement
6920 int nbUniqueNodes = nodeSet.size();
6921 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6922 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6923 // Polygons and Polyhedral volumes
6924 if (elem->IsPoly()) {
6926 if (elem->GetType() == SMDSAbs_Face) {
6928 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6930 for (; inode < nbNodes; inode++) {
6931 face_nodes[inode] = curNodes[inode];
6934 vector<const SMDS_MeshNode *> polygons_nodes;
6935 vector<int> quantities;
6936 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6939 for (int iface = 0; iface < nbNew; iface++) {
6940 int nbNodes = quantities[iface];
6941 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6942 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6943 poly_nodes[ii] = polygons_nodes[inode];
6945 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6946 myLastCreatedElems.Append(newElem);
6948 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6951 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6952 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6953 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6955 if (nbNew > 0) quid = nbNew - 1;
6956 vector<int> newquant(quantities.begin()+quid, quantities.end());
6957 const SMDS_MeshElement* newElem = 0;
6958 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6959 myLastCreatedElems.Append(newElem);
6960 if ( aShapeId && newElem )
6961 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6962 rmElemIds.push_back(elem->GetID());
6965 rmElemIds.push_back(elem->GetID());
6969 else if (elem->GetType() == SMDSAbs_Volume) {
6970 // Polyhedral volume
6971 if (nbUniqueNodes < 4) {
6972 rmElemIds.push_back(elem->GetID());
6975 // each face has to be analyzed in order to check volume validity
6976 const SMDS_VtkVolume* aPolyedre =
6977 dynamic_cast<const SMDS_VtkVolume*>( elem );
6979 int nbFaces = aPolyedre->NbFaces();
6981 vector<const SMDS_MeshNode *> poly_nodes;
6982 vector<int> quantities;
6984 for (int iface = 1; iface <= nbFaces; iface++) {
6985 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6986 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6988 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6989 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6990 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6991 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6992 faceNode = (*nnIt).second;
6994 faceNodes[inode - 1] = faceNode;
6997 SimplifyFace(faceNodes, poly_nodes, quantities);
7000 if (quantities.size() > 3) {
7001 // to be done: remove coincident faces
7004 if (quantities.size() > 3)
7006 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7007 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7008 const SMDS_MeshElement* newElem = 0;
7009 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7010 myLastCreatedElems.Append(newElem);
7011 if ( aShapeId && newElem )
7012 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7013 rmElemIds.push_back(elem->GetID());
7017 rmElemIds.push_back(elem->GetID());
7028 // TODO not all the possible cases are solved. Find something more generic?
7029 switch ( nbNodes ) {
7030 case 2: ///////////////////////////////////// EDGE
7031 isOk = false; break;
7032 case 3: ///////////////////////////////////// TRIANGLE
7033 isOk = false; break;
7035 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7037 else { //////////////////////////////////// QUADRANGLE
7038 if ( nbUniqueNodes < 3 )
7040 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7041 isOk = false; // opposite nodes stick
7042 //MESSAGE("isOk " << isOk);
7045 case 6: ///////////////////////////////////// PENTAHEDRON
7046 if ( nbUniqueNodes == 4 ) {
7047 // ---------------------------------> tetrahedron
7049 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7050 // all top nodes stick: reverse a bottom
7051 uniqueNodes[ 0 ] = curNodes [ 1 ];
7052 uniqueNodes[ 1 ] = curNodes [ 0 ];
7054 else if (nbRepl == 3 &&
7055 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7056 // all bottom nodes stick: set a top before
7057 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7058 uniqueNodes[ 0 ] = curNodes [ 3 ];
7059 uniqueNodes[ 1 ] = curNodes [ 4 ];
7060 uniqueNodes[ 2 ] = curNodes [ 5 ];
7062 else if (nbRepl == 4 &&
7063 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7064 // a lateral face turns into a line: reverse a bottom
7065 uniqueNodes[ 0 ] = curNodes [ 1 ];
7066 uniqueNodes[ 1 ] = curNodes [ 0 ];
7071 else if ( nbUniqueNodes == 5 ) {
7072 // PENTAHEDRON --------------------> 2 tetrahedrons
7073 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7074 // a bottom node sticks with a linked top one
7076 SMDS_MeshElement* newElem =
7077 aMesh->AddVolume(curNodes[ 3 ],
7080 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7081 myLastCreatedElems.Append(newElem);
7083 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7084 // 2. : reverse a bottom
7085 uniqueNodes[ 0 ] = curNodes [ 1 ];
7086 uniqueNodes[ 1 ] = curNodes [ 0 ];
7096 if(elem->IsQuadratic()) { // Quadratic quadrangle
7108 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7111 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7113 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7114 uniqueNodes[0] = curNodes[0];
7115 uniqueNodes[1] = curNodes[2];
7116 uniqueNodes[2] = curNodes[3];
7117 uniqueNodes[3] = curNodes[5];
7118 uniqueNodes[4] = curNodes[6];
7119 uniqueNodes[5] = curNodes[7];
7122 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7123 uniqueNodes[0] = curNodes[0];
7124 uniqueNodes[1] = curNodes[1];
7125 uniqueNodes[2] = curNodes[2];
7126 uniqueNodes[3] = curNodes[4];
7127 uniqueNodes[4] = curNodes[5];
7128 uniqueNodes[5] = curNodes[6];
7131 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7132 uniqueNodes[0] = curNodes[1];
7133 uniqueNodes[1] = curNodes[2];
7134 uniqueNodes[2] = curNodes[3];
7135 uniqueNodes[3] = curNodes[5];
7136 uniqueNodes[4] = curNodes[6];
7137 uniqueNodes[5] = curNodes[0];
7140 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7141 uniqueNodes[0] = curNodes[0];
7142 uniqueNodes[1] = curNodes[1];
7143 uniqueNodes[2] = curNodes[3];
7144 uniqueNodes[3] = curNodes[4];
7145 uniqueNodes[4] = curNodes[6];
7146 uniqueNodes[5] = curNodes[7];
7149 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7150 uniqueNodes[0] = curNodes[0];
7151 uniqueNodes[1] = curNodes[2];
7152 uniqueNodes[2] = curNodes[3];
7153 uniqueNodes[3] = curNodes[1];
7154 uniqueNodes[4] = curNodes[6];
7155 uniqueNodes[5] = curNodes[7];
7158 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7159 uniqueNodes[0] = curNodes[0];
7160 uniqueNodes[1] = curNodes[1];
7161 uniqueNodes[2] = curNodes[2];
7162 uniqueNodes[3] = curNodes[4];
7163 uniqueNodes[4] = curNodes[5];
7164 uniqueNodes[5] = curNodes[7];
7167 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7168 uniqueNodes[0] = curNodes[0];
7169 uniqueNodes[1] = curNodes[1];
7170 uniqueNodes[2] = curNodes[3];
7171 uniqueNodes[3] = curNodes[4];
7172 uniqueNodes[4] = curNodes[2];
7173 uniqueNodes[5] = curNodes[7];
7176 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7177 uniqueNodes[0] = curNodes[0];
7178 uniqueNodes[1] = curNodes[1];
7179 uniqueNodes[2] = curNodes[2];
7180 uniqueNodes[3] = curNodes[4];
7181 uniqueNodes[4] = curNodes[5];
7182 uniqueNodes[5] = curNodes[3];
7187 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7190 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7194 //////////////////////////////////// HEXAHEDRON
7196 SMDS_VolumeTool hexa (elem);
7197 hexa.SetExternalNormal();
7198 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7199 //////////////////////// HEX ---> 1 tetrahedron
7200 for ( int iFace = 0; iFace < 6; iFace++ ) {
7201 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7202 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7203 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7204 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7205 // one face turns into a point ...
7206 int iOppFace = hexa.GetOppFaceIndex( iFace );
7207 ind = hexa.GetFaceNodesIndices( iOppFace );
7209 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7210 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7213 if ( nbStick == 1 ) {
7214 // ... and the opposite one - into a triangle.
7216 ind = hexa.GetFaceNodesIndices( iFace );
7217 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7224 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7225 //////////////////////// HEX ---> 1 prism
7226 int nbTria = 0, iTria[3];
7227 const int *ind; // indices of face nodes
7228 // look for triangular faces
7229 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7230 ind = hexa.GetFaceNodesIndices( iFace );
7231 TIDSortedNodeSet faceNodes;
7232 for ( iCur = 0; iCur < 4; iCur++ )
7233 faceNodes.insert( curNodes[ind[iCur]] );
7234 if ( faceNodes.size() == 3 )
7235 iTria[ nbTria++ ] = iFace;
7237 // check if triangles are opposite
7238 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7241 // set nodes of the bottom triangle
7242 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7244 for ( iCur = 0; iCur < 4; iCur++ )
7245 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7246 indB.push_back( ind[iCur] );
7247 if ( !hexa.IsForward() )
7248 std::swap( indB[0], indB[2] );
7249 for ( iCur = 0; iCur < 3; iCur++ )
7250 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7251 // set nodes of the top triangle
7252 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7253 for ( iCur = 0; iCur < 3; ++iCur )
7254 for ( int j = 0; j < 4; ++j )
7255 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7257 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7263 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7264 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7265 for ( int iFace = 0; iFace < 6; iFace++ ) {
7266 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7267 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7268 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7269 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7270 // one face turns into a point ...
7271 int iOppFace = hexa.GetOppFaceIndex( iFace );
7272 ind = hexa.GetFaceNodesIndices( iOppFace );
7274 iUnique = 2; // reverse a tetrahedron 1 bottom
7275 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7276 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7278 else if ( iUnique >= 0 )
7279 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7281 if ( nbStick == 0 ) {
7282 // ... and the opposite one is a quadrangle
7284 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7285 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7288 SMDS_MeshElement* newElem =
7289 aMesh->AddVolume(curNodes[ind[ 0 ]],
7292 curNodes[indTop[ 0 ]]);
7293 myLastCreatedElems.Append(newElem);
7295 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7302 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7303 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7304 // find indices of quad and tri faces
7305 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7306 for ( iFace = 0; iFace < 6; iFace++ ) {
7307 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7309 for ( iCur = 0; iCur < 4; iCur++ )
7310 nodeSet.insert( curNodes[ind[ iCur ]] );
7311 nbUniqueNodes = nodeSet.size();
7312 if ( nbUniqueNodes == 3 )
7313 iTriFace[ nbTri++ ] = iFace;
7314 else if ( nbUniqueNodes == 4 )
7315 iQuadFace[ nbQuad++ ] = iFace;
7317 if (nbQuad == 2 && nbTri == 4 &&
7318 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7319 // 2 opposite quadrangles stuck with a diagonal;
7320 // sample groups of merged indices: (0-4)(2-6)
7321 // --------------------------------------------> 2 tetrahedrons
7322 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7323 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7324 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7325 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7326 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7327 // stuck with 0-2 diagonal
7335 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7336 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7337 // stuck with 1-3 diagonal
7349 uniqueNodes[ 0 ] = curNodes [ i0 ];
7350 uniqueNodes[ 1 ] = curNodes [ i1d ];
7351 uniqueNodes[ 2 ] = curNodes [ i3d ];
7352 uniqueNodes[ 3 ] = curNodes [ i0t ];
7355 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7359 myLastCreatedElems.Append(newElem);
7361 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7364 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7365 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7366 // --------------------------------------------> prism
7367 // find 2 opposite triangles
7369 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7370 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7371 // find indices of kept and replaced nodes
7372 // and fill unique nodes of 2 opposite triangles
7373 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7374 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7375 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7376 // fill unique nodes
7379 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7380 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7381 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7383 // iCur of a linked node of the opposite face (make normals co-directed):
7384 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7385 // check that correspondent corners of triangles are linked
7386 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7389 uniqueNodes[ iUnique ] = n;
7390 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7399 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7402 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7409 } // switch ( nbNodes )
7411 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7413 if ( isOk ) { // the elem remains valid after sticking nodes
7414 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7416 // Change nodes of polyedre
7417 const SMDS_VtkVolume* aPolyedre =
7418 dynamic_cast<const SMDS_VtkVolume*>( elem );
7420 int nbFaces = aPolyedre->NbFaces();
7422 vector<const SMDS_MeshNode *> poly_nodes;
7423 vector<int> quantities (nbFaces);
7425 for (int iface = 1; iface <= nbFaces; iface++) {
7426 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7427 quantities[iface - 1] = nbFaceNodes;
7429 for (inode = 1; inode <= nbFaceNodes; inode++) {
7430 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7432 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7433 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7434 curNode = (*nnIt).second;
7436 poly_nodes.push_back(curNode);
7439 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7442 else // replace non-polyhedron elements
7444 const SMDSAbs_ElementType etyp = elem->GetType();
7445 const int elemId = elem->GetID();
7446 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7447 uniqueNodes.resize(nbUniqueNodes);
7449 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7451 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7452 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7453 if ( sm && newElem )
7454 sm->AddElement( newElem );
7455 if ( elem != newElem )
7456 ReplaceElemInGroups( elem, newElem, aMesh );
7460 // Remove invalid regular element or invalid polygon
7461 rmElemIds.push_back( elem->GetID() );
7464 } // loop on elements
7466 // Remove bad elements, then equal nodes (order important)
7468 Remove( rmElemIds, false );
7469 Remove( rmNodeIds, true );
7474 // ========================================================
7475 // class : SortableElement
7476 // purpose : allow sorting elements basing on their nodes
7477 // ========================================================
7478 class SortableElement : public set <const SMDS_MeshElement*>
7482 SortableElement( const SMDS_MeshElement* theElem )
7485 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7486 while ( nodeIt->more() )
7487 this->insert( nodeIt->next() );
7490 const SMDS_MeshElement* Get() const
7493 void Set(const SMDS_MeshElement* e) const
7498 mutable const SMDS_MeshElement* myElem;
7501 //=======================================================================
7502 //function : FindEqualElements
7503 //purpose : Return list of group of elements built on the same nodes.
7504 // Search among theElements or in the whole mesh if theElements is empty
7505 //=======================================================================
7507 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7508 TListOfListOfElementsID & theGroupsOfElementsID)
7510 myLastCreatedElems.Clear();
7511 myLastCreatedNodes.Clear();
7513 typedef map< SortableElement, int > TMapOfNodeSet;
7514 typedef list<int> TGroupOfElems;
7516 if ( theElements.empty() )
7517 { // get all elements in the mesh
7518 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7519 while ( eIt->more() )
7520 theElements.insert( theElements.end(), eIt->next());
7523 vector< TGroupOfElems > arrayOfGroups;
7524 TGroupOfElems groupOfElems;
7525 TMapOfNodeSet mapOfNodeSet;
7527 TIDSortedElemSet::iterator elemIt = theElements.begin();
7528 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7529 const SMDS_MeshElement* curElem = *elemIt;
7530 SortableElement SE(curElem);
7533 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7534 if( !(pp.second) ) {
7535 TMapOfNodeSet::iterator& itSE = pp.first;
7536 ind = (*itSE).second;
7537 arrayOfGroups[ind].push_back(curElem->GetID());
7540 groupOfElems.clear();
7541 groupOfElems.push_back(curElem->GetID());
7542 arrayOfGroups.push_back(groupOfElems);
7547 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7548 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7549 groupOfElems = *groupIt;
7550 if ( groupOfElems.size() > 1 ) {
7551 groupOfElems.sort();
7552 theGroupsOfElementsID.push_back(groupOfElems);
7557 //=======================================================================
7558 //function : MergeElements
7559 //purpose : In each given group, substitute all elements by the first one.
7560 //=======================================================================
7562 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7564 myLastCreatedElems.Clear();
7565 myLastCreatedNodes.Clear();
7567 typedef list<int> TListOfIDs;
7568 TListOfIDs rmElemIds; // IDs of elems to remove
7570 SMESHDS_Mesh* aMesh = GetMeshDS();
7572 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7573 while ( groupsIt != theGroupsOfElementsID.end() ) {
7574 TListOfIDs& aGroupOfElemID = *groupsIt;
7575 aGroupOfElemID.sort();
7576 int elemIDToKeep = aGroupOfElemID.front();
7577 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7578 aGroupOfElemID.pop_front();
7579 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7580 while ( idIt != aGroupOfElemID.end() ) {
7581 int elemIDToRemove = *idIt;
7582 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7583 // add the kept element in groups of removed one (PAL15188)
7584 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7585 rmElemIds.push_back( elemIDToRemove );
7591 Remove( rmElemIds, false );
7594 //=======================================================================
7595 //function : MergeEqualElements
7596 //purpose : Remove all but one of elements built on the same nodes.
7597 //=======================================================================
7599 void SMESH_MeshEditor::MergeEqualElements()
7601 TIDSortedElemSet aMeshElements; /* empty input ==
7602 to merge equal elements in the whole mesh */
7603 TListOfListOfElementsID aGroupsOfElementsID;
7604 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7605 MergeElements(aGroupsOfElementsID);
7608 //=======================================================================
7609 //function : findAdjacentFace
7611 //=======================================================================
7613 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7614 const SMDS_MeshNode* n2,
7615 const SMDS_MeshElement* elem)
7617 TIDSortedElemSet elemSet, avoidSet;
7619 avoidSet.insert ( elem );
7620 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7623 //=======================================================================
7624 //function : FindFreeBorder
7626 //=======================================================================
7628 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7630 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7631 const SMDS_MeshNode* theSecondNode,
7632 const SMDS_MeshNode* theLastNode,
7633 list< const SMDS_MeshNode* > & theNodes,
7634 list< const SMDS_MeshElement* >& theFaces)
7636 if ( !theFirstNode || !theSecondNode )
7638 // find border face between theFirstNode and theSecondNode
7639 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7643 theFaces.push_back( curElem );
7644 theNodes.push_back( theFirstNode );
7645 theNodes.push_back( theSecondNode );
7647 //vector<const SMDS_MeshNode*> nodes;
7648 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7649 TIDSortedElemSet foundElems;
7650 bool needTheLast = ( theLastNode != 0 );
7652 while ( nStart != theLastNode ) {
7653 if ( nStart == theFirstNode )
7654 return !needTheLast;
7656 // find all free border faces sharing form nStart
7658 list< const SMDS_MeshElement* > curElemList;
7659 list< const SMDS_MeshNode* > nStartList;
7660 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7661 while ( invElemIt->more() ) {
7662 const SMDS_MeshElement* e = invElemIt->next();
7663 if ( e == curElem || foundElems.insert( e ).second ) {
7665 int iNode = 0, nbNodes = e->NbNodes();
7666 //const SMDS_MeshNode* nodes[nbNodes+1];
7667 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7669 if(e->IsQuadratic()) {
7670 const SMDS_VtkFace* F =
7671 dynamic_cast<const SMDS_VtkFace*>(e);
7672 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7673 // use special nodes iterator
7674 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7675 while( anIter->more() ) {
7676 nodes[ iNode++ ] = cast2Node(anIter->next());
7680 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7681 while ( nIt->more() )
7682 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7684 nodes[ iNode ] = nodes[ 0 ];
7686 for ( iNode = 0; iNode < nbNodes; iNode++ )
7687 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7688 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7689 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7691 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7692 curElemList.push_back( e );
7696 // analyse the found
7698 int nbNewBorders = curElemList.size();
7699 if ( nbNewBorders == 0 ) {
7700 // no free border furthermore
7701 return !needTheLast;
7703 else if ( nbNewBorders == 1 ) {
7704 // one more element found
7706 nStart = nStartList.front();
7707 curElem = curElemList.front();
7708 theFaces.push_back( curElem );
7709 theNodes.push_back( nStart );
7712 // several continuations found
7713 list< const SMDS_MeshElement* >::iterator curElemIt;
7714 list< const SMDS_MeshNode* >::iterator nStartIt;
7715 // check if one of them reached the last node
7716 if ( needTheLast ) {
7717 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7718 curElemIt!= curElemList.end();
7719 curElemIt++, nStartIt++ )
7720 if ( *nStartIt == theLastNode ) {
7721 theFaces.push_back( *curElemIt );
7722 theNodes.push_back( *nStartIt );
7726 // find the best free border by the continuations
7727 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7728 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7729 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7730 curElemIt!= curElemList.end();
7731 curElemIt++, nStartIt++ )
7733 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7734 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7735 // find one more free border
7736 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7740 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7741 // choice: clear a worse one
7742 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7743 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7744 contNodes[ iWorse ].clear();
7745 contFaces[ iWorse ].clear();
7748 if ( contNodes[0].empty() && contNodes[1].empty() )
7751 // append the best free border
7752 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7753 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7754 theNodes.pop_back(); // remove nIgnore
7755 theNodes.pop_back(); // remove nStart
7756 theFaces.pop_back(); // remove curElem
7757 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7758 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7759 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7760 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7763 } // several continuations found
7764 } // while ( nStart != theLastNode )
7769 //=======================================================================
7770 //function : CheckFreeBorderNodes
7771 //purpose : Return true if the tree nodes are on a free border
7772 //=======================================================================
7774 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7775 const SMDS_MeshNode* theNode2,
7776 const SMDS_MeshNode* theNode3)
7778 list< const SMDS_MeshNode* > nodes;
7779 list< const SMDS_MeshElement* > faces;
7780 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7783 //=======================================================================
7784 //function : SewFreeBorder
7786 //=======================================================================
7788 SMESH_MeshEditor::Sew_Error
7789 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7790 const SMDS_MeshNode* theBordSecondNode,
7791 const SMDS_MeshNode* theBordLastNode,
7792 const SMDS_MeshNode* theSideFirstNode,
7793 const SMDS_MeshNode* theSideSecondNode,
7794 const SMDS_MeshNode* theSideThirdNode,
7795 const bool theSideIsFreeBorder,
7796 const bool toCreatePolygons,
7797 const bool toCreatePolyedrs)
7799 myLastCreatedElems.Clear();
7800 myLastCreatedNodes.Clear();
7802 MESSAGE("::SewFreeBorder()");
7803 Sew_Error aResult = SEW_OK;
7805 // ====================================
7806 // find side nodes and elements
7807 // ====================================
7809 list< const SMDS_MeshNode* > nSide[ 2 ];
7810 list< const SMDS_MeshElement* > eSide[ 2 ];
7811 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7812 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7816 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7817 nSide[0], eSide[0])) {
7818 MESSAGE(" Free Border 1 not found " );
7819 aResult = SEW_BORDER1_NOT_FOUND;
7821 if (theSideIsFreeBorder) {
7824 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7825 nSide[1], eSide[1])) {
7826 MESSAGE(" Free Border 2 not found " );
7827 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7830 if ( aResult != SEW_OK )
7833 if (!theSideIsFreeBorder) {
7837 // -------------------------------------------------------------------------
7839 // 1. If nodes to merge are not coincident, move nodes of the free border
7840 // from the coord sys defined by the direction from the first to last
7841 // nodes of the border to the correspondent sys of the side 2
7842 // 2. On the side 2, find the links most co-directed with the correspondent
7843 // links of the free border
7844 // -------------------------------------------------------------------------
7846 // 1. Since sewing may break if there are volumes to split on the side 2,
7847 // we wont move nodes but just compute new coordinates for them
7848 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7849 TNodeXYZMap nBordXYZ;
7850 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7851 list< const SMDS_MeshNode* >::iterator nBordIt;
7853 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7854 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7855 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7856 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7857 double tol2 = 1.e-8;
7858 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7859 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7860 // Need node movement.
7862 // find X and Z axes to create trsf
7863 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7865 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7867 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7870 gp_Ax3 toBordAx( Pb1, Zb, X );
7871 gp_Ax3 fromSideAx( Ps1, Zs, X );
7872 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7874 gp_Trsf toBordSys, fromSide2Sys;
7875 toBordSys.SetTransformation( toBordAx );
7876 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7877 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7880 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7881 const SMDS_MeshNode* n = *nBordIt;
7882 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7883 toBordSys.Transforms( xyz );
7884 fromSide2Sys.Transforms( xyz );
7885 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7889 // just insert nodes XYZ in the nBordXYZ map
7890 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7891 const SMDS_MeshNode* n = *nBordIt;
7892 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7896 // 2. On the side 2, find the links most co-directed with the correspondent
7897 // links of the free border
7899 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7900 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7901 sideNodes.push_back( theSideFirstNode );
7903 bool hasVolumes = false;
7904 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7905 set<long> foundSideLinkIDs, checkedLinkIDs;
7906 SMDS_VolumeTool volume;
7907 //const SMDS_MeshNode* faceNodes[ 4 ];
7909 const SMDS_MeshNode* sideNode;
7910 const SMDS_MeshElement* sideElem;
7911 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7912 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7913 nBordIt = bordNodes.begin();
7915 // border node position and border link direction to compare with
7916 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7917 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7918 // choose next side node by link direction or by closeness to
7919 // the current border node:
7920 bool searchByDir = ( *nBordIt != theBordLastNode );
7922 // find the next node on the Side 2
7924 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7926 checkedLinkIDs.clear();
7927 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7929 // loop on inverse elements of current node (prevSideNode) on the Side 2
7930 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7931 while ( invElemIt->more() )
7933 const SMDS_MeshElement* elem = invElemIt->next();
7934 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7935 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7936 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7937 bool isVolume = volume.Set( elem );
7938 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7939 if ( isVolume ) // --volume
7941 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7942 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7943 if(elem->IsQuadratic()) {
7944 const SMDS_VtkFace* F =
7945 dynamic_cast<const SMDS_VtkFace*>(elem);
7946 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7947 // use special nodes iterator
7948 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7949 while( anIter->more() ) {
7950 nodes[ iNode ] = cast2Node(anIter->next());
7951 if ( nodes[ iNode++ ] == prevSideNode )
7952 iPrevNode = iNode - 1;
7956 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7957 while ( nIt->more() ) {
7958 nodes[ iNode ] = cast2Node( nIt->next() );
7959 if ( nodes[ iNode++ ] == prevSideNode )
7960 iPrevNode = iNode - 1;
7963 // there are 2 links to check
7968 // loop on links, to be precise, on the second node of links
7969 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7970 const SMDS_MeshNode* n = nodes[ iNode ];
7972 if ( !volume.IsLinked( n, prevSideNode ))
7976 if ( iNode ) // a node before prevSideNode
7977 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7978 else // a node after prevSideNode
7979 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7981 // check if this link was already used
7982 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7983 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7984 if (!isJustChecked &&
7985 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7987 // test a link geometrically
7988 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7989 bool linkIsBetter = false;
7990 double dot = 0.0, dist = 0.0;
7991 if ( searchByDir ) { // choose most co-directed link
7992 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7993 linkIsBetter = ( dot > maxDot );
7995 else { // choose link with the node closest to bordPos
7996 dist = ( nextXYZ - bordPos ).SquareModulus();
7997 linkIsBetter = ( dist < minDist );
7999 if ( linkIsBetter ) {
8008 } // loop on inverse elements of prevSideNode
8011 MESSAGE(" Cant find path by links of the Side 2 ");
8012 return SEW_BAD_SIDE_NODES;
8014 sideNodes.push_back( sideNode );
8015 sideElems.push_back( sideElem );
8016 foundSideLinkIDs.insert ( linkID );
8017 prevSideNode = sideNode;
8019 if ( *nBordIt == theBordLastNode )
8020 searchByDir = false;
8022 // find the next border link to compare with
8023 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8024 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8025 // move to next border node if sideNode is before forward border node (bordPos)
8026 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8027 prevBordNode = *nBordIt;
8029 bordPos = nBordXYZ[ *nBordIt ];
8030 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8031 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8035 while ( sideNode != theSideSecondNode );
8037 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8038 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8039 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8041 } // end nodes search on the side 2
8043 // ============================
8044 // sew the border to the side 2
8045 // ============================
8047 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8048 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8050 TListOfListOfNodes nodeGroupsToMerge;
8051 if ( nbNodes[0] == nbNodes[1] ||
8052 ( theSideIsFreeBorder && !theSideThirdNode)) {
8054 // all nodes are to be merged
8056 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8057 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8058 nIt[0]++, nIt[1]++ )
8060 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8061 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8062 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8067 // insert new nodes into the border and the side to get equal nb of segments
8069 // get normalized parameters of nodes on the borders
8070 //double param[ 2 ][ maxNbNodes ];
8072 param[0] = new double [ maxNbNodes ];
8073 param[1] = new double [ maxNbNodes ];
8075 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8076 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8077 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8078 const SMDS_MeshNode* nPrev = *nIt;
8079 double bordLength = 0;
8080 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8081 const SMDS_MeshNode* nCur = *nIt;
8082 gp_XYZ segment (nCur->X() - nPrev->X(),
8083 nCur->Y() - nPrev->Y(),
8084 nCur->Z() - nPrev->Z());
8085 double segmentLen = segment.Modulus();
8086 bordLength += segmentLen;
8087 param[ iBord ][ iNode ] = bordLength;
8090 // normalize within [0,1]
8091 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8092 param[ iBord ][ iNode ] /= bordLength;
8096 // loop on border segments
8097 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8098 int i[ 2 ] = { 0, 0 };
8099 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8100 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8102 TElemOfNodeListMap insertMap;
8103 TElemOfNodeListMap::iterator insertMapIt;
8105 // key: elem to insert nodes into
8106 // value: 2 nodes to insert between + nodes to be inserted
8108 bool next[ 2 ] = { false, false };
8110 // find min adjacent segment length after sewing
8111 double nextParam = 10., prevParam = 0;
8112 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8113 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8114 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8115 if ( i[ iBord ] > 0 )
8116 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8118 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8119 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8120 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8122 // choose to insert or to merge nodes
8123 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8124 if ( Abs( du ) <= minSegLen * 0.2 ) {
8127 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8128 const SMDS_MeshNode* n0 = *nIt[0];
8129 const SMDS_MeshNode* n1 = *nIt[1];
8130 nodeGroupsToMerge.back().push_back( n1 );
8131 nodeGroupsToMerge.back().push_back( n0 );
8132 // position of node of the border changes due to merge
8133 param[ 0 ][ i[0] ] += du;
8134 // move n1 for the sake of elem shape evaluation during insertion.
8135 // n1 will be removed by MergeNodes() anyway
8136 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8137 next[0] = next[1] = true;
8142 int intoBord = ( du < 0 ) ? 0 : 1;
8143 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8144 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8145 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8146 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8147 if ( intoBord == 1 ) {
8148 // move node of the border to be on a link of elem of the side
8149 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8150 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8151 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8152 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8153 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8155 insertMapIt = insertMap.find( elem );
8156 bool notFound = ( insertMapIt == insertMap.end() );
8157 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8159 // insert into another link of the same element:
8160 // 1. perform insertion into the other link of the elem
8161 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8162 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8163 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8164 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8165 // 2. perform insertion into the link of adjacent faces
8167 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8169 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8173 if (toCreatePolyedrs) {
8174 // perform insertion into the links of adjacent volumes
8175 UpdateVolumes(n12, n22, nodeList);
8177 // 3. find an element appeared on n1 and n2 after the insertion
8178 insertMap.erase( elem );
8179 elem = findAdjacentFace( n1, n2, 0 );
8181 if ( notFound || otherLink ) {
8182 // add element and nodes of the side into the insertMap
8183 insertMapIt = insertMap.insert
8184 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8185 (*insertMapIt).second.push_back( n1 );
8186 (*insertMapIt).second.push_back( n2 );
8188 // add node to be inserted into elem
8189 (*insertMapIt).second.push_back( nIns );
8190 next[ 1 - intoBord ] = true;
8193 // go to the next segment
8194 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8195 if ( next[ iBord ] ) {
8196 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8198 nPrev[ iBord ] = *nIt[ iBord ];
8199 nIt[ iBord ]++; i[ iBord ]++;
8203 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8205 // perform insertion of nodes into elements
8207 for (insertMapIt = insertMap.begin();
8208 insertMapIt != insertMap.end();
8211 const SMDS_MeshElement* elem = (*insertMapIt).first;
8212 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8213 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8214 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8216 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8218 if ( !theSideIsFreeBorder ) {
8219 // look for and insert nodes into the faces adjacent to elem
8221 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8223 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8228 if (toCreatePolyedrs) {
8229 // perform insertion into the links of adjacent volumes
8230 UpdateVolumes(n1, n2, nodeList);
8236 } // end: insert new nodes
8238 MergeNodes ( nodeGroupsToMerge );
8243 //=======================================================================
8244 //function : InsertNodesIntoLink
8245 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8246 // and theBetweenNode2 and split theElement
8247 //=======================================================================
8249 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8250 const SMDS_MeshNode* theBetweenNode1,
8251 const SMDS_MeshNode* theBetweenNode2,
8252 list<const SMDS_MeshNode*>& theNodesToInsert,
8253 const bool toCreatePoly)
8255 if ( theFace->GetType() != SMDSAbs_Face ) return;
8257 // find indices of 2 link nodes and of the rest nodes
8258 int iNode = 0, il1, il2, i3, i4;
8259 il1 = il2 = i3 = i4 = -1;
8260 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8261 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8263 if(theFace->IsQuadratic()) {
8264 const SMDS_VtkFace* F =
8265 dynamic_cast<const SMDS_VtkFace*>(theFace);
8266 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8267 // use special nodes iterator
8268 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8269 while( anIter->more() ) {
8270 const SMDS_MeshNode* n = cast2Node(anIter->next());
8271 if ( n == theBetweenNode1 )
8273 else if ( n == theBetweenNode2 )
8279 nodes[ iNode++ ] = n;
8283 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8284 while ( nodeIt->more() ) {
8285 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8286 if ( n == theBetweenNode1 )
8288 else if ( n == theBetweenNode2 )
8294 nodes[ iNode++ ] = n;
8297 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8300 // arrange link nodes to go one after another regarding the face orientation
8301 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8302 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8307 aNodesToInsert.reverse();
8309 // check that not link nodes of a quadrangles are in good order
8310 int nbFaceNodes = theFace->NbNodes();
8311 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8317 if (toCreatePoly || theFace->IsPoly()) {
8320 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8322 // add nodes of face up to first node of link
8325 if(theFace->IsQuadratic()) {
8326 const SMDS_VtkFace* F =
8327 dynamic_cast<const SMDS_VtkFace*>(theFace);
8328 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8329 // use special nodes iterator
8330 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8331 while( anIter->more() && !isFLN ) {
8332 const SMDS_MeshNode* n = cast2Node(anIter->next());
8333 poly_nodes[iNode++] = n;
8334 if (n == nodes[il1]) {
8338 // add nodes to insert
8339 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8340 for (; nIt != aNodesToInsert.end(); nIt++) {
8341 poly_nodes[iNode++] = *nIt;
8343 // add nodes of face starting from last node of link
8344 while ( anIter->more() ) {
8345 poly_nodes[iNode++] = cast2Node(anIter->next());
8349 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8350 while ( nodeIt->more() && !isFLN ) {
8351 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8352 poly_nodes[iNode++] = n;
8353 if (n == nodes[il1]) {
8357 // add nodes to insert
8358 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8359 for (; nIt != aNodesToInsert.end(); nIt++) {
8360 poly_nodes[iNode++] = *nIt;
8362 // add nodes of face starting from last node of link
8363 while ( nodeIt->more() ) {
8364 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8365 poly_nodes[iNode++] = n;
8369 // edit or replace the face
8370 SMESHDS_Mesh *aMesh = GetMeshDS();
8372 if (theFace->IsPoly()) {
8373 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8376 int aShapeId = FindShape( theFace );
8378 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8379 myLastCreatedElems.Append(newElem);
8380 if ( aShapeId && newElem )
8381 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8383 aMesh->RemoveElement(theFace);
8388 SMESHDS_Mesh *aMesh = GetMeshDS();
8389 if( !theFace->IsQuadratic() ) {
8391 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8392 int nbLinkNodes = 2 + aNodesToInsert.size();
8393 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8394 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8395 linkNodes[ 0 ] = nodes[ il1 ];
8396 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8397 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8398 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8399 linkNodes[ iNode++ ] = *nIt;
8401 // decide how to split a quadrangle: compare possible variants
8402 // and choose which of splits to be a quadrangle
8403 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8404 if ( nbFaceNodes == 3 ) {
8405 iBestQuad = nbSplits;
8408 else if ( nbFaceNodes == 4 ) {
8409 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8410 double aBestRate = DBL_MAX;
8411 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8413 double aBadRate = 0;
8414 // evaluate elements quality
8415 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8416 if ( iSplit == iQuad ) {
8417 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8421 aBadRate += getBadRate( &quad, aCrit );
8424 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8426 nodes[ iSplit < iQuad ? i4 : i3 ]);
8427 aBadRate += getBadRate( &tria, aCrit );
8431 if ( aBadRate < aBestRate ) {
8433 aBestRate = aBadRate;
8438 // create new elements
8439 int aShapeId = FindShape( theFace );
8442 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8443 SMDS_MeshElement* newElem = 0;
8444 if ( iSplit == iBestQuad )
8445 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8450 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8452 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8453 myLastCreatedElems.Append(newElem);
8454 if ( aShapeId && newElem )
8455 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8458 // change nodes of theFace
8459 const SMDS_MeshNode* newNodes[ 4 ];
8460 newNodes[ 0 ] = linkNodes[ i1 ];
8461 newNodes[ 1 ] = linkNodes[ i2 ];
8462 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8463 newNodes[ 3 ] = nodes[ i4 ];
8464 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8465 const SMDS_MeshElement* newElem = 0;
8466 if (iSplit == iBestQuad)
8467 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8469 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8470 myLastCreatedElems.Append(newElem);
8471 if ( aShapeId && newElem )
8472 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8473 } // end if(!theFace->IsQuadratic())
8474 else { // theFace is quadratic
8475 // we have to split theFace on simple triangles and one simple quadrangle
8477 int nbshift = tmp*2;
8478 // shift nodes in nodes[] by nbshift
8480 for(i=0; i<nbshift; i++) {
8481 const SMDS_MeshNode* n = nodes[0];
8482 for(j=0; j<nbFaceNodes-1; j++) {
8483 nodes[j] = nodes[j+1];
8485 nodes[nbFaceNodes-1] = n;
8487 il1 = il1 - nbshift;
8488 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8489 // n0 n1 n2 n0 n1 n2
8490 // +-----+-----+ +-----+-----+
8499 // create new elements
8500 int aShapeId = FindShape( theFace );
8503 if(nbFaceNodes==6) { // quadratic triangle
8504 SMDS_MeshElement* newElem =
8505 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8506 myLastCreatedElems.Append(newElem);
8507 if ( aShapeId && newElem )
8508 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8509 if(theFace->IsMediumNode(nodes[il1])) {
8510 // create quadrangle
8511 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8512 myLastCreatedElems.Append(newElem);
8513 if ( aShapeId && newElem )
8514 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8520 // create quadrangle
8521 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8522 myLastCreatedElems.Append(newElem);
8523 if ( aShapeId && newElem )
8524 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8530 else { // nbFaceNodes==8 - quadratic quadrangle
8531 SMDS_MeshElement* newElem =
8532 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8533 myLastCreatedElems.Append(newElem);
8534 if ( aShapeId && newElem )
8535 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8536 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8537 myLastCreatedElems.Append(newElem);
8538 if ( aShapeId && newElem )
8539 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8540 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8541 myLastCreatedElems.Append(newElem);
8542 if ( aShapeId && newElem )
8543 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8544 if(theFace->IsMediumNode(nodes[il1])) {
8545 // create quadrangle
8546 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8547 myLastCreatedElems.Append(newElem);
8548 if ( aShapeId && newElem )
8549 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8555 // create quadrangle
8556 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8557 myLastCreatedElems.Append(newElem);
8558 if ( aShapeId && newElem )
8559 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8565 // create needed triangles using n1,n2,n3 and inserted nodes
8566 int nbn = 2 + aNodesToInsert.size();
8567 //const SMDS_MeshNode* aNodes[nbn];
8568 vector<const SMDS_MeshNode*> aNodes(nbn);
8569 aNodes[0] = nodes[n1];
8570 aNodes[nbn-1] = nodes[n2];
8571 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8572 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8573 aNodes[iNode++] = *nIt;
8575 for(i=1; i<nbn; i++) {
8576 SMDS_MeshElement* newElem =
8577 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8578 myLastCreatedElems.Append(newElem);
8579 if ( aShapeId && newElem )
8580 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8584 aMesh->RemoveElement(theFace);
8587 //=======================================================================
8588 //function : UpdateVolumes
8590 //=======================================================================
8591 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8592 const SMDS_MeshNode* theBetweenNode2,
8593 list<const SMDS_MeshNode*>& theNodesToInsert)
8595 myLastCreatedElems.Clear();
8596 myLastCreatedNodes.Clear();
8598 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8599 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8600 const SMDS_MeshElement* elem = invElemIt->next();
8602 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8603 SMDS_VolumeTool aVolume (elem);
8604 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8607 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8608 int iface, nbFaces = aVolume.NbFaces();
8609 vector<const SMDS_MeshNode *> poly_nodes;
8610 vector<int> quantities (nbFaces);
8612 for (iface = 0; iface < nbFaces; iface++) {
8613 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8614 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8615 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8617 for (int inode = 0; inode < nbFaceNodes; inode++) {
8618 poly_nodes.push_back(faceNodes[inode]);
8620 if (nbInserted == 0) {
8621 if (faceNodes[inode] == theBetweenNode1) {
8622 if (faceNodes[inode + 1] == theBetweenNode2) {
8623 nbInserted = theNodesToInsert.size();
8625 // add nodes to insert
8626 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8627 for (; nIt != theNodesToInsert.end(); nIt++) {
8628 poly_nodes.push_back(*nIt);
8632 else if (faceNodes[inode] == theBetweenNode2) {
8633 if (faceNodes[inode + 1] == theBetweenNode1) {
8634 nbInserted = theNodesToInsert.size();
8636 // add nodes to insert in reversed order
8637 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8639 for (; nIt != theNodesToInsert.begin(); nIt--) {
8640 poly_nodes.push_back(*nIt);
8642 poly_nodes.push_back(*nIt);
8649 quantities[iface] = nbFaceNodes + nbInserted;
8652 // Replace or update the volume
8653 SMESHDS_Mesh *aMesh = GetMeshDS();
8655 if (elem->IsPoly()) {
8656 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8660 int aShapeId = FindShape( elem );
8662 SMDS_MeshElement* newElem =
8663 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8664 myLastCreatedElems.Append(newElem);
8665 if (aShapeId && newElem)
8666 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8668 aMesh->RemoveElement(elem);
8675 //================================================================================
8677 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8679 //================================================================================
8681 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8682 vector<const SMDS_MeshNode *> & nodes,
8683 vector<int> & nbNodeInFaces )
8686 nbNodeInFaces.clear();
8687 SMDS_VolumeTool vTool ( elem );
8688 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8690 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8691 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8692 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8697 //=======================================================================
8699 * \brief Convert elements contained in a submesh to quadratic
8700 * \return int - nb of checked elements
8702 //=======================================================================
8704 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8705 SMESH_MesherHelper& theHelper,
8706 const bool theForce3d)
8709 if( !theSm ) return nbElem;
8711 vector<int> nbNodeInFaces;
8712 vector<const SMDS_MeshNode *> nodes;
8713 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8714 while(ElemItr->more())
8717 const SMDS_MeshElement* elem = ElemItr->next();
8718 if( !elem ) continue;
8720 // analyse a necessity of conversion
8721 const SMDSAbs_ElementType aType = elem->GetType();
8722 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8724 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8725 bool hasCentralNodes = false;
8726 if ( elem->IsQuadratic() )
8729 switch ( aGeomType ) {
8730 case SMDSEntity_Quad_Triangle:
8731 case SMDSEntity_Quad_Quadrangle:
8732 case SMDSEntity_Quad_Hexa:
8733 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8735 case SMDSEntity_BiQuad_Triangle:
8736 case SMDSEntity_BiQuad_Quadrangle:
8737 case SMDSEntity_TriQuad_Hexa:
8738 alreadyOK = theHelper.GetIsBiQuadratic();
8739 hasCentralNodes = true;
8744 // take into account already present modium nodes
8746 case SMDSAbs_Volume:
8747 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8749 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8751 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8757 // get elem data needed to re-create it
8759 const int id = elem->GetID();
8760 const int nbNodes = elem->NbCornerNodes();
8761 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8762 if ( aGeomType == SMDSEntity_Polyhedra )
8763 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8764 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8765 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8767 // remove a linear element
8768 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8770 // remove central nodes of biquadratic elements (biquad->quad convertion)
8771 if ( hasCentralNodes )
8772 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8773 if ( nodes[i]->NbInverseElements() == 0 )
8774 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8776 const SMDS_MeshElement* NewElem = 0;
8782 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8790 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8793 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8796 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8800 case SMDSAbs_Volume :
8804 case SMDSEntity_Tetra:
8805 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8807 case SMDSEntity_Pyramid:
8808 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8810 case SMDSEntity_Penta:
8811 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8813 case SMDSEntity_Hexa:
8814 case SMDSEntity_Quad_Hexa:
8815 case SMDSEntity_TriQuad_Hexa:
8816 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8817 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8819 case SMDSEntity_Hexagonal_Prism:
8821 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8828 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8829 if( NewElem && NewElem->getshapeId() < 1 )
8830 theSm->AddElement( NewElem );
8834 //=======================================================================
8835 //function : ConvertToQuadratic
8837 //=======================================================================
8839 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8841 SMESHDS_Mesh* meshDS = GetMeshDS();
8843 SMESH_MesherHelper aHelper(*myMesh);
8845 aHelper.SetIsQuadratic( true );
8846 aHelper.SetIsBiQuadratic( theToBiQuad );
8847 aHelper.SetElementsOnShape(true);
8848 aHelper.ToFixNodeParameters( true );
8850 // convert elements assigned to sub-meshes
8851 int nbCheckedElems = 0;
8852 if ( myMesh->HasShapeToMesh() )
8854 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8856 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8857 while ( smIt->more() ) {
8858 SMESH_subMesh* sm = smIt->next();
8859 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8860 aHelper.SetSubShape( sm->GetSubShape() );
8861 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8867 // convert elements NOT assigned to sub-meshes
8868 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8869 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8871 aHelper.SetElementsOnShape(false);
8872 SMESHDS_SubMesh *smDS = 0;
8875 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8876 while( aEdgeItr->more() )
8878 const SMDS_MeshEdge* edge = aEdgeItr->next();
8879 if ( !edge->IsQuadratic() )
8881 int id = edge->GetID();
8882 const SMDS_MeshNode* n1 = edge->GetNode(0);
8883 const SMDS_MeshNode* n2 = edge->GetNode(1);
8885 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8887 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8888 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8892 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8897 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8898 while( aFaceItr->more() )
8900 const SMDS_MeshFace* face = aFaceItr->next();
8901 if ( !face ) continue;
8903 const SMDSAbs_EntityType type = face->GetEntityType();
8907 case SMDSEntity_Quad_Triangle:
8908 case SMDSEntity_Quad_Quadrangle:
8909 alreadyOK = !theToBiQuad;
8910 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8912 case SMDSEntity_BiQuad_Triangle:
8913 case SMDSEntity_BiQuad_Quadrangle:
8914 alreadyOK = theToBiQuad;
8915 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8917 default: alreadyOK = false;
8922 const int id = face->GetID();
8923 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8925 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8927 SMDS_MeshFace * NewFace = 0;
8930 case SMDSEntity_Triangle:
8931 case SMDSEntity_Quad_Triangle:
8932 case SMDSEntity_BiQuad_Triangle:
8933 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8934 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8935 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8938 case SMDSEntity_Quadrangle:
8939 case SMDSEntity_Quad_Quadrangle:
8940 case SMDSEntity_BiQuad_Quadrangle:
8941 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8942 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8943 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8947 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8949 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8953 vector<int> nbNodeInFaces;
8954 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8955 while(aVolumeItr->more())
8957 const SMDS_MeshVolume* volume = aVolumeItr->next();
8958 if ( !volume ) continue;
8960 const SMDSAbs_EntityType type = volume->GetEntityType();
8961 if ( volume->IsQuadratic() )
8966 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8967 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8968 default: alreadyOK = true;
8972 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8976 const int id = volume->GetID();
8977 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8978 if ( type == SMDSEntity_Polyhedra )
8979 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8980 else if ( type == SMDSEntity_Hexagonal_Prism )
8981 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8983 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8985 SMDS_MeshVolume * NewVolume = 0;
8988 case SMDSEntity_Tetra:
8989 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8991 case SMDSEntity_Hexa:
8992 case SMDSEntity_Quad_Hexa:
8993 case SMDSEntity_TriQuad_Hexa:
8994 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8995 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8996 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8997 if ( nodes[i]->NbInverseElements() == 0 )
8998 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9000 case SMDSEntity_Pyramid:
9001 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9002 nodes[3], nodes[4], id, theForce3d);
9004 case SMDSEntity_Penta:
9005 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9006 nodes[3], nodes[4], nodes[5], id, theForce3d);
9008 case SMDSEntity_Hexagonal_Prism:
9010 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9012 ReplaceElemInGroups(volume, NewVolume, meshDS);
9017 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9018 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9019 // aHelper.FixQuadraticElements(myError);
9020 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9024 //================================================================================
9026 * \brief Makes given elements quadratic
9027 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9028 * \param theElements - elements to make quadratic
9030 //================================================================================
9032 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9033 TIDSortedElemSet& theElements,
9034 const bool theToBiQuad)
9036 if ( theElements.empty() ) return;
9038 // we believe that all theElements are of the same type
9039 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9041 // get all nodes shared by theElements
9042 TIDSortedNodeSet allNodes;
9043 TIDSortedElemSet::iterator eIt = theElements.begin();
9044 for ( ; eIt != theElements.end(); ++eIt )
9045 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9047 // complete theElements with elements of lower dim whose all nodes are in allNodes
9049 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9050 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9051 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9052 for ( ; nIt != allNodes.end(); ++nIt )
9054 const SMDS_MeshNode* n = *nIt;
9055 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9056 while ( invIt->more() )
9058 const SMDS_MeshElement* e = invIt->next();
9059 const SMDSAbs_ElementType type = e->GetType();
9060 if ( e->IsQuadratic() )
9062 quadAdjacentElems[ type ].insert( e );
9065 switch ( e->GetEntityType() ) {
9066 case SMDSEntity_Quad_Triangle:
9067 case SMDSEntity_Quad_Quadrangle:
9068 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9069 case SMDSEntity_BiQuad_Triangle:
9070 case SMDSEntity_BiQuad_Quadrangle:
9071 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9072 default: alreadyOK = true;
9077 if ( type >= elemType )
9078 continue; // same type or more complex linear element
9080 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9081 continue; // e is already checked
9085 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9086 while ( nodeIt->more() && allIn )
9087 allIn = allNodes.count( nodeIt->next() );
9089 theElements.insert(e );
9093 SMESH_MesherHelper helper(*myMesh);
9094 helper.SetIsQuadratic( true );
9095 helper.SetIsBiQuadratic( theToBiQuad );
9097 // add links of quadratic adjacent elements to the helper
9099 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9100 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9101 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9103 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9105 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9106 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9107 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9109 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9111 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9112 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9113 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9115 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9118 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9120 SMESHDS_Mesh* meshDS = GetMeshDS();
9121 SMESHDS_SubMesh* smDS = 0;
9122 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9124 const SMDS_MeshElement* elem = *eIt;
9127 int nbCentralNodes = 0;
9128 switch ( elem->GetEntityType() ) {
9129 // linear convertible
9130 case SMDSEntity_Edge:
9131 case SMDSEntity_Triangle:
9132 case SMDSEntity_Quadrangle:
9133 case SMDSEntity_Tetra:
9134 case SMDSEntity_Pyramid:
9135 case SMDSEntity_Hexa:
9136 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9137 // quadratic that can become bi-quadratic
9138 case SMDSEntity_Quad_Triangle:
9139 case SMDSEntity_Quad_Quadrangle:
9140 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9142 case SMDSEntity_BiQuad_Triangle:
9143 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9144 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9146 default: alreadyOK = true;
9148 if ( alreadyOK ) continue;
9150 const SMDSAbs_ElementType type = elem->GetType();
9151 const int id = elem->GetID();
9152 const int nbNodes = elem->NbCornerNodes();
9153 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9155 helper.SetSubShape( elem->getshapeId() );
9157 if ( !smDS || !smDS->Contains( elem ))
9158 smDS = meshDS->MeshElements( elem->getshapeId() );
9159 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9161 SMDS_MeshElement * newElem = 0;
9164 case 4: // cases for most frequently used element types go first (for optimization)
9165 if ( type == SMDSAbs_Volume )
9166 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9168 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9171 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9172 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9175 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9178 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9181 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9182 nodes[4], id, theForce3d);
9185 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9186 nodes[4], nodes[5], id, theForce3d);
9190 ReplaceElemInGroups( elem, newElem, meshDS);
9191 if( newElem && smDS )
9192 smDS->AddElement( newElem );
9194 // remove central nodes
9195 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9196 if ( nodes[i]->NbInverseElements() == 0 )
9197 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9199 } // loop on theElements
9202 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9203 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9204 // helper.FixQuadraticElements( myError );
9205 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9209 //=======================================================================
9211 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9212 * \return int - nb of checked elements
9214 //=======================================================================
9216 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9217 SMDS_ElemIteratorPtr theItr,
9218 const int theShapeID)
9221 SMESHDS_Mesh* meshDS = GetMeshDS();
9223 while( theItr->more() )
9225 const SMDS_MeshElement* elem = theItr->next();
9227 if( elem && elem->IsQuadratic())
9229 int id = elem->GetID();
9230 int nbCornerNodes = elem->NbCornerNodes();
9231 SMDSAbs_ElementType aType = elem->GetType();
9233 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9235 //remove a quadratic element
9236 if ( !theSm || !theSm->Contains( elem ))
9237 theSm = meshDS->MeshElements( elem->getshapeId() );
9238 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9240 // remove medium nodes
9241 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9242 if ( nodes[i]->NbInverseElements() == 0 )
9243 meshDS->RemoveFreeNode( nodes[i], theSm );
9245 // add a linear element
9246 nodes.resize( nbCornerNodes );
9247 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9248 ReplaceElemInGroups(elem, newElem, meshDS);
9249 if( theSm && newElem )
9250 theSm->AddElement( newElem );
9256 //=======================================================================
9257 //function : ConvertFromQuadratic
9259 //=======================================================================
9261 bool SMESH_MeshEditor::ConvertFromQuadratic()
9263 int nbCheckedElems = 0;
9264 if ( myMesh->HasShapeToMesh() )
9266 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9268 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9269 while ( smIt->more() ) {
9270 SMESH_subMesh* sm = smIt->next();
9271 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9272 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9278 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9279 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9281 SMESHDS_SubMesh *aSM = 0;
9282 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9290 //================================================================================
9292 * \brief Return true if all medium nodes of the element are in the node set
9294 //================================================================================
9296 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9298 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9299 if ( !nodeSet.count( elem->GetNode(i) ))
9305 //================================================================================
9307 * \brief Makes given elements linear
9309 //================================================================================
9311 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9313 if ( theElements.empty() ) return;
9315 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9316 set<int> mediumNodeIDs;
9317 TIDSortedElemSet::iterator eIt = theElements.begin();
9318 for ( ; eIt != theElements.end(); ++eIt )
9320 const SMDS_MeshElement* e = *eIt;
9321 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9322 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9325 // replace given elements by linear ones
9326 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9327 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9329 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9330 // except those elements sharing medium nodes of quadratic element whose medium nodes
9331 // are not all in mediumNodeIDs
9333 // get remaining medium nodes
9334 TIDSortedNodeSet mediumNodes;
9335 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9336 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9337 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9338 mediumNodes.insert( mediumNodes.end(), n );
9340 // find more quadratic elements to convert
9341 TIDSortedElemSet moreElemsToConvert;
9342 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9343 for ( ; nIt != mediumNodes.end(); ++nIt )
9345 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9346 while ( invIt->more() )
9348 const SMDS_MeshElement* e = invIt->next();
9349 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9351 // find a more complex element including e and
9352 // whose medium nodes are not in mediumNodes
9353 bool complexFound = false;
9354 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9356 SMDS_ElemIteratorPtr invIt2 =
9357 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9358 while ( invIt2->more() )
9360 const SMDS_MeshElement* eComplex = invIt2->next();
9361 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9363 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9364 if ( nbCommonNodes == e->NbNodes())
9366 complexFound = true;
9367 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9373 if ( !complexFound )
9374 moreElemsToConvert.insert( e );
9378 elemIt = elemSetIterator( moreElemsToConvert );
9379 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9382 //=======================================================================
9383 //function : SewSideElements
9385 //=======================================================================
9387 SMESH_MeshEditor::Sew_Error
9388 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9389 TIDSortedElemSet& theSide2,
9390 const SMDS_MeshNode* theFirstNode1,
9391 const SMDS_MeshNode* theFirstNode2,
9392 const SMDS_MeshNode* theSecondNode1,
9393 const SMDS_MeshNode* theSecondNode2)
9395 myLastCreatedElems.Clear();
9396 myLastCreatedNodes.Clear();
9398 MESSAGE ("::::SewSideElements()");
9399 if ( theSide1.size() != theSide2.size() )
9400 return SEW_DIFF_NB_OF_ELEMENTS;
9402 Sew_Error aResult = SEW_OK;
9404 // 1. Build set of faces representing each side
9405 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9406 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9408 // =======================================================================
9409 // 1. Build set of faces representing each side:
9410 // =======================================================================
9411 // a. build set of nodes belonging to faces
9412 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9413 // c. create temporary faces representing side of volumes if correspondent
9414 // face does not exist
9416 SMESHDS_Mesh* aMesh = GetMeshDS();
9417 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9418 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9419 TIDSortedElemSet faceSet1, faceSet2;
9420 set<const SMDS_MeshElement*> volSet1, volSet2;
9421 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9422 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9423 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9424 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9425 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9426 int iSide, iFace, iNode;
9428 list<const SMDS_MeshElement* > tempFaceList;
9429 for ( iSide = 0; iSide < 2; iSide++ ) {
9430 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9431 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9432 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9433 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9434 set<const SMDS_MeshElement*>::iterator vIt;
9435 TIDSortedElemSet::iterator eIt;
9436 set<const SMDS_MeshNode*>::iterator nIt;
9438 // check that given nodes belong to given elements
9439 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9440 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9441 int firstIndex = -1, secondIndex = -1;
9442 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9443 const SMDS_MeshElement* elem = *eIt;
9444 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9445 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9446 if ( firstIndex > -1 && secondIndex > -1 ) break;
9448 if ( firstIndex < 0 || secondIndex < 0 ) {
9449 // we can simply return until temporary faces created
9450 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9453 // -----------------------------------------------------------
9454 // 1a. Collect nodes of existing faces
9455 // and build set of face nodes in order to detect missing
9456 // faces corresponding to sides of volumes
9457 // -----------------------------------------------------------
9459 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9461 // loop on the given element of a side
9462 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9463 //const SMDS_MeshElement* elem = *eIt;
9464 const SMDS_MeshElement* elem = *eIt;
9465 if ( elem->GetType() == SMDSAbs_Face ) {
9466 faceSet->insert( elem );
9467 set <const SMDS_MeshNode*> faceNodeSet;
9468 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9469 while ( nodeIt->more() ) {
9470 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9471 nodeSet->insert( n );
9472 faceNodeSet.insert( n );
9474 setOfFaceNodeSet.insert( faceNodeSet );
9476 else if ( elem->GetType() == SMDSAbs_Volume )
9477 volSet->insert( elem );
9479 // ------------------------------------------------------------------------------
9480 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9481 // ------------------------------------------------------------------------------
9483 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9484 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9485 while ( fIt->more() ) { // loop on faces sharing a node
9486 const SMDS_MeshElement* f = fIt->next();
9487 if ( faceSet->find( f ) == faceSet->end() ) {
9488 // check if all nodes are in nodeSet and
9489 // complete setOfFaceNodeSet if they are
9490 set <const SMDS_MeshNode*> faceNodeSet;
9491 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9492 bool allInSet = true;
9493 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9494 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9495 if ( nodeSet->find( n ) == nodeSet->end() )
9498 faceNodeSet.insert( n );
9501 faceSet->insert( f );
9502 setOfFaceNodeSet.insert( faceNodeSet );
9508 // -------------------------------------------------------------------------
9509 // 1c. Create temporary faces representing sides of volumes if correspondent
9510 // face does not exist
9511 // -------------------------------------------------------------------------
9513 if ( !volSet->empty() ) {
9514 //int nodeSetSize = nodeSet->size();
9516 // loop on given volumes
9517 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9518 SMDS_VolumeTool vol (*vIt);
9519 // loop on volume faces: find free faces
9520 // --------------------------------------
9521 list<const SMDS_MeshElement* > freeFaceList;
9522 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9523 if ( !vol.IsFreeFace( iFace ))
9525 // check if there is already a face with same nodes in a face set
9526 const SMDS_MeshElement* aFreeFace = 0;
9527 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9528 int nbNodes = vol.NbFaceNodes( iFace );
9529 set <const SMDS_MeshNode*> faceNodeSet;
9530 vol.GetFaceNodes( iFace, faceNodeSet );
9531 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9533 // no such a face is given but it still can exist, check it
9534 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9535 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9538 // create a temporary face
9539 if ( nbNodes == 3 ) {
9540 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9541 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9543 else if ( nbNodes == 4 ) {
9544 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9545 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9548 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9549 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9550 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9553 tempFaceList.push_back( aFreeFace );
9557 freeFaceList.push_back( aFreeFace );
9559 } // loop on faces of a volume
9561 // choose one of several free faces of a volume
9562 // --------------------------------------------
9563 if ( freeFaceList.size() > 1 ) {
9564 // choose a face having max nb of nodes shared by other elems of a side
9565 int maxNbNodes = -1;
9566 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9567 while ( fIt != freeFaceList.end() ) { // loop on free faces
9568 int nbSharedNodes = 0;
9569 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9570 while ( nodeIt->more() ) { // loop on free face nodes
9571 const SMDS_MeshNode* n =
9572 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9573 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9574 while ( invElemIt->more() ) {
9575 const SMDS_MeshElement* e = invElemIt->next();
9576 nbSharedNodes += faceSet->count( e );
9577 nbSharedNodes += elemSet->count( e );
9580 if ( nbSharedNodes > maxNbNodes ) {
9581 maxNbNodes = nbSharedNodes;
9582 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9584 else if ( nbSharedNodes == maxNbNodes ) {
9588 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9591 if ( freeFaceList.size() > 1 )
9593 // could not choose one face, use another way
9594 // choose a face most close to the bary center of the opposite side
9595 gp_XYZ aBC( 0., 0., 0. );
9596 set <const SMDS_MeshNode*> addedNodes;
9597 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9598 eIt = elemSet2->begin();
9599 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9600 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9601 while ( nodeIt->more() ) { // loop on free face nodes
9602 const SMDS_MeshNode* n =
9603 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9604 if ( addedNodes.insert( n ).second )
9605 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9608 aBC /= addedNodes.size();
9609 double minDist = DBL_MAX;
9610 fIt = freeFaceList.begin();
9611 while ( fIt != freeFaceList.end() ) { // loop on free faces
9613 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9614 while ( nodeIt->more() ) { // loop on free face nodes
9615 const SMDS_MeshNode* n =
9616 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9617 gp_XYZ p( n->X(),n->Y(),n->Z() );
9618 dist += ( aBC - p ).SquareModulus();
9620 if ( dist < minDist ) {
9622 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9625 fIt = freeFaceList.erase( fIt++ );
9628 } // choose one of several free faces of a volume
9630 if ( freeFaceList.size() == 1 ) {
9631 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9632 faceSet->insert( aFreeFace );
9633 // complete a node set with nodes of a found free face
9634 // for ( iNode = 0; iNode < ; iNode++ )
9635 // nodeSet->insert( fNodes[ iNode ] );
9638 } // loop on volumes of a side
9640 // // complete a set of faces if new nodes in a nodeSet appeared
9641 // // ----------------------------------------------------------
9642 // if ( nodeSetSize != nodeSet->size() ) {
9643 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9644 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9645 // while ( fIt->more() ) { // loop on faces sharing a node
9646 // const SMDS_MeshElement* f = fIt->next();
9647 // if ( faceSet->find( f ) == faceSet->end() ) {
9648 // // check if all nodes are in nodeSet and
9649 // // complete setOfFaceNodeSet if they are
9650 // set <const SMDS_MeshNode*> faceNodeSet;
9651 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9652 // bool allInSet = true;
9653 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9654 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9655 // if ( nodeSet->find( n ) == nodeSet->end() )
9656 // allInSet = false;
9658 // faceNodeSet.insert( n );
9660 // if ( allInSet ) {
9661 // faceSet->insert( f );
9662 // setOfFaceNodeSet.insert( faceNodeSet );
9668 } // Create temporary faces, if there are volumes given
9671 if ( faceSet1.size() != faceSet2.size() ) {
9672 // delete temporary faces: they are in reverseElements of actual nodes
9673 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9674 // while ( tmpFaceIt->more() )
9675 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9676 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9677 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9678 // aMesh->RemoveElement(*tmpFaceIt);
9679 MESSAGE("Diff nb of faces");
9680 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9683 // ============================================================
9684 // 2. Find nodes to merge:
9685 // bind a node to remove to a node to put instead
9686 // ============================================================
9688 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9689 if ( theFirstNode1 != theFirstNode2 )
9690 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9691 if ( theSecondNode1 != theSecondNode2 )
9692 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9694 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9695 set< long > linkIdSet; // links to process
9696 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9698 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9699 list< NLink > linkList[2];
9700 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9701 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9702 // loop on links in linkList; find faces by links and append links
9703 // of the found faces to linkList
9704 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9705 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9707 NLink link[] = { *linkIt[0], *linkIt[1] };
9708 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9709 if ( !linkIdSet.count( linkID ) )
9712 // by links, find faces in the face sets,
9713 // and find indices of link nodes in the found faces;
9714 // in a face set, there is only one or no face sharing a link
9715 // ---------------------------------------------------------------
9717 const SMDS_MeshElement* face[] = { 0, 0 };
9718 vector<const SMDS_MeshNode*> fnodes[2];
9719 int iLinkNode[2][2];
9720 TIDSortedElemSet avoidSet;
9721 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9722 const SMDS_MeshNode* n1 = link[iSide].first;
9723 const SMDS_MeshNode* n2 = link[iSide].second;
9724 //cout << "Side " << iSide << " ";
9725 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9726 // find a face by two link nodes
9727 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9728 *faceSetPtr[ iSide ], avoidSet,
9729 &iLinkNode[iSide][0],
9730 &iLinkNode[iSide][1] );
9733 //cout << " F " << face[ iSide]->GetID() <<endl;
9734 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9735 // put face nodes to fnodes
9736 if ( face[ iSide ]->IsQuadratic() )
9738 // use interlaced nodes iterator
9739 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9740 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9741 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9742 while ( nIter->more() )
9743 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9747 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9748 face[ iSide ]->end_nodes() );
9750 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9754 // check similarity of elements of the sides
9755 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9756 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9757 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9758 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9761 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9763 break; // do not return because it's necessary to remove tmp faces
9766 // set nodes to merge
9767 // -------------------
9769 if ( face[0] && face[1] ) {
9770 const int nbNodes = face[0]->NbNodes();
9771 if ( nbNodes != face[1]->NbNodes() ) {
9772 MESSAGE("Diff nb of face nodes");
9773 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9774 break; // do not return because it s necessary to remove tmp faces
9776 bool reverse[] = { false, false }; // order of nodes in the link
9777 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9778 // analyse link orientation in faces
9779 int i1 = iLinkNode[ iSide ][ 0 ];
9780 int i2 = iLinkNode[ iSide ][ 1 ];
9781 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9783 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9784 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9785 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9787 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9788 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9791 // add other links of the faces to linkList
9792 // -----------------------------------------
9794 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9795 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9796 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9797 if ( !iter_isnew.second ) { // already in a set: no need to process
9798 linkIdSet.erase( iter_isnew.first );
9800 else // new in set == encountered for the first time: add
9802 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9803 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9804 linkList[0].push_back ( NLink( n1, n2 ));
9805 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9810 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9813 } // loop on link lists
9815 if ( aResult == SEW_OK &&
9816 ( //linkIt[0] != linkList[0].end() ||
9817 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9818 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9819 " " << (faceSetPtr[1]->empty()));
9820 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9823 // ====================================================================
9824 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9825 // ====================================================================
9827 // delete temporary faces
9828 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9829 // while ( tmpFaceIt->more() )
9830 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9831 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9832 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9833 aMesh->RemoveElement(*tmpFaceIt);
9835 if ( aResult != SEW_OK)
9838 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9839 // loop on nodes replacement map
9840 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9841 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9842 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9843 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9844 nodeIDsToRemove.push_back( nToRemove->GetID() );
9845 // loop on elements sharing nToRemove
9846 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9847 while ( invElemIt->more() ) {
9848 const SMDS_MeshElement* e = invElemIt->next();
9849 // get a new suite of nodes: make replacement
9850 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9851 vector< const SMDS_MeshNode*> nodes( nbNodes );
9852 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9853 while ( nIt->more() ) {
9854 const SMDS_MeshNode* n =
9855 static_cast<const SMDS_MeshNode*>( nIt->next() );
9856 nnIt = nReplaceMap.find( n );
9857 if ( nnIt != nReplaceMap.end() ) {
9863 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9864 // elemIDsToRemove.push_back( e->GetID() );
9868 SMDSAbs_ElementType etyp = e->GetType();
9869 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9872 myLastCreatedElems.Append(newElem);
9873 AddToSameGroups(newElem, e, aMesh);
9874 int aShapeId = e->getshapeId();
9877 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9880 aMesh->RemoveElement(e);
9885 Remove( nodeIDsToRemove, true );
9890 //================================================================================
9892 * \brief Find corresponding nodes in two sets of faces
9893 * \param theSide1 - first face set
9894 * \param theSide2 - second first face
9895 * \param theFirstNode1 - a boundary node of set 1
9896 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9897 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9898 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9899 * \param nReplaceMap - output map of corresponding nodes
9900 * \return bool - is a success or not
9902 //================================================================================
9905 //#define DEBUG_MATCHING_NODES
9908 SMESH_MeshEditor::Sew_Error
9909 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9910 set<const SMDS_MeshElement*>& theSide2,
9911 const SMDS_MeshNode* theFirstNode1,
9912 const SMDS_MeshNode* theFirstNode2,
9913 const SMDS_MeshNode* theSecondNode1,
9914 const SMDS_MeshNode* theSecondNode2,
9915 TNodeNodeMap & nReplaceMap)
9917 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9919 nReplaceMap.clear();
9920 if ( theFirstNode1 != theFirstNode2 )
9921 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9922 if ( theSecondNode1 != theSecondNode2 )
9923 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9925 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9926 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9928 list< NLink > linkList[2];
9929 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9930 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9932 // loop on links in linkList; find faces by links and append links
9933 // of the found faces to linkList
9934 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9935 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9936 NLink link[] = { *linkIt[0], *linkIt[1] };
9937 if ( linkSet.find( link[0] ) == linkSet.end() )
9940 // by links, find faces in the face sets,
9941 // and find indices of link nodes in the found faces;
9942 // in a face set, there is only one or no face sharing a link
9943 // ---------------------------------------------------------------
9945 const SMDS_MeshElement* face[] = { 0, 0 };
9946 list<const SMDS_MeshNode*> notLinkNodes[2];
9947 //bool reverse[] = { false, false }; // order of notLinkNodes
9949 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9951 const SMDS_MeshNode* n1 = link[iSide].first;
9952 const SMDS_MeshNode* n2 = link[iSide].second;
9953 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9954 set< const SMDS_MeshElement* > facesOfNode1;
9955 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9957 // during a loop of the first node, we find all faces around n1,
9958 // during a loop of the second node, we find one face sharing both n1 and n2
9959 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9960 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9961 while ( fIt->more() ) { // loop on faces sharing a node
9962 const SMDS_MeshElement* f = fIt->next();
9963 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9964 ! facesOfNode1.insert( f ).second ) // f encounters twice
9966 if ( face[ iSide ] ) {
9967 MESSAGE( "2 faces per link " );
9968 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9971 faceSet->erase( f );
9973 // get not link nodes
9974 int nbN = f->NbNodes();
9975 if ( f->IsQuadratic() )
9977 nbNodes[ iSide ] = nbN;
9978 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9979 int i1 = f->GetNodeIndex( n1 );
9980 int i2 = f->GetNodeIndex( n2 );
9981 int iEnd = nbN, iBeg = -1, iDelta = 1;
9982 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9984 std::swap( iEnd, iBeg ); iDelta = -1;
9989 if ( i == iEnd ) i = iBeg + iDelta;
9990 if ( i == i1 ) break;
9991 nodes.push_back ( f->GetNode( i ) );
9997 // check similarity of elements of the sides
9998 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9999 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10000 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10001 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10004 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10008 // set nodes to merge
10009 // -------------------
10011 if ( face[0] && face[1] ) {
10012 if ( nbNodes[0] != nbNodes[1] ) {
10013 MESSAGE("Diff nb of face nodes");
10014 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10016 #ifdef DEBUG_MATCHING_NODES
10017 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10018 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10019 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10021 int nbN = nbNodes[0];
10023 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10024 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10025 for ( int i = 0 ; i < nbN - 2; ++i ) {
10026 #ifdef DEBUG_MATCHING_NODES
10027 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10029 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10033 // add other links of the face 1 to linkList
10034 // -----------------------------------------
10036 const SMDS_MeshElement* f0 = face[0];
10037 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10038 for ( int i = 0; i < nbN; i++ )
10040 const SMDS_MeshNode* n2 = f0->GetNode( i );
10041 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10042 linkSet.insert( SMESH_TLink( n1, n2 ));
10043 if ( !iter_isnew.second ) { // already in a set: no need to process
10044 linkSet.erase( iter_isnew.first );
10046 else // new in set == encountered for the first time: add
10048 #ifdef DEBUG_MATCHING_NODES
10049 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10050 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10052 linkList[0].push_back ( NLink( n1, n2 ));
10053 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10058 } // loop on link lists
10063 //================================================================================
10065 * \brief Create elements equal (on same nodes) to given ones
10066 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10067 * elements of the uppest dimension are duplicated.
10069 //================================================================================
10071 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10073 CrearLastCreated();
10074 SMESHDS_Mesh* mesh = GetMeshDS();
10076 // get an element type and an iterator over elements
10078 SMDSAbs_ElementType type;
10079 SMDS_ElemIteratorPtr elemIt;
10080 vector< const SMDS_MeshElement* > allElems;
10081 if ( theElements.empty() )
10083 if ( mesh->NbNodes() == 0 )
10085 // get most complex type
10086 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10087 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10088 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10090 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10091 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10096 // put all elements in the vector <allElems>
10097 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10098 elemIt = mesh->elementsIterator( type );
10099 while ( elemIt->more() )
10100 allElems.push_back( elemIt->next());
10101 elemIt = elemSetIterator( allElems );
10105 type = (*theElements.begin())->GetType();
10106 elemIt = elemSetIterator( theElements );
10109 // duplicate elements
10111 if ( type == SMDSAbs_Ball )
10113 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10114 while ( elemIt->more() )
10116 const SMDS_MeshElement* elem = elemIt->next();
10117 if ( elem->GetType() != SMDSAbs_Ball )
10119 if (( elem = mesh->AddBall( elem->GetNode(0),
10120 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10121 myLastCreatedElems.Append( elem );
10126 vector< const SMDS_MeshNode* > nodes;
10127 while ( elemIt->more() )
10129 const SMDS_MeshElement* elem = elemIt->next();
10130 if ( elem->GetType() != type )
10133 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10135 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10137 std::vector<int> quantities =
10138 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10139 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10143 AddElement( nodes, type, elem->IsPoly() );
10144 elem = 0; // myLastCreatedElems is already filled
10147 myLastCreatedElems.Append( elem );
10152 //================================================================================
10154 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10155 \param theElems - the list of elements (edges or faces) to be replicated
10156 The nodes for duplication could be found from these elements
10157 \param theNodesNot - list of nodes to NOT replicate
10158 \param theAffectedElems - the list of elements (cells and edges) to which the
10159 replicated nodes should be associated to.
10160 \return TRUE if operation has been completed successfully, FALSE otherwise
10162 //================================================================================
10164 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10165 const TIDSortedElemSet& theNodesNot,
10166 const TIDSortedElemSet& theAffectedElems )
10168 myLastCreatedElems.Clear();
10169 myLastCreatedNodes.Clear();
10171 if ( theElems.size() == 0 )
10174 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10179 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10180 // duplicate elements and nodes
10181 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10182 // replce nodes by duplications
10183 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10187 //================================================================================
10189 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10190 \param theMeshDS - mesh instance
10191 \param theElems - the elements replicated or modified (nodes should be changed)
10192 \param theNodesNot - nodes to NOT replicate
10193 \param theNodeNodeMap - relation of old node to new created node
10194 \param theIsDoubleElem - flag os to replicate element or modify
10195 \return TRUE if operation has been completed successfully, FALSE otherwise
10197 //================================================================================
10199 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10200 const TIDSortedElemSet& theElems,
10201 const TIDSortedElemSet& theNodesNot,
10202 std::map< const SMDS_MeshNode*,
10203 const SMDS_MeshNode* >& theNodeNodeMap,
10204 const bool theIsDoubleElem )
10206 MESSAGE("doubleNodes");
10207 // iterate on through element and duplicate them (by nodes duplication)
10209 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10210 for ( ; elemItr != theElems.end(); ++elemItr )
10212 const SMDS_MeshElement* anElem = *elemItr;
10216 bool isDuplicate = false;
10217 // duplicate nodes to duplicate element
10218 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10219 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10221 while ( anIter->more() )
10224 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10225 SMDS_MeshNode* aNewNode = aCurrNode;
10226 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10227 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10228 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10231 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10232 theNodeNodeMap[ aCurrNode ] = aNewNode;
10233 myLastCreatedNodes.Append( aNewNode );
10235 isDuplicate |= (aCurrNode != aNewNode);
10236 newNodes[ ind++ ] = aNewNode;
10238 if ( !isDuplicate )
10241 if ( theIsDoubleElem )
10242 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10245 MESSAGE("ChangeElementNodes");
10246 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10253 //================================================================================
10255 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10256 \param theNodes - identifiers of nodes to be doubled
10257 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10258 nodes. If list of element identifiers is empty then nodes are doubled but
10259 they not assigned to elements
10260 \return TRUE if operation has been completed successfully, FALSE otherwise
10262 //================================================================================
10264 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10265 const std::list< int >& theListOfModifiedElems )
10267 MESSAGE("DoubleNodes");
10268 myLastCreatedElems.Clear();
10269 myLastCreatedNodes.Clear();
10271 if ( theListOfNodes.size() == 0 )
10274 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10278 // iterate through nodes and duplicate them
10280 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10282 std::list< int >::const_iterator aNodeIter;
10283 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10285 int aCurr = *aNodeIter;
10286 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10292 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10295 anOldNodeToNewNode[ aNode ] = aNewNode;
10296 myLastCreatedNodes.Append( aNewNode );
10300 // Create map of new nodes for modified elements
10302 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10304 std::list< int >::const_iterator anElemIter;
10305 for ( anElemIter = theListOfModifiedElems.begin();
10306 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10308 int aCurr = *anElemIter;
10309 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10313 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10315 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10317 while ( anIter->more() )
10319 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10320 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10322 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10323 aNodeArr[ ind++ ] = aNewNode;
10326 aNodeArr[ ind++ ] = aCurrNode;
10328 anElemToNodes[ anElem ] = aNodeArr;
10331 // Change nodes of elements
10333 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10334 anElemToNodesIter = anElemToNodes.begin();
10335 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10337 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10338 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10341 MESSAGE("ChangeElementNodes");
10342 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10351 //================================================================================
10353 \brief Check if element located inside shape
10354 \return TRUE if IN or ON shape, FALSE otherwise
10356 //================================================================================
10358 template<class Classifier>
10359 bool isInside(const SMDS_MeshElement* theElem,
10360 Classifier& theClassifier,
10361 const double theTol)
10363 gp_XYZ centerXYZ (0, 0, 0);
10364 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10365 while (aNodeItr->more())
10366 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10368 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10369 theClassifier.Perform(aPnt, theTol);
10370 TopAbs_State aState = theClassifier.State();
10371 return (aState == TopAbs_IN || aState == TopAbs_ON );
10374 //================================================================================
10376 * \brief Classifier of the 3D point on the TopoDS_Face
10377 * with interaface suitable for isInside()
10379 //================================================================================
10381 struct _FaceClassifier
10383 Extrema_ExtPS _extremum;
10384 BRepAdaptor_Surface _surface;
10385 TopAbs_State _state;
10387 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10389 _extremum.Initialize( _surface,
10390 _surface.FirstUParameter(), _surface.LastUParameter(),
10391 _surface.FirstVParameter(), _surface.LastVParameter(),
10392 _surface.Tolerance(), _surface.Tolerance() );
10394 void Perform(const gp_Pnt& aPnt, double theTol)
10397 _state = TopAbs_OUT;
10398 _extremum.Perform(aPnt);
10399 if ( _extremum.IsDone() )
10400 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10401 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10403 TopAbs_State State() const
10410 //================================================================================
10412 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10413 This method is the first step of DoubleNodeElemGroupsInRegion.
10414 \param theElems - list of groups of elements (edges or faces) to be replicated
10415 \param theNodesNot - list of groups of nodes not to replicated
10416 \param theShape - shape to detect affected elements (element which geometric center
10417 located on or inside shape). If the shape is null, detection is done on faces orientations
10418 (select elements with a gravity center on the side given by faces normals).
10419 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10420 The replicated nodes should be associated to affected elements.
10421 \return groups of affected elements
10422 \sa DoubleNodeElemGroupsInRegion()
10424 //================================================================================
10426 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10427 const TIDSortedElemSet& theNodesNot,
10428 const TopoDS_Shape& theShape,
10429 TIDSortedElemSet& theAffectedElems)
10431 if ( theShape.IsNull() )
10433 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10434 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10435 std::set<const SMDS_MeshElement*> edgesToCheck;
10436 alreadyCheckedNodes.clear();
10437 alreadyCheckedElems.clear();
10438 edgesToCheck.clear();
10440 // --- iterates on elements to be replicated and get elements by back references from their nodes
10442 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10444 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10446 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10447 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10450 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10451 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10452 std::set<const SMDS_MeshNode*> nodesElem;
10454 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10455 while ( nodeItr->more() )
10457 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10458 nodesElem.insert(aNode);
10460 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10461 for (; nodit != nodesElem.end(); nodit++)
10463 MESSAGE(" noeud ");
10464 const SMDS_MeshNode* aNode = *nodit;
10465 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10467 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10469 alreadyCheckedNodes.insert(aNode);
10470 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10471 while ( backElemItr->more() )
10473 MESSAGE(" backelem ");
10474 const SMDS_MeshElement* curElem = backElemItr->next();
10475 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10477 if (theElems.find(curElem) != theElems.end())
10479 alreadyCheckedElems.insert(curElem);
10480 double x=0, y=0, z=0;
10482 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10483 while ( nodeItr2->more() )
10485 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10486 x += anotherNode->X();
10487 y += anotherNode->Y();
10488 z += anotherNode->Z();
10492 p.SetCoord( x/nb -aNode->X(),
10494 z/nb -aNode->Z() );
10495 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10498 MESSAGE(" --- inserted")
10499 theAffectedElems.insert( curElem );
10501 else if (curElem->GetType() == SMDSAbs_Edge)
10502 edgesToCheck.insert(curElem);
10506 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10507 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10508 for( ; eit != edgesToCheck.end(); eit++)
10510 bool onside = true;
10511 const SMDS_MeshElement* anEdge = *eit;
10512 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10513 while ( nodeItr->more() )
10515 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10516 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10524 MESSAGE(" --- edge onside inserted")
10525 theAffectedElems.insert(anEdge);
10531 const double aTol = Precision::Confusion();
10532 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10533 auto_ptr<_FaceClassifier> aFaceClassifier;
10534 if ( theShape.ShapeType() == TopAbs_SOLID )
10536 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10537 bsc3d->PerformInfinitePoint(aTol);
10539 else if (theShape.ShapeType() == TopAbs_FACE )
10541 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10544 // iterates on indicated elements and get elements by back references from their nodes
10545 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10547 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10549 MESSAGE("element " << ielem++);
10550 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10553 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10554 while ( nodeItr->more() )
10556 MESSAGE(" noeud ");
10557 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10558 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10560 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10561 while ( backElemItr->more() )
10563 MESSAGE(" backelem ");
10564 const SMDS_MeshElement* curElem = backElemItr->next();
10565 if ( curElem && theElems.find(curElem) == theElems.end() &&
10567 isInside( curElem, *bsc3d, aTol ) :
10568 isInside( curElem, *aFaceClassifier, aTol )))
10569 theAffectedElems.insert( curElem );
10577 //================================================================================
10579 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10580 \param theElems - group of of elements (edges or faces) to be replicated
10581 \param theNodesNot - group of nodes not to replicate
10582 \param theShape - shape to detect affected elements (element which geometric center
10583 located on or inside shape).
10584 The replicated nodes should be associated to affected elements.
10585 \return TRUE if operation has been completed successfully, FALSE otherwise
10587 //================================================================================
10589 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10590 const TIDSortedElemSet& theNodesNot,
10591 const TopoDS_Shape& theShape )
10593 if ( theShape.IsNull() )
10596 const double aTol = Precision::Confusion();
10597 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10598 auto_ptr<_FaceClassifier> aFaceClassifier;
10599 if ( theShape.ShapeType() == TopAbs_SOLID )
10601 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10602 bsc3d->PerformInfinitePoint(aTol);
10604 else if (theShape.ShapeType() == TopAbs_FACE )
10606 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10609 // iterates on indicated elements and get elements by back references from their nodes
10610 TIDSortedElemSet anAffected;
10611 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10612 for ( ; elemItr != theElems.end(); ++elemItr )
10614 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10618 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10619 while ( nodeItr->more() )
10621 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10622 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10624 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10625 while ( backElemItr->more() )
10627 const SMDS_MeshElement* curElem = backElemItr->next();
10628 if ( curElem && theElems.find(curElem) == theElems.end() &&
10630 isInside( curElem, *bsc3d, aTol ) :
10631 isInside( curElem, *aFaceClassifier, aTol )))
10632 anAffected.insert( curElem );
10636 return DoubleNodes( theElems, theNodesNot, anAffected );
10640 * \brief compute an oriented angle between two planes defined by four points.
10641 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10642 * @param p0 base of the rotation axe
10643 * @param p1 extremity of the rotation axe
10644 * @param g1 belongs to the first plane
10645 * @param g2 belongs to the second plane
10647 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10649 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10650 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10651 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10652 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10653 gp_Vec vref(p0, p1);
10656 gp_Vec n1 = vref.Crossed(v1);
10657 gp_Vec n2 = vref.Crossed(v2);
10659 return n2.AngleWithRef(n1, vref);
10661 catch ( Standard_Failure ) {
10663 return Max( v1.Magnitude(), v2.Magnitude() );
10667 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10668 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10669 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10670 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10671 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10672 * 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.
10673 * 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.
10674 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10675 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10676 * \param theElems - list of groups of volumes, where a group of volume is a set of
10677 * SMDS_MeshElements sorted by Id.
10678 * \param createJointElems - if TRUE, create the elements
10679 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10680 * the boundary between \a theDomains and the rest mesh
10681 * \return TRUE if operation has been completed successfully, FALSE otherwise
10683 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10684 bool createJointElems,
10685 bool onAllBoundaries)
10687 MESSAGE("----------------------------------------------");
10688 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10689 MESSAGE("----------------------------------------------");
10691 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10692 meshDS->BuildDownWardConnectivity(true);
10694 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10696 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10697 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10698 // build the list of nodes shared by 2 or more domains, with their domain indexes
10700 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10701 std::map<int,int>celldom; // cell vtkId --> domain
10702 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10703 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10704 faceDomains.clear();
10706 cellDomains.clear();
10707 nodeDomains.clear();
10708 std::map<int,int> emptyMap;
10709 std::set<int> emptySet;
10712 MESSAGE(".. Number of domains :"<<theElems.size());
10714 TIDSortedElemSet theRestDomElems;
10715 const int iRestDom = -1;
10716 const int idom0 = onAllBoundaries ? iRestDom : 0;
10717 const int nbDomains = theElems.size();
10719 // Check if the domains do not share an element
10720 for (int idom = 0; idom < nbDomains-1; idom++)
10722 // MESSAGE("... Check of domain #" << idom);
10723 const TIDSortedElemSet& domain = theElems[idom];
10724 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10725 for (; elemItr != domain.end(); ++elemItr)
10727 const SMDS_MeshElement* anElem = *elemItr;
10728 int idombisdeb = idom + 1 ;
10729 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10731 const TIDSortedElemSet& domainbis = theElems[idombis];
10732 if ( domainbis.count(anElem) )
10734 MESSAGE(".... Domain #" << idom);
10735 MESSAGE(".... Domain #" << idombis);
10736 throw SALOME_Exception("The domains are not disjoint.");
10743 for (int idom = 0; idom < nbDomains; idom++)
10746 // --- build a map (face to duplicate --> volume to modify)
10747 // with all the faces shared by 2 domains (group of elements)
10748 // and corresponding volume of this domain, for each shared face.
10749 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10751 MESSAGE("... Neighbors of domain #" << idom);
10752 const TIDSortedElemSet& domain = theElems[idom];
10753 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10754 for (; elemItr != domain.end(); ++elemItr)
10756 const SMDS_MeshElement* anElem = *elemItr;
10759 int vtkId = anElem->getVtkId();
10760 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10761 int neighborsVtkIds[NBMAXNEIGHBORS];
10762 int downIds[NBMAXNEIGHBORS];
10763 unsigned char downTypes[NBMAXNEIGHBORS];
10764 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10765 for (int n = 0; n < nbNeighbors; n++)
10767 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10768 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10769 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10772 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10774 // MESSAGE("Domain " << idombis);
10775 const TIDSortedElemSet& domainbis = theElems[idombis];
10776 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10778 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10780 DownIdType face(downIds[n], downTypes[n]);
10781 if (!faceDomains[face].count(idom))
10783 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10784 celldom[vtkId] = idom;
10785 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10789 theRestDomElems.insert( elem );
10790 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10791 celldom[neighborsVtkIds[n]] = iRestDom;
10799 //MESSAGE("Number of shared faces " << faceDomains.size());
10800 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10802 // --- explore the shared faces domain by domain,
10803 // explore the nodes of the face and see if they belong to a cell in the domain,
10804 // which has only a node or an edge on the border (not a shared face)
10806 for (int idomain = idom0; idomain < nbDomains; idomain++)
10808 //MESSAGE("Domain " << idomain);
10809 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10810 itface = faceDomains.begin();
10811 for (; itface != faceDomains.end(); ++itface)
10813 const std::map<int, int>& domvol = itface->second;
10814 if (!domvol.count(idomain))
10816 DownIdType face = itface->first;
10817 //MESSAGE(" --- face " << face.cellId);
10818 std::set<int> oldNodes;
10820 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10821 std::set<int>::iterator itn = oldNodes.begin();
10822 for (; itn != oldNodes.end(); ++itn)
10825 //MESSAGE(" node " << oldId);
10826 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10827 for (int i=0; i<l.ncells; i++)
10829 int vtkId = l.cells[i];
10830 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10831 if (!domain.count(anElem))
10833 int vtkType = grid->GetCellType(vtkId);
10834 int downId = grid->CellIdToDownId(vtkId);
10837 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10838 continue; // not OK at this stage of the algorithm:
10839 //no cells created after BuildDownWardConnectivity
10841 DownIdType aCell(downId, vtkType);
10842 cellDomains[aCell][idomain] = vtkId;
10843 celldom[vtkId] = idomain;
10844 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10850 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10851 // for each shared face, get the nodes
10852 // for each node, for each domain of the face, create a clone of the node
10854 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10855 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10856 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10858 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10859 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10860 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10862 MESSAGE(".. Duplication of the nodes");
10863 for (int idomain = idom0; idomain < nbDomains; idomain++)
10865 itface = faceDomains.begin();
10866 for (; itface != faceDomains.end(); ++itface)
10868 const std::map<int, int>& domvol = itface->second;
10869 if (!domvol.count(idomain))
10871 DownIdType face = itface->first;
10872 //MESSAGE(" --- face " << face.cellId);
10873 std::set<int> oldNodes;
10875 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10876 std::set<int>::iterator itn = oldNodes.begin();
10877 for (; itn != oldNodes.end(); ++itn)
10880 if (nodeDomains[oldId].empty())
10882 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10883 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10885 std::map<int, int>::const_iterator itdom = domvol.begin();
10886 for (; itdom != domvol.end(); ++itdom)
10888 int idom = itdom->first;
10889 //MESSAGE(" domain " << idom);
10890 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10892 if (nodeDomains[oldId].size() >= 2) // a multiple node
10894 vector<int> orderedDoms;
10895 //MESSAGE("multiple node " << oldId);
10896 if (mutipleNodes.count(oldId))
10897 orderedDoms = mutipleNodes[oldId];
10900 map<int,int>::iterator it = nodeDomains[oldId].begin();
10901 for (; it != nodeDomains[oldId].end(); ++it)
10902 orderedDoms.push_back(it->first);
10904 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10905 //stringstream txt;
10906 //for (int i=0; i<orderedDoms.size(); i++)
10907 // txt << orderedDoms[i] << " ";
10908 //MESSAGE("orderedDoms " << txt.str());
10909 mutipleNodes[oldId] = orderedDoms;
10911 double *coords = grid->GetPoint(oldId);
10912 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10913 int newId = newNode->getVtkId();
10914 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10915 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10922 MESSAGE(".. Creation of elements");
10923 for (int idomain = idom0; idomain < nbDomains; idomain++)
10925 itface = faceDomains.begin();
10926 for (; itface != faceDomains.end(); ++itface)
10928 std::map<int, int> domvol = itface->second;
10929 if (!domvol.count(idomain))
10931 DownIdType face = itface->first;
10932 //MESSAGE(" --- face " << face.cellId);
10933 std::set<int> oldNodes;
10935 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10936 int nbMultipleNodes = 0;
10937 std::set<int>::iterator itn = oldNodes.begin();
10938 for (; itn != oldNodes.end(); ++itn)
10941 if (mutipleNodes.count(oldId))
10944 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10946 //MESSAGE("multiple Nodes detected on a shared face");
10947 int downId = itface->first.cellId;
10948 unsigned char cellType = itface->first.cellType;
10949 // --- shared edge or shared face ?
10950 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10953 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10954 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10955 if (mutipleNodes.count(nodes[i]))
10956 if (!mutipleNodesToFace.count(nodes[i]))
10957 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10959 else // shared face (between two volumes)
10961 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10962 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10963 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10964 for (int ie =0; ie < nbEdges; ie++)
10967 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10968 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10970 vector<int> vn0 = mutipleNodes[nodes[0]];
10971 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10973 for (int i0 = 0; i0 < vn0.size(); i0++)
10974 for (int i1 = 0; i1 < vn1.size(); i1++)
10975 if (vn0[i0] == vn1[i1])
10976 doms.push_back(vn0[i0]);
10977 if (doms.size() >2)
10979 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10980 double *coords = grid->GetPoint(nodes[0]);
10981 gp_Pnt p0(coords[0], coords[1], coords[2]);
10982 coords = grid->GetPoint(nodes[nbNodes - 1]);
10983 gp_Pnt p1(coords[0], coords[1], coords[2]);
10985 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10986 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10987 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10988 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10989 for (int id=0; id < doms.size(); id++)
10991 int idom = doms[id];
10992 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10993 for (int ivol=0; ivol<nbvol; ivol++)
10995 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10996 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10997 if (domain.count(elem))
10999 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11000 domvol[idom] = svol;
11001 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11003 vtkIdType npts = 0;
11004 vtkIdType* pts = 0;
11005 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11006 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11009 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11010 angleDom[idom] = 0;
11014 gp_Pnt g(values[0], values[1], values[2]);
11015 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11016 //MESSAGE(" angle=" << angleDom[idom]);
11022 map<double, int> sortedDom; // sort domains by angle
11023 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11024 sortedDom[ia->second] = ia->first;
11025 vector<int> vnodes;
11027 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11029 vdom.push_back(ib->second);
11030 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11032 for (int ino = 0; ino < nbNodes; ino++)
11033 vnodes.push_back(nodes[ino]);
11034 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11043 // --- iterate on shared faces (volumes to modify, face to extrude)
11044 // get node id's of the face (id SMDS = id VTK)
11045 // create flat element with old and new nodes if requested
11047 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11048 // (domain1 X domain2) = domain1 + MAXINT*domain2
11050 std::map<int, std::map<long,int> > nodeQuadDomains;
11051 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11053 MESSAGE(".. Creation of elements: simple junction");
11054 if (createJointElems)
11057 string joints2DName = "joints2D";
11058 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11059 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11060 string joints3DName = "joints3D";
11061 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11062 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11064 itface = faceDomains.begin();
11065 for (; itface != faceDomains.end(); ++itface)
11067 DownIdType face = itface->first;
11068 std::set<int> oldNodes;
11069 std::set<int>::iterator itn;
11071 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11073 std::map<int, int> domvol = itface->second;
11074 std::map<int, int>::iterator itdom = domvol.begin();
11075 int dom1 = itdom->first;
11076 int vtkVolId = itdom->second;
11078 int dom2 = itdom->first;
11079 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11081 stringstream grpname;
11084 grpname << dom1 << "_" << dom2;
11086 grpname << dom2 << "_" << dom1;
11087 string namegrp = grpname.str();
11088 if (!mapOfJunctionGroups.count(namegrp))
11089 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11090 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11092 sgrp->Add(vol->GetID());
11093 if (vol->GetType() == SMDSAbs_Volume)
11094 joints3DGrp->Add(vol->GetID());
11095 else if (vol->GetType() == SMDSAbs_Face)
11096 joints2DGrp->Add(vol->GetID());
11100 // --- create volumes on multiple domain intersection if requested
11101 // iterate on mutipleNodesToFace
11102 // iterate on edgesMultiDomains
11104 MESSAGE(".. Creation of elements: multiple junction");
11105 if (createJointElems)
11107 // --- iterate on mutipleNodesToFace
11109 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11110 for (; itn != mutipleNodesToFace.end(); ++itn)
11112 int node = itn->first;
11113 vector<int> orderDom = itn->second;
11114 vector<vtkIdType> orderedNodes;
11115 for (int idom = 0; idom <orderDom.size(); idom++)
11116 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11117 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11119 stringstream grpname;
11121 grpname << 0 << "_" << 0;
11123 string namegrp = grpname.str();
11124 if (!mapOfJunctionGroups.count(namegrp))
11125 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11126 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11128 sgrp->Add(face->GetID());
11131 // --- iterate on edgesMultiDomains
11133 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11134 for (; ite != edgesMultiDomains.end(); ++ite)
11136 vector<int> nodes = ite->first;
11137 vector<int> orderDom = ite->second;
11138 vector<vtkIdType> orderedNodes;
11139 if (nodes.size() == 2)
11141 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11142 for (int ino=0; ino < nodes.size(); ino++)
11143 if (orderDom.size() == 3)
11144 for (int idom = 0; idom <orderDom.size(); idom++)
11145 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11147 for (int idom = orderDom.size()-1; idom >=0; idom--)
11148 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11149 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11152 string namegrp = "jointsMultiples";
11153 if (!mapOfJunctionGroups.count(namegrp))
11154 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11155 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11157 sgrp->Add(vol->GetID());
11161 INFOS("Quadratic multiple joints not implemented");
11162 // TODO quadratic nodes
11167 // --- list the explicit faces and edges of the mesh that need to be modified,
11168 // i.e. faces and edges built with one or more duplicated nodes.
11169 // associate these faces or edges to their corresponding domain.
11170 // only the first domain found is kept when a face or edge is shared
11172 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11173 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11174 faceOrEdgeDom.clear();
11177 MESSAGE(".. Modification of elements");
11178 for (int idomain = idom0; idomain < nbDomains; idomain++)
11180 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11181 for (; itnod != nodeDomains.end(); ++itnod)
11183 int oldId = itnod->first;
11184 //MESSAGE(" node " << oldId);
11185 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11186 for (int i = 0; i < l.ncells; i++)
11188 int vtkId = l.cells[i];
11189 int vtkType = grid->GetCellType(vtkId);
11190 int downId = grid->CellIdToDownId(vtkId);
11192 continue; // new cells: not to be modified
11193 DownIdType aCell(downId, vtkType);
11194 int volParents[1000];
11195 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11196 for (int j = 0; j < nbvol; j++)
11197 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11198 if (!feDom.count(vtkId))
11200 feDom[vtkId] = idomain;
11201 faceOrEdgeDom[aCell] = emptyMap;
11202 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11203 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11204 // << " type " << vtkType << " downId " << downId);
11210 // --- iterate on shared faces (volumes to modify, face to extrude)
11211 // get node id's of the face
11212 // replace old nodes by new nodes in volumes, and update inverse connectivity
11214 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11215 for (int m=0; m<3; m++)
11217 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11218 itface = (*amap).begin();
11219 for (; itface != (*amap).end(); ++itface)
11221 DownIdType face = itface->first;
11222 std::set<int> oldNodes;
11223 std::set<int>::iterator itn;
11225 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11226 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11227 std::map<int, int> localClonedNodeIds;
11229 std::map<int, int> domvol = itface->second;
11230 std::map<int, int>::iterator itdom = domvol.begin();
11231 for (; itdom != domvol.end(); ++itdom)
11233 int idom = itdom->first;
11234 int vtkVolId = itdom->second;
11235 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11236 localClonedNodeIds.clear();
11237 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11240 if (nodeDomains[oldId].count(idom))
11242 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11243 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11246 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11251 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11252 grid->BuildLinks();
11260 * \brief Double nodes on some external faces and create flat elements.
11261 * Flat elements are mainly used by some types of mechanic calculations.
11263 * Each group of the list must be constituted of faces.
11264 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11265 * @param theElems - list of groups of faces, where a group of faces is a set of
11266 * SMDS_MeshElements sorted by Id.
11267 * @return TRUE if operation has been completed successfully, FALSE otherwise
11269 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11271 MESSAGE("-------------------------------------------------");
11272 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11273 MESSAGE("-------------------------------------------------");
11275 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11277 // --- For each group of faces
11278 // duplicate the nodes, create a flat element based on the face
11279 // replace the nodes of the faces by their clones
11281 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11282 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11283 clonedNodes.clear();
11284 intermediateNodes.clear();
11285 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11286 mapOfJunctionGroups.clear();
11288 for (int idom = 0; idom < theElems.size(); idom++)
11290 const TIDSortedElemSet& domain = theElems[idom];
11291 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11292 for (; elemItr != domain.end(); ++elemItr)
11294 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11295 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11298 // MESSAGE("aFace=" << aFace->GetID());
11299 bool isQuad = aFace->IsQuadratic();
11300 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11302 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11304 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11305 while (nodeIt->more())
11307 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11308 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11310 ln2.push_back(node);
11312 ln0.push_back(node);
11314 const SMDS_MeshNode* clone = 0;
11315 if (!clonedNodes.count(node))
11317 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11318 clonedNodes[node] = clone;
11321 clone = clonedNodes[node];
11324 ln3.push_back(clone);
11326 ln1.push_back(clone);
11328 const SMDS_MeshNode* inter = 0;
11329 if (isQuad && (!isMedium))
11331 if (!intermediateNodes.count(node))
11333 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11334 intermediateNodes[node] = inter;
11337 inter = intermediateNodes[node];
11338 ln4.push_back(inter);
11342 // --- extrude the face
11344 vector<const SMDS_MeshNode*> ln;
11345 SMDS_MeshVolume* vol = 0;
11346 vtkIdType aType = aFace->GetVtkType();
11350 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11351 // MESSAGE("vol prism " << vol->GetID());
11352 ln.push_back(ln1[0]);
11353 ln.push_back(ln1[1]);
11354 ln.push_back(ln1[2]);
11357 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11358 // MESSAGE("vol hexa " << vol->GetID());
11359 ln.push_back(ln1[0]);
11360 ln.push_back(ln1[1]);
11361 ln.push_back(ln1[2]);
11362 ln.push_back(ln1[3]);
11364 case VTK_QUADRATIC_TRIANGLE:
11365 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11366 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11367 // MESSAGE("vol quad prism " << vol->GetID());
11368 ln.push_back(ln1[0]);
11369 ln.push_back(ln1[1]);
11370 ln.push_back(ln1[2]);
11371 ln.push_back(ln3[0]);
11372 ln.push_back(ln3[1]);
11373 ln.push_back(ln3[2]);
11375 case VTK_QUADRATIC_QUAD:
11376 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11377 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11378 // ln4[0], ln4[1], ln4[2], ln4[3]);
11379 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11380 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11381 ln4[0], ln4[1], ln4[2], ln4[3]);
11382 // MESSAGE("vol quad hexa " << vol->GetID());
11383 ln.push_back(ln1[0]);
11384 ln.push_back(ln1[1]);
11385 ln.push_back(ln1[2]);
11386 ln.push_back(ln1[3]);
11387 ln.push_back(ln3[0]);
11388 ln.push_back(ln3[1]);
11389 ln.push_back(ln3[2]);
11390 ln.push_back(ln3[3]);
11400 stringstream grpname;
11404 string namegrp = grpname.str();
11405 if (!mapOfJunctionGroups.count(namegrp))
11406 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11407 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11409 sgrp->Add(vol->GetID());
11412 // --- modify the face
11414 aFace->ChangeNodes(&ln[0], ln.size());
11421 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11422 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11423 * groups of faces to remove inside the object, (idem edges).
11424 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11426 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11427 const TopoDS_Shape& theShape,
11428 SMESH_NodeSearcher* theNodeSearcher,
11429 const char* groupName,
11430 std::vector<double>& nodesCoords,
11431 std::vector<std::vector<int> >& listOfListOfNodes)
11433 MESSAGE("--------------------------------");
11434 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11435 MESSAGE("--------------------------------");
11437 // --- zone of volumes to remove is given :
11438 // 1 either by a geom shape (one or more vertices) and a radius,
11439 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11440 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11441 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11442 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11443 // defined by it's name.
11445 SMESHDS_GroupBase* groupDS = 0;
11446 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11447 while ( groupIt->more() )
11450 SMESH_Group * group = groupIt->next();
11451 if ( !group ) continue;
11452 groupDS = group->GetGroupDS();
11453 if ( !groupDS || groupDS->IsEmpty() ) continue;
11454 std::string grpName = group->GetName();
11455 //MESSAGE("grpName=" << grpName);
11456 if (grpName == groupName)
11462 bool isNodeGroup = false;
11463 bool isNodeCoords = false;
11466 if (groupDS->GetType() != SMDSAbs_Node)
11468 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11471 if (nodesCoords.size() > 0)
11472 isNodeCoords = true; // a list o nodes given by their coordinates
11473 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11475 // --- define groups to build
11477 int idg; // --- group of SMDS volumes
11478 string grpvName = groupName;
11479 grpvName += "_vol";
11480 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11483 MESSAGE("group not created " << grpvName);
11486 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11488 int idgs; // --- group of SMDS faces on the skin
11489 string grpsName = groupName;
11490 grpsName += "_skin";
11491 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11494 MESSAGE("group not created " << grpsName);
11497 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11499 int idgi; // --- group of SMDS faces internal (several shapes)
11500 string grpiName = groupName;
11501 grpiName += "_internalFaces";
11502 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11505 MESSAGE("group not created " << grpiName);
11508 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11510 int idgei; // --- group of SMDS faces internal (several shapes)
11511 string grpeiName = groupName;
11512 grpeiName += "_internalEdges";
11513 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11516 MESSAGE("group not created " << grpeiName);
11519 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11521 // --- build downward connectivity
11523 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11524 meshDS->BuildDownWardConnectivity(true);
11525 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11527 // --- set of volumes detected inside
11529 std::set<int> setOfInsideVol;
11530 std::set<int> setOfVolToCheck;
11532 std::vector<gp_Pnt> gpnts;
11535 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11537 MESSAGE("group of nodes provided");
11538 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11539 while ( elemIt->more() )
11541 const SMDS_MeshElement* elem = elemIt->next();
11544 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11547 SMDS_MeshElement* vol = 0;
11548 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11549 while (volItr->more())
11551 vol = (SMDS_MeshElement*)volItr->next();
11552 setOfInsideVol.insert(vol->getVtkId());
11553 sgrp->Add(vol->GetID());
11557 else if (isNodeCoords)
11559 MESSAGE("list of nodes coordinates provided");
11562 while (i < nodesCoords.size()-2)
11564 double x = nodesCoords[i++];
11565 double y = nodesCoords[i++];
11566 double z = nodesCoords[i++];
11567 gp_Pnt p = gp_Pnt(x, y ,z);
11568 gpnts.push_back(p);
11569 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11573 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11575 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11576 TopTools_IndexedMapOfShape vertexMap;
11577 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11578 gp_Pnt p = gp_Pnt(0,0,0);
11579 if (vertexMap.Extent() < 1)
11582 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11584 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11585 p = BRep_Tool::Pnt(vertex);
11586 gpnts.push_back(p);
11587 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11591 if (gpnts.size() > 0)
11594 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11596 nodeId = startNode->GetID();
11597 MESSAGE("nodeId " << nodeId);
11599 double radius2 = radius*radius;
11600 MESSAGE("radius2 " << radius2);
11602 // --- volumes on start node
11604 setOfVolToCheck.clear();
11605 SMDS_MeshElement* startVol = 0;
11606 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11607 while (volItr->more())
11609 startVol = (SMDS_MeshElement*)volItr->next();
11610 setOfVolToCheck.insert(startVol->getVtkId());
11612 if (setOfVolToCheck.empty())
11614 MESSAGE("No volumes found");
11618 // --- starting with central volumes then their neighbors, check if they are inside
11619 // or outside the domain, until no more new neighbor volume is inside.
11620 // Fill the group of inside volumes
11622 std::map<int, double> mapOfNodeDistance2;
11623 mapOfNodeDistance2.clear();
11624 std::set<int> setOfOutsideVol;
11625 while (!setOfVolToCheck.empty())
11627 std::set<int>::iterator it = setOfVolToCheck.begin();
11629 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11630 bool volInside = false;
11631 vtkIdType npts = 0;
11632 vtkIdType* pts = 0;
11633 grid->GetCellPoints(vtkId, npts, pts);
11634 for (int i=0; i<npts; i++)
11636 double distance2 = 0;
11637 if (mapOfNodeDistance2.count(pts[i]))
11639 distance2 = mapOfNodeDistance2[pts[i]];
11640 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11644 double *coords = grid->GetPoint(pts[i]);
11645 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11647 for (int j=0; j<gpnts.size(); j++)
11649 double d2 = aPoint.SquareDistance(gpnts[j]);
11650 if (d2 < distance2)
11653 if (distance2 < radius2)
11657 mapOfNodeDistance2[pts[i]] = distance2;
11658 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11660 if (distance2 < radius2)
11662 volInside = true; // one or more nodes inside the domain
11663 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11669 setOfInsideVol.insert(vtkId);
11670 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11671 int neighborsVtkIds[NBMAXNEIGHBORS];
11672 int downIds[NBMAXNEIGHBORS];
11673 unsigned char downTypes[NBMAXNEIGHBORS];
11674 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11675 for (int n = 0; n < nbNeighbors; n++)
11676 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11677 setOfVolToCheck.insert(neighborsVtkIds[n]);
11681 setOfOutsideVol.insert(vtkId);
11682 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11684 setOfVolToCheck.erase(vtkId);
11688 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11689 // If yes, add the volume to the inside set
11691 bool addedInside = true;
11692 std::set<int> setOfVolToReCheck;
11693 while (addedInside)
11695 MESSAGE(" --------------------------- re check");
11696 addedInside = false;
11697 std::set<int>::iterator itv = setOfInsideVol.begin();
11698 for (; itv != setOfInsideVol.end(); ++itv)
11701 int neighborsVtkIds[NBMAXNEIGHBORS];
11702 int downIds[NBMAXNEIGHBORS];
11703 unsigned char downTypes[NBMAXNEIGHBORS];
11704 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11705 for (int n = 0; n < nbNeighbors; n++)
11706 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11707 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11709 setOfVolToCheck = setOfVolToReCheck;
11710 setOfVolToReCheck.clear();
11711 while (!setOfVolToCheck.empty())
11713 std::set<int>::iterator it = setOfVolToCheck.begin();
11715 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11717 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11718 int countInside = 0;
11719 int neighborsVtkIds[NBMAXNEIGHBORS];
11720 int downIds[NBMAXNEIGHBORS];
11721 unsigned char downTypes[NBMAXNEIGHBORS];
11722 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11723 for (int n = 0; n < nbNeighbors; n++)
11724 if (setOfInsideVol.count(neighborsVtkIds[n]))
11726 MESSAGE("countInside " << countInside);
11727 if (countInside > 1)
11729 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11730 setOfInsideVol.insert(vtkId);
11731 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11732 addedInside = true;
11735 setOfVolToReCheck.insert(vtkId);
11737 setOfVolToCheck.erase(vtkId);
11741 // --- map of Downward faces at the boundary, inside the global volume
11742 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11743 // fill group of SMDS faces inside the volume (when several volume shapes)
11744 // fill group of SMDS faces on the skin of the global volume (if skin)
11746 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11747 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11748 std::set<int>::iterator it = setOfInsideVol.begin();
11749 for (; it != setOfInsideVol.end(); ++it)
11752 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11753 int neighborsVtkIds[NBMAXNEIGHBORS];
11754 int downIds[NBMAXNEIGHBORS];
11755 unsigned char downTypes[NBMAXNEIGHBORS];
11756 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11757 for (int n = 0; n < nbNeighbors; n++)
11759 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11760 if (neighborDim == 3)
11762 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11764 DownIdType face(downIds[n], downTypes[n]);
11765 boundaryFaces[face] = vtkId;
11767 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11768 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11769 if (vtkFaceId >= 0)
11771 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11772 // find also the smds edges on this face
11773 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11774 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11775 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11776 for (int i = 0; i < nbEdges; i++)
11778 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11779 if (vtkEdgeId >= 0)
11780 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11784 else if (neighborDim == 2) // skin of the volume
11786 DownIdType face(downIds[n], downTypes[n]);
11787 skinFaces[face] = vtkId;
11788 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11789 if (vtkFaceId >= 0)
11790 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11795 // --- identify the edges constituting the wire of each subshape on the skin
11796 // define polylines with the nodes of edges, equivalent to wires
11797 // project polylines on subshapes, and partition, to get geom faces
11799 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11800 std::set<int> emptySet;
11802 std::set<int> shapeIds;
11804 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11805 while (itelem->more())
11807 const SMDS_MeshElement *elem = itelem->next();
11808 int shapeId = elem->getshapeId();
11809 int vtkId = elem->getVtkId();
11810 if (!shapeIdToVtkIdSet.count(shapeId))
11812 shapeIdToVtkIdSet[shapeId] = emptySet;
11813 shapeIds.insert(shapeId);
11815 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11818 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11819 std::set<DownIdType, DownIdCompare> emptyEdges;
11820 emptyEdges.clear();
11822 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11823 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11825 int shapeId = itShape->first;
11826 MESSAGE(" --- Shape ID --- "<< shapeId);
11827 shapeIdToEdges[shapeId] = emptyEdges;
11829 std::vector<int> nodesEdges;
11831 std::set<int>::iterator its = itShape->second.begin();
11832 for (; its != itShape->second.end(); ++its)
11835 MESSAGE(" " << vtkId);
11836 int neighborsVtkIds[NBMAXNEIGHBORS];
11837 int downIds[NBMAXNEIGHBORS];
11838 unsigned char downTypes[NBMAXNEIGHBORS];
11839 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11840 for (int n = 0; n < nbNeighbors; n++)
11842 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11844 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11845 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11846 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11848 DownIdType edge(downIds[n], downTypes[n]);
11849 if (!shapeIdToEdges[shapeId].count(edge))
11851 shapeIdToEdges[shapeId].insert(edge);
11853 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11854 nodesEdges.push_back(vtkNodeId[0]);
11855 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11856 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11862 std::list<int> order;
11864 if (nodesEdges.size() > 0)
11866 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11867 nodesEdges[0] = -1;
11868 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11869 nodesEdges[1] = -1; // do not reuse this edge
11873 int nodeTofind = order.back(); // try first to push back
11875 for (i = 0; i<nodesEdges.size(); i++)
11876 if (nodesEdges[i] == nodeTofind)
11878 if (i == nodesEdges.size())
11879 found = false; // no follower found on back
11882 if (i%2) // odd ==> use the previous one
11883 if (nodesEdges[i-1] < 0)
11887 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11888 nodesEdges[i-1] = -1;
11890 else // even ==> use the next one
11891 if (nodesEdges[i+1] < 0)
11895 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11896 nodesEdges[i+1] = -1;
11901 // try to push front
11903 nodeTofind = order.front(); // try to push front
11904 for (i = 0; i<nodesEdges.size(); i++)
11905 if (nodesEdges[i] == nodeTofind)
11907 if (i == nodesEdges.size())
11909 found = false; // no predecessor found on front
11912 if (i%2) // odd ==> use the previous one
11913 if (nodesEdges[i-1] < 0)
11917 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11918 nodesEdges[i-1] = -1;
11920 else // even ==> use the next one
11921 if (nodesEdges[i+1] < 0)
11925 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11926 nodesEdges[i+1] = -1;
11932 std::vector<int> nodes;
11933 nodes.push_back(shapeId);
11934 std::list<int>::iterator itl = order.begin();
11935 for (; itl != order.end(); itl++)
11937 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11938 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11940 listOfListOfNodes.push_back(nodes);
11943 // partition geom faces with blocFissure
11944 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11945 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11951 //================================================================================
11953 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11954 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11955 * \return TRUE if operation has been completed successfully, FALSE otherwise
11957 //================================================================================
11959 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11961 // iterates on volume elements and detect all free faces on them
11962 SMESHDS_Mesh* aMesh = GetMeshDS();
11965 //bool res = false;
11966 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11967 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11970 const SMDS_MeshVolume* volume = vIt->next();
11971 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11972 vTool.SetExternalNormal();
11973 //const bool isPoly = volume->IsPoly();
11974 const int iQuad = volume->IsQuadratic();
11975 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11977 if (!vTool.IsFreeFace(iface))
11980 vector<const SMDS_MeshNode *> nodes;
11981 int nbFaceNodes = vTool.NbFaceNodes(iface);
11982 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11984 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11985 nodes.push_back(faceNodes[inode]);
11986 if (iQuad) { // add medium nodes
11987 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11988 nodes.push_back(faceNodes[inode]);
11989 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11990 nodes.push_back(faceNodes[8]);
11992 // add new face based on volume nodes
11993 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11995 continue; // face already exsist
11997 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12001 return ( nbFree==(nbExisted+nbCreated) );
12006 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12008 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12010 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12013 //================================================================================
12015 * \brief Creates missing boundary elements
12016 * \param elements - elements whose boundary is to be checked
12017 * \param dimension - defines type of boundary elements to create
12018 * \param group - a group to store created boundary elements in
12019 * \param targetMesh - a mesh to store created boundary elements in
12020 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12021 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12022 * boundary elements will be copied into the targetMesh
12023 * \param toAddExistingBondary - if true, not only new but also pre-existing
12024 * boundary elements will be added into the new group
12025 * \param aroundElements - if true, elements will be created on boundary of given
12026 * elements else, on boundary of the whole mesh.
12027 * \return nb of added boundary elements
12029 //================================================================================
12031 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12032 Bnd_Dimension dimension,
12033 SMESH_Group* group/*=0*/,
12034 SMESH_Mesh* targetMesh/*=0*/,
12035 bool toCopyElements/*=false*/,
12036 bool toCopyExistingBoundary/*=false*/,
12037 bool toAddExistingBondary/*= false*/,
12038 bool aroundElements/*= false*/)
12040 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12041 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12042 // hope that all elements are of the same type, do not check them all
12043 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12044 throw SALOME_Exception(LOCALIZED("wrong element type"));
12047 toCopyElements = toCopyExistingBoundary = false;
12049 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12050 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12051 int nbAddedBnd = 0;
12053 // editor adding present bnd elements and optionally holding elements to add to the group
12054 SMESH_MeshEditor* presentEditor;
12055 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12056 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12058 SMESH_MesherHelper helper( *myMesh );
12059 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12060 SMDS_VolumeTool vTool;
12061 TIDSortedElemSet avoidSet;
12062 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12065 typedef vector<const SMDS_MeshNode*> TConnectivity;
12067 SMDS_ElemIteratorPtr eIt;
12068 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12069 else eIt = elemSetIterator( elements );
12071 while (eIt->more())
12073 const SMDS_MeshElement* elem = eIt->next();
12074 const int iQuad = elem->IsQuadratic();
12076 // ------------------------------------------------------------------------------------
12077 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12078 // ------------------------------------------------------------------------------------
12079 vector<const SMDS_MeshElement*> presentBndElems;
12080 vector<TConnectivity> missingBndElems;
12081 TConnectivity nodes, elemNodes;
12082 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12084 vTool.SetExternalNormal();
12085 const SMDS_MeshElement* otherVol = 0;
12086 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12088 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12089 ( !aroundElements || elements.count( otherVol )))
12091 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12092 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12093 if ( missType == SMDSAbs_Edge ) // boundary edges
12095 nodes.resize( 2+iQuad );
12096 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12098 for ( int j = 0; j < nodes.size(); ++j )
12100 if ( const SMDS_MeshElement* edge =
12101 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12102 presentBndElems.push_back( edge );
12104 missingBndElems.push_back( nodes );
12107 else // boundary face
12110 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12111 nodes.push_back( nn[inode] ); // add corner nodes
12113 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12114 nodes.push_back( nn[inode] ); // add medium nodes
12115 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12117 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12119 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12120 SMDSAbs_Face, /*noMedium=*/false ))
12121 presentBndElems.push_back( f );
12123 missingBndElems.push_back( nodes );
12125 if ( targetMesh != myMesh )
12127 // add 1D elements on face boundary to be added to a new mesh
12128 const SMDS_MeshElement* edge;
12129 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12132 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12134 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12135 if ( edge && avoidSet.insert( edge ).second )
12136 presentBndElems.push_back( edge );
12142 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12144 avoidSet.clear(), avoidSet.insert( elem );
12145 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12146 SMDS_MeshElement::iterator() );
12147 elemNodes.push_back( elemNodes[0] );
12148 nodes.resize( 2 + iQuad );
12149 const int nbLinks = elem->NbCornerNodes();
12150 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12152 nodes[0] = elemNodes[iN];
12153 nodes[1] = elemNodes[iN+1+iQuad];
12154 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12155 continue; // not free link
12157 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12158 if ( const SMDS_MeshElement* edge =
12159 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12160 presentBndElems.push_back( edge );
12162 missingBndElems.push_back( nodes );
12166 // ---------------------------------
12167 // 2. Add missing boundary elements
12168 // ---------------------------------
12169 if ( targetMesh != myMesh )
12170 // instead of making a map of nodes in this mesh and targetMesh,
12171 // we create nodes with same IDs.
12172 for ( int i = 0; i < missingBndElems.size(); ++i )
12174 TConnectivity& srcNodes = missingBndElems[i];
12175 TConnectivity nodes( srcNodes.size() );
12176 for ( inode = 0; inode < nodes.size(); ++inode )
12177 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12178 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12180 /*noMedium=*/false))
12182 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12186 for ( int i = 0; i < missingBndElems.size(); ++i )
12188 TConnectivity& nodes = missingBndElems[i];
12189 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12191 /*noMedium=*/false))
12193 SMDS_MeshElement* elem =
12194 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12197 // try to set a new element to a shape
12198 if ( myMesh->HasShapeToMesh() )
12201 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12202 const int nbN = nodes.size() / (iQuad+1 );
12203 for ( inode = 0; inode < nbN && ok; ++inode )
12205 pair<int, TopAbs_ShapeEnum> i_stype =
12206 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12207 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12208 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12210 if ( ok && mediumShapes.size() > 1 )
12212 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12213 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12214 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12216 if (( ok = ( stype_i->first != stype_i_0.first )))
12217 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12218 aMesh->IndexToShape( stype_i_0.second ));
12221 if ( ok && mediumShapes.begin()->first == missShapeType )
12222 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12226 // ----------------------------------
12227 // 3. Copy present boundary elements
12228 // ----------------------------------
12229 if ( toCopyExistingBoundary )
12230 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12232 const SMDS_MeshElement* e = presentBndElems[i];
12233 TConnectivity nodes( e->NbNodes() );
12234 for ( inode = 0; inode < nodes.size(); ++inode )
12235 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12236 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12238 else // store present elements to add them to a group
12239 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12241 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12244 } // loop on given elements
12246 // ---------------------------------------------
12247 // 4. Fill group with boundary elements
12248 // ---------------------------------------------
12251 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12252 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12253 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12255 tgtEditor.myLastCreatedElems.Clear();
12256 tgtEditor2.myLastCreatedElems.Clear();
12258 // -----------------------
12259 // 5. Copy given elements
12260 // -----------------------
12261 if ( toCopyElements && targetMesh != myMesh )
12263 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12264 else eIt = elemSetIterator( elements );
12265 while (eIt->more())
12267 const SMDS_MeshElement* elem = eIt->next();
12268 TConnectivity nodes( elem->NbNodes() );
12269 for ( inode = 0; inode < nodes.size(); ++inode )
12270 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12271 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12273 tgtEditor.myLastCreatedElems.Clear();