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"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
82 #include <gp_Trsf.hxx>
96 #include <boost/tuple/tuple.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104 using namespace SMESH::Controls;
108 template < class ELEM_SET >
109 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111 typedef SMDS_SetIterator
112 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
117 //=======================================================================
118 //function : SMESH_MeshEditor
120 //=======================================================================
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123 :myMesh( theMesh ) // theMesh may be NULL
127 //================================================================================
129 * \brief Clears myLastCreatedNodes and myLastCreatedElems
131 //================================================================================
133 void SMESH_MeshEditor::CrearLastCreated()
135 myLastCreatedNodes.Clear();
136 myLastCreatedElems.Clear();
140 //=======================================================================
144 //=======================================================================
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148 const SMDSAbs_ElementType type,
151 const double ballDiameter)
153 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154 SMDS_MeshElement* e = 0;
155 int nbnode = node.size();
156 SMESHDS_Mesh* mesh = GetMeshDS();
161 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162 else e = mesh->AddFace (node[0], node[1], node[2] );
164 else if (nbnode == 4) {
165 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
168 else if (nbnode == 6) {
169 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170 node[4], node[5], ID);
171 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
174 else if (nbnode == 7) {
175 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176 node[4], node[5], node[6], ID);
177 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
178 node[4], node[5], node[6] );
180 else if (nbnode == 8) {
181 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182 node[4], node[5], node[6], node[7], ID);
183 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
184 node[4], node[5], node[6], node[7] );
186 else if (nbnode == 9) {
187 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188 node[4], node[5], node[6], node[7], node[8], ID);
189 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
190 node[4], node[5], node[6], node[7], node[8] );
193 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194 else e = mesh->AddPolygonalFace (node );
201 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 5) {
205 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
210 else if (nbnode == 6) {
211 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], ID);
213 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 10) {
223 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7],
225 node[8], node[9], ID);
226 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7],
230 else if (nbnode == 12) {
231 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7],
233 node[8], node[9], node[10], node[11], ID);
234 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
235 node[4], node[5], node[6], node[7],
236 node[8], node[9], node[10], node[11] );
238 else if (nbnode == 13) {
239 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240 node[4], node[5], node[6], node[7],
241 node[8], node[9], node[10],node[11],
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
244 node[4], node[5], node[6], node[7],
245 node[8], node[9], node[10],node[11],
248 else if (nbnode == 15) {
249 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 node[4], node[5], node[6], node[7],
251 node[8], node[9], node[10],node[11],
252 node[12],node[13],node[14],ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
254 node[4], node[5], node[6], node[7],
255 node[8], node[9], node[10],node[11],
256 node[12],node[13],node[14] );
258 else if (nbnode == 20) {
259 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7],
261 node[8], node[9], node[10],node[11],
262 node[12],node[13],node[14],node[15],
263 node[16],node[17],node[18],node[19],ID);
264 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
265 node[4], node[5], node[6], node[7],
266 node[8], node[9], node[10],node[11],
267 node[12],node[13],node[14],node[15],
268 node[16],node[17],node[18],node[19] );
270 else if (nbnode == 27) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10],node[11],
274 node[12],node[13],node[14],node[15],
275 node[16],node[17],node[18],node[19],
276 node[20],node[21],node[22],node[23],
277 node[24],node[25],node[26], ID);
278 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
279 node[4], node[5], node[6], node[7],
280 node[8], node[9], node[10],node[11],
281 node[12],node[13],node[14],node[15],
282 node[16],node[17],node[18],node[19],
283 node[20],node[21],node[22],node[23],
284 node[24],node[25],node[26] );
291 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292 else e = mesh->AddEdge (node[0], node[1] );
294 else if ( nbnode == 3 ) {
295 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296 else e = mesh->AddEdge (node[0], node[1], node[2] );
300 case SMDSAbs_0DElement:
302 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303 else e = mesh->Add0DElement (node[0] );
308 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
313 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314 else e = mesh->AddBall (node[0], ballDiameter);
319 if ( e ) myLastCreatedElems.Append( e );
323 //=======================================================================
327 //=======================================================================
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
330 const SMDSAbs_ElementType type,
334 vector<const SMDS_MeshNode*> nodes;
335 nodes.reserve( nodeIDs.size() );
336 vector<int>::const_iterator id = nodeIDs.begin();
337 while ( id != nodeIDs.end() ) {
338 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339 nodes.push_back( node );
343 return AddElement( nodes, type, isPoly, ID );
346 //=======================================================================
348 //purpose : Remove a node or an element.
349 // Modify a compute state of sub-meshes which become empty
350 //=======================================================================
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
355 myLastCreatedElems.Clear();
356 myLastCreatedNodes.Clear();
358 SMESHDS_Mesh* aMesh = GetMeshDS();
359 set< SMESH_subMesh *> smmap;
362 list<int>::const_iterator it = theIDs.begin();
363 for ( ; it != theIDs.end(); it++ ) {
364 const SMDS_MeshElement * elem;
366 elem = aMesh->FindNode( *it );
368 elem = aMesh->FindElement( *it );
372 // Notify VERTEX sub-meshes about modification
374 const SMDS_MeshNode* node = cast2Node( elem );
375 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376 if ( int aShapeID = node->getshapeId() )
377 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380 // Find sub-meshes to notify about modification
381 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382 // while ( nodeIt->more() ) {
383 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384 // const SMDS_PositionPtr& aPosition = node->GetPosition();
385 // if ( aPosition.get() ) {
386 // if ( int aShapeID = aPosition->GetShapeId() ) {
387 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388 // smmap.insert( sm );
395 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
397 aMesh->RemoveElement( elem );
401 // Notify sub-meshes about modification
402 if ( !smmap.empty() ) {
403 set< SMESH_subMesh *>::iterator smIt;
404 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
408 // // Check if the whole mesh becomes empty
409 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
415 //================================================================================
417 * \brief Create 0D elements on all nodes of the given object except those
418 * nodes on which a 0D element already exists.
419 * \param elements - Elements on whose nodes to create 0D elements; if empty,
420 * the all mesh is treated
421 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
423 //================================================================================
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426 TIDSortedElemSet& all0DElems )
428 SMDS_ElemIteratorPtr elemIt;
429 vector< const SMDS_MeshElement* > allNodes;
430 if ( elements.empty() )
432 allNodes.reserve( GetMeshDS()->NbNodes() );
433 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434 while ( elemIt->more() )
435 allNodes.push_back( elemIt->next() );
437 elemIt = elemSetIterator( allNodes );
441 elemIt = elemSetIterator( elements );
444 while ( elemIt->more() )
446 const SMDS_MeshElement* e = elemIt->next();
447 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448 while ( nodeIt->more() )
450 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
453 all0DElems.insert( it0D->next() );
455 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456 all0DElems.insert( myLastCreatedElems.Last() );
462 //=======================================================================
463 //function : FindShape
464 //purpose : Return an index of the shape theElem is on
465 // or zero if a shape not found
466 //=======================================================================
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
470 myLastCreatedElems.Clear();
471 myLastCreatedNodes.Clear();
473 SMESHDS_Mesh * aMesh = GetMeshDS();
474 if ( aMesh->ShapeToMesh().IsNull() )
477 int aShapeID = theElem->getshapeId();
481 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482 if ( sm->Contains( theElem ))
485 if ( theElem->GetType() == SMDSAbs_Node ) {
486 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
489 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
492 TopoDS_Shape aShape; // the shape a node of theElem is on
493 if ( theElem->GetType() != SMDSAbs_Node )
495 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496 while ( nodeIt->more() ) {
497 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498 if ((aShapeID = node->getshapeId()) > 0) {
499 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500 if ( sm->Contains( theElem ))
502 if ( aShape.IsNull() )
503 aShape = aMesh->IndexToShape( aShapeID );
509 // None of nodes is on a proper shape,
510 // find the shape among ancestors of aShape on which a node is
511 if ( !aShape.IsNull() ) {
512 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513 for ( ; ancIt.More(); ancIt.Next() ) {
514 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515 if ( sm && sm->Contains( theElem ))
516 return aMesh->ShapeToIndex( ancIt.Value() );
521 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
522 while ( const SMESHDS_SubMesh* sm = smIt->next() )
523 if ( sm->Contains( theElem ))
530 //=======================================================================
531 //function : IsMedium
533 //=======================================================================
535 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
536 const SMDSAbs_ElementType typeToCheck)
538 bool isMedium = false;
539 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
540 while (it->more() && !isMedium ) {
541 const SMDS_MeshElement* elem = it->next();
542 isMedium = elem->IsMediumNode(node);
547 //=======================================================================
548 //function : shiftNodesQuadTria
549 //purpose : Shift nodes in the array corresponded to quadratic triangle
550 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
551 //=======================================================================
553 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
555 const SMDS_MeshNode* nd1 = aNodes[0];
556 aNodes[0] = aNodes[1];
557 aNodes[1] = aNodes[2];
559 const SMDS_MeshNode* nd2 = aNodes[3];
560 aNodes[3] = aNodes[4];
561 aNodes[4] = aNodes[5];
565 //=======================================================================
566 //function : nbEdgeConnectivity
567 //purpose : return number of the edges connected with the theNode.
568 // if theEdges has connections with the other type of the
569 // elements, return -1
570 //=======================================================================
572 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
574 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
576 // while(elemIt->more()) {
581 return theNode->NbInverseElements();
584 //=======================================================================
585 //function : getNodesFromTwoTria
587 //=======================================================================
589 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
590 const SMDS_MeshElement * theTria2,
591 vector< const SMDS_MeshNode*>& N1,
592 vector< const SMDS_MeshNode*>& N2)
594 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
595 if ( N1.size() < 6 ) return false;
596 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
597 if ( N2.size() < 6 ) return false;
599 int sames[3] = {-1,-1,-1};
611 if(nbsames!=2) return false;
613 shiftNodesQuadTria(N1);
615 shiftNodesQuadTria(N1);
618 i = sames[0] + sames[1] + sames[2];
620 shiftNodesQuadTria(N2);
622 // now we receive following N1 and N2 (using numeration as in the image below)
623 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
624 // i.e. first nodes from both arrays form a new diagonal
628 //=======================================================================
629 //function : InverseDiag
630 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
631 // but having other common link.
632 // Return False if args are improper
633 //=======================================================================
635 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
636 const SMDS_MeshElement * theTria2 )
638 MESSAGE("InverseDiag");
639 myLastCreatedElems.Clear();
640 myLastCreatedNodes.Clear();
642 if (!theTria1 || !theTria2)
645 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
646 if (!F1) return false;
647 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
648 if (!F2) return false;
649 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
650 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
652 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
653 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
657 // put nodes in array and find out indices of the same ones
658 const SMDS_MeshNode* aNodes [6];
659 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
661 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
662 while ( it->more() ) {
663 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
665 if ( i > 2 ) // theTria2
666 // find same node of theTria1
667 for ( int j = 0; j < 3; j++ )
668 if ( aNodes[ i ] == aNodes[ j ]) {
677 return false; // theTria1 is not a triangle
678 it = theTria2->nodesIterator();
680 if ( i == 6 && it->more() )
681 return false; // theTria2 is not a triangle
684 // find indices of 1,2 and of A,B in theTria1
685 int iA = -1, iB = 0, i1 = 0, i2 = 0;
686 for ( i = 0; i < 6; i++ ) {
687 if ( sameInd [ i ] == -1 ) {
692 if ( iA >= 0) iB = i;
696 // nodes 1 and 2 should not be the same
697 if ( aNodes[ i1 ] == aNodes[ i2 ] )
701 aNodes[ iA ] = aNodes[ i2 ];
703 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
705 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
706 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
710 } // end if(F1 && F2)
712 // check case of quadratic faces
713 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
714 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
716 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
717 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
721 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
722 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
730 vector< const SMDS_MeshNode* > N1;
731 vector< const SMDS_MeshNode* > N2;
732 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
734 // now we receive following N1 and N2 (using numeration as above image)
735 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
736 // i.e. first nodes from both arrays determ new diagonal
738 vector< const SMDS_MeshNode*> N1new( N1.size() );
739 vector< const SMDS_MeshNode*> N2new( N2.size() );
740 N1new.back() = N1.back(); // central node of biquadratic
741 N2new.back() = N2.back();
742 N1new[0] = N1[0]; N2new[0] = N1[0];
743 N1new[1] = N2[0]; N2new[1] = N1[1];
744 N1new[2] = N2[1]; N2new[2] = N2[0];
745 N1new[3] = N1[4]; N2new[3] = N1[3];
746 N1new[4] = N2[3]; N2new[4] = N2[5];
747 N1new[5] = N1[5]; N2new[5] = N1[4];
748 // change nodes in faces
749 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
750 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
752 // move the central node of biquadratic triangle
753 SMESH_MesherHelper helper( *GetMesh() );
754 for ( int is2nd = 0; is2nd < 2; ++is2nd )
756 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
757 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
758 if ( nodes.size() < 7 )
760 helper.SetSubShape( tria->getshapeId() );
761 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
765 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
766 SMESH_TNodeXYZ( nodes[4] ) +
767 SMESH_TNodeXYZ( nodes[5] )) / 3.;
772 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
773 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
774 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
776 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
777 xyz = S->Value( uv.X(), uv.Y() );
778 xyz.Transform( loc );
779 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
780 nodes[6]->getshapeId() > 0 )
781 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
783 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
788 //=======================================================================
789 //function : findTriangles
790 //purpose : find triangles sharing theNode1-theNode2 link
791 //=======================================================================
793 static bool findTriangles(const SMDS_MeshNode * theNode1,
794 const SMDS_MeshNode * theNode2,
795 const SMDS_MeshElement*& theTria1,
796 const SMDS_MeshElement*& theTria2)
798 if ( !theNode1 || !theNode2 ) return false;
800 theTria1 = theTria2 = 0;
802 set< const SMDS_MeshElement* > emap;
803 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
805 const SMDS_MeshElement* elem = it->next();
806 if ( elem->NbCornerNodes() == 3 )
809 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
811 const SMDS_MeshElement* elem = it->next();
812 if ( emap.count( elem )) {
820 // theTria1 must be element with minimum ID
821 if ( theTria2->GetID() < theTria1->GetID() )
822 std::swap( theTria2, theTria1 );
830 //=======================================================================
831 //function : InverseDiag
832 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
833 // with ones built on the same 4 nodes but having other common link.
834 // Return false if proper faces not found
835 //=======================================================================
837 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
838 const SMDS_MeshNode * theNode2)
840 myLastCreatedElems.Clear();
841 myLastCreatedNodes.Clear();
843 MESSAGE( "::InverseDiag()" );
845 const SMDS_MeshElement *tr1, *tr2;
846 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
849 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
850 if (!F1) return false;
851 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
852 if (!F2) return false;
853 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
854 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
856 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
857 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
861 // put nodes in array
862 // and find indices of 1,2 and of A in tr1 and of B in tr2
863 int i, iA1 = 0, i1 = 0;
864 const SMDS_MeshNode* aNodes1 [3];
865 SMDS_ElemIteratorPtr it;
866 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
867 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
868 if ( aNodes1[ i ] == theNode1 )
869 iA1 = i; // node A in tr1
870 else if ( aNodes1[ i ] != theNode2 )
874 const SMDS_MeshNode* aNodes2 [3];
875 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
876 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
877 if ( aNodes2[ i ] == theNode2 )
878 iB2 = i; // node B in tr2
879 else if ( aNodes2[ i ] != theNode1 )
883 // nodes 1 and 2 should not be the same
884 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
888 aNodes1[ iA1 ] = aNodes2[ i2 ];
890 aNodes2[ iB2 ] = aNodes1[ i1 ];
892 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
893 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
898 // check case of quadratic faces
899 return InverseDiag(tr1,tr2);
902 //=======================================================================
903 //function : getQuadrangleNodes
904 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
905 // fusion of triangles tr1 and tr2 having shared link on
906 // theNode1 and theNode2
907 //=======================================================================
909 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
910 const SMDS_MeshNode * theNode1,
911 const SMDS_MeshNode * theNode2,
912 const SMDS_MeshElement * tr1,
913 const SMDS_MeshElement * tr2 )
915 if( tr1->NbNodes() != tr2->NbNodes() )
917 // find the 4-th node to insert into tr1
918 const SMDS_MeshNode* n4 = 0;
919 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
921 while ( !n4 && i<3 ) {
922 const SMDS_MeshNode * n = cast2Node( it->next() );
924 bool isDiag = ( n == theNode1 || n == theNode2 );
928 // Make an array of nodes to be in a quadrangle
929 int iNode = 0, iFirstDiag = -1;
930 it = tr1->nodesIterator();
933 const SMDS_MeshNode * n = cast2Node( it->next() );
935 bool isDiag = ( n == theNode1 || n == theNode2 );
937 if ( iFirstDiag < 0 )
939 else if ( iNode - iFirstDiag == 1 )
940 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
942 else if ( n == n4 ) {
943 return false; // tr1 and tr2 should not have all the same nodes
945 theQuadNodes[ iNode++ ] = n;
947 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
948 theQuadNodes[ iNode ] = n4;
953 //=======================================================================
954 //function : DeleteDiag
955 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
956 // with a quadrangle built on the same 4 nodes.
957 // Return false if proper faces not found
958 //=======================================================================
960 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
961 const SMDS_MeshNode * theNode2)
963 myLastCreatedElems.Clear();
964 myLastCreatedNodes.Clear();
966 MESSAGE( "::DeleteDiag()" );
968 const SMDS_MeshElement *tr1, *tr2;
969 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
972 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
973 if (!F1) return false;
974 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
975 if (!F2) return false;
976 SMESHDS_Mesh * aMesh = GetMeshDS();
978 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
979 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
981 const SMDS_MeshNode* aNodes [ 4 ];
982 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
985 const SMDS_MeshElement* newElem = 0;
986 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
987 myLastCreatedElems.Append(newElem);
988 AddToSameGroups( newElem, tr1, aMesh );
989 int aShapeId = tr1->getshapeId();
992 aMesh->SetMeshElementOnShape( newElem, aShapeId );
994 aMesh->RemoveElement( tr1 );
995 aMesh->RemoveElement( tr2 );
1000 // check case of quadratic faces
1001 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1003 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1007 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1008 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1016 vector< const SMDS_MeshNode* > N1;
1017 vector< const SMDS_MeshNode* > N2;
1018 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1020 // now we receive following N1 and N2 (using numeration as above image)
1021 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1022 // i.e. first nodes from both arrays determ new diagonal
1024 const SMDS_MeshNode* aNodes[8];
1034 const SMDS_MeshElement* newElem = 0;
1035 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1036 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1037 myLastCreatedElems.Append(newElem);
1038 AddToSameGroups( newElem, tr1, aMesh );
1039 int aShapeId = tr1->getshapeId();
1042 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044 aMesh->RemoveElement( tr1 );
1045 aMesh->RemoveElement( tr2 );
1047 // remove middle node (9)
1048 GetMeshDS()->RemoveNode( N1[4] );
1053 //=======================================================================
1054 //function : Reorient
1055 //purpose : Reverse theElement orientation
1056 //=======================================================================
1058 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1060 MESSAGE("Reorient");
1061 myLastCreatedElems.Clear();
1062 myLastCreatedNodes.Clear();
1066 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1067 if ( !it || !it->more() )
1070 const SMDSAbs_ElementType type = theElem->GetType();
1071 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1074 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1075 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1077 const SMDS_VtkVolume* aPolyedre =
1078 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1080 MESSAGE("Warning: bad volumic element");
1083 const int nbFaces = aPolyedre->NbFaces();
1084 vector<const SMDS_MeshNode *> poly_nodes;
1085 vector<int> quantities (nbFaces);
1087 // reverse each face of the polyedre
1088 for (int iface = 1; iface <= nbFaces; iface++) {
1089 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1090 quantities[iface - 1] = nbFaceNodes;
1092 for (inode = nbFaceNodes; inode >= 1; inode--) {
1093 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1094 poly_nodes.push_back(curNode);
1097 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1099 else // other elements
1101 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1102 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1103 if ( interlace.empty() )
1105 std::reverse( nodes.begin(), nodes.end() ); // polygon
1107 else if ( interlace.size() > 1 )
1109 SMDS_MeshCell::applyInterlace( interlace, nodes );
1111 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1116 //================================================================================
1118 * \brief Reorient faces.
1119 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1120 * \param theDirection - desired direction of normal of \a theFace
1121 * \param theFace - one of \a theFaces that sould be oriented according to
1122 * \a theDirection and whose orientation defines orientation of other faces
1123 * \return number of reoriented faces.
1125 //================================================================================
1127 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1128 const gp_Dir& theDirection,
1129 const SMDS_MeshElement * theFace)
1132 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1134 if ( theFaces.empty() )
1136 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1137 while ( fIt->more() )
1138 theFaces.insert( theFaces.end(), fIt->next() );
1141 // orient theFace according to theDirection
1143 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1144 if ( normal * theDirection.XYZ() < 0 )
1145 nbReori += Reorient( theFace );
1147 // Orient other faces
1149 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1150 TIDSortedElemSet avoidSet;
1151 set< SMESH_TLink > checkedLinks;
1152 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1154 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1155 theFaces.erase( theFace );
1156 startFaces.insert( theFace );
1158 int nodeInd1, nodeInd2;
1159 const SMDS_MeshElement* otherFace;
1160 vector< const SMDS_MeshElement* > facesNearLink;
1161 vector< std::pair< int, int > > nodeIndsOfFace;
1163 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1164 while ( !startFaces.empty() )
1166 startFace = startFaces.begin();
1167 theFace = *startFace;
1168 startFaces.erase( startFace );
1169 if ( !visitedFaces.insert( theFace ).second )
1173 avoidSet.insert(theFace);
1175 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1177 const int nbNodes = theFace->NbCornerNodes();
1178 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1180 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1181 linkIt_isNew = checkedLinks.insert( link );
1182 if ( !linkIt_isNew.second )
1184 // link has already been checked and won't be encountered more
1185 // if the group (theFaces) is manifold
1186 //checkedLinks.erase( linkIt_isNew.first );
1190 facesNearLink.clear();
1191 nodeIndsOfFace.clear();
1192 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1194 &nodeInd1, &nodeInd2 )))
1195 if ( otherFace != theFace)
1197 facesNearLink.push_back( otherFace );
1198 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1199 avoidSet.insert( otherFace );
1201 if ( facesNearLink.size() > 1 )
1203 // NON-MANIFOLD mesh shell !
1204 // select a face most co-directed with theFace,
1205 // other faces won't be visited this time
1207 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1208 double proj, maxProj = -1;
1209 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1210 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1211 if (( proj = Abs( NF * NOF )) > maxProj ) {
1213 otherFace = facesNearLink[i];
1214 nodeInd1 = nodeIndsOfFace[i].first;
1215 nodeInd2 = nodeIndsOfFace[i].second;
1218 // not to visit rejected faces
1219 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1220 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1221 visitedFaces.insert( facesNearLink[i] );
1223 else if ( facesNearLink.size() == 1 )
1225 otherFace = facesNearLink[0];
1226 nodeInd1 = nodeIndsOfFace.back().first;
1227 nodeInd2 = nodeIndsOfFace.back().second;
1229 if ( otherFace && otherFace != theFace)
1231 // link must be reverse in otherFace if orientation ot otherFace
1232 // is same as that of theFace
1233 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1235 nbReori += Reorient( otherFace );
1237 startFaces.insert( otherFace );
1240 std::swap( link.first, link.second ); // reverse the link
1246 //================================================================================
1248 * \brief Reorient faces basing on orientation of adjacent volumes.
1249 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1250 * \param theVolumes - reference volumes.
1251 * \param theOutsideNormal - to orient faces to have their normal
1252 * pointing either \a outside or \a inside the adjacent volumes.
1253 * \return number of reoriented faces.
1255 //================================================================================
1257 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1258 TIDSortedElemSet & theVolumes,
1259 const bool theOutsideNormal)
1263 SMDS_ElemIteratorPtr faceIt;
1264 if ( theFaces.empty() )
1265 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1267 faceIt = elemSetIterator( theFaces );
1269 vector< const SMDS_MeshNode* > faceNodes;
1270 TIDSortedElemSet checkedVolumes;
1271 set< const SMDS_MeshNode* > faceNodesSet;
1272 SMDS_VolumeTool volumeTool;
1274 while ( faceIt->more() ) // loop on given faces
1276 const SMDS_MeshElement* face = faceIt->next();
1277 if ( face->GetType() != SMDSAbs_Face )
1280 const int nbCornersNodes = face->NbCornerNodes();
1281 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1283 checkedVolumes.clear();
1284 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1285 while ( vIt->more() )
1287 const SMDS_MeshElement* volume = vIt->next();
1289 if ( !checkedVolumes.insert( volume ).second )
1291 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1294 // is volume adjacent?
1295 bool allNodesCommon = true;
1296 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1297 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1298 if ( !allNodesCommon )
1301 // get nodes of a corresponding volume facet
1302 faceNodesSet.clear();
1303 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1304 volumeTool.Set( volume );
1305 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1306 if ( facetID < 0 ) continue;
1307 volumeTool.SetExternalNormal();
1308 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1310 // compare order of faceNodes and facetNodes
1311 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1313 for ( int i = 0; i < 2; ++i )
1315 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1316 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1317 if ( faceNodes[ iN ] == n )
1323 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1324 if ( isOutside != theOutsideNormal )
1325 nbReori += Reorient( face );
1327 } // loop on given faces
1332 //=======================================================================
1333 //function : getBadRate
1335 //=======================================================================
1337 static double getBadRate (const SMDS_MeshElement* theElem,
1338 SMESH::Controls::NumericalFunctorPtr& theCrit)
1340 SMESH::Controls::TSequenceOfXYZ P;
1341 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1343 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1344 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1347 //=======================================================================
1348 //function : QuadToTri
1349 //purpose : Cut quadrangles into triangles.
1350 // theCrit is used to select a diagonal to cut
1351 //=======================================================================
1353 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1354 SMESH::Controls::NumericalFunctorPtr theCrit)
1356 myLastCreatedElems.Clear();
1357 myLastCreatedNodes.Clear();
1359 if ( !theCrit.get() )
1362 SMESHDS_Mesh * aMesh = GetMeshDS();
1364 Handle(Geom_Surface) surface;
1365 SMESH_MesherHelper helper( *GetMesh() );
1367 TIDSortedElemSet::iterator itElem;
1368 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1370 const SMDS_MeshElement* elem = *itElem;
1371 if ( !elem || elem->GetType() != SMDSAbs_Face )
1373 if ( elem->NbCornerNodes() != 4 )
1376 // retrieve element nodes
1377 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1379 // compare two sets of possible triangles
1380 double aBadRate1, aBadRate2; // to what extent a set is bad
1381 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1382 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1383 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1385 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1386 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1387 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1389 const int aShapeId = FindShape( elem );
1390 const SMDS_MeshElement* newElem1 = 0;
1391 const SMDS_MeshElement* newElem2 = 0;
1393 if ( !elem->IsQuadratic() ) // split liner quadrangle
1395 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1396 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1397 if ( aBadRate1 <= aBadRate2 ) {
1398 // tr1 + tr2 is better
1399 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1400 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1403 // tr3 + tr4 is better
1404 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1405 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1408 else // split quadratic quadrangle
1410 helper.SetIsQuadratic( true );
1411 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1413 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1414 if ( aNodes.size() == 9 )
1416 helper.SetIsBiQuadratic( true );
1417 if ( aBadRate1 <= aBadRate2 )
1418 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1420 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1422 // create a new element
1423 if ( aBadRate1 <= aBadRate2 ) {
1424 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1425 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1428 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1429 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1433 // care of a new element
1435 myLastCreatedElems.Append(newElem1);
1436 myLastCreatedElems.Append(newElem2);
1437 AddToSameGroups( newElem1, elem, aMesh );
1438 AddToSameGroups( newElem2, elem, aMesh );
1440 // put a new triangle on the same shape
1442 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1443 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1445 aMesh->RemoveElement( elem );
1450 //=======================================================================
1452 * \brief Split each of given quadrangles into 4 triangles.
1453 * \param theElems - The faces to be splitted. If empty all faces are split.
1455 //=======================================================================
1457 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1459 myLastCreatedElems.Clear();
1460 myLastCreatedNodes.Clear();
1462 SMESH_MesherHelper helper( *GetMesh() );
1463 helper.SetElementsOnShape( true );
1465 SMDS_ElemIteratorPtr faceIt;
1466 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1467 else faceIt = elemSetIterator( theElems );
1470 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1472 vector< const SMDS_MeshNode* > nodes;
1473 SMESHDS_SubMesh* subMeshDS;
1475 Handle(Geom_Surface) surface;
1476 TopLoc_Location loc;
1478 while ( faceIt->more() )
1480 const SMDS_MeshElement* quad = faceIt->next();
1481 if ( !quad || quad->NbCornerNodes() != 4 )
1484 // get a surface the quad is on
1486 if ( quad->getshapeId() < 1 )
1489 helper.SetSubShape( 0 );
1492 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1494 helper.SetSubShape( quad->getshapeId() );
1495 if ( !helper.GetSubShape().IsNull() &&
1496 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1498 F = TopoDS::Face( helper.GetSubShape() );
1499 surface = BRep_Tool::Surface( F, loc );
1500 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1504 helper.SetSubShape( 0 );
1509 // create a central node
1511 const SMDS_MeshNode* nCentral;
1512 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1514 if ( nodes.size() == 9 )
1516 nCentral = nodes.back();
1523 for ( ; iN < nodes.size(); ++iN )
1524 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1526 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1527 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1529 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1530 xyz[0], xyz[1], xyz[2], xyz[3],
1531 xyz[4], xyz[5], xyz[6], xyz[7] );
1535 for ( ; iN < nodes.size(); ++iN )
1536 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1538 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1539 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1541 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1542 uv[0], uv[1], uv[2], uv[3],
1543 uv[4], uv[5], uv[6], uv[7] );
1545 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1549 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1550 uv[8].X(), uv[8].Y() );
1551 myLastCreatedNodes.Append( nCentral );
1554 // create 4 triangles
1556 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1558 helper.SetIsQuadratic ( nodes.size() > 4 );
1559 helper.SetIsBiQuadratic( nodes.size() == 9 );
1560 if ( helper.GetIsQuadratic() )
1561 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1563 for ( int i = 0; i < 4; ++i )
1565 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1568 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1569 myLastCreatedElems.Append( tria );
1574 //=======================================================================
1575 //function : BestSplit
1576 //purpose : Find better diagonal for cutting.
1577 //=======================================================================
1579 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1580 SMESH::Controls::NumericalFunctorPtr theCrit)
1582 myLastCreatedElems.Clear();
1583 myLastCreatedNodes.Clear();
1588 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1591 if( theQuad->NbNodes()==4 ||
1592 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1594 // retrieve element nodes
1595 const SMDS_MeshNode* aNodes [4];
1596 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1598 //while (itN->more())
1600 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1602 // compare two sets of possible triangles
1603 double aBadRate1, aBadRate2; // to what extent a set is bad
1604 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1605 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1606 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1608 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1609 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1610 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1611 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1612 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1613 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1614 return 1; // diagonal 1-3
1616 return 2; // diagonal 2-4
1623 // Methods of splitting volumes into tetra
1625 const int theHexTo5_1[5*4+1] =
1627 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1629 const int theHexTo5_2[5*4+1] =
1631 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1633 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1635 const int theHexTo6_1[6*4+1] =
1637 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
1639 const int theHexTo6_2[6*4+1] =
1641 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
1643 const int theHexTo6_3[6*4+1] =
1645 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
1647 const int theHexTo6_4[6*4+1] =
1649 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
1651 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1653 const int thePyraTo2_1[2*4+1] =
1655 0, 1, 2, 4, 0, 2, 3, 4, -1
1657 const int thePyraTo2_2[2*4+1] =
1659 1, 2, 3, 4, 1, 3, 0, 4, -1
1661 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1663 const int thePentaTo3_1[3*4+1] =
1665 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1667 const int thePentaTo3_2[3*4+1] =
1669 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1671 const int thePentaTo3_3[3*4+1] =
1673 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1675 const int thePentaTo3_4[3*4+1] =
1677 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1679 const int thePentaTo3_5[3*4+1] =
1681 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1683 const int thePentaTo3_6[3*4+1] =
1685 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1687 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1688 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1690 // Methods of splitting hexahedron into prisms
1692 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1694 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
1696 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1698 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
1700 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1702 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
1705 const int theHexTo2Prisms_BT_1[6*2+1] =
1707 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1709 const int theHexTo2Prisms_BT_2[6*2+1] =
1711 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1713 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1715 const int theHexTo2Prisms_LR_1[6*2+1] =
1717 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1719 const int theHexTo2Prisms_LR_2[6*2+1] =
1721 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1723 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1725 const int theHexTo2Prisms_FB_1[6*2+1] =
1727 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1729 const int theHexTo2Prisms_FB_2[6*2+1] =
1731 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1733 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1736 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1739 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1740 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1741 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1742 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1748 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1749 bool _baryNode; //!< additional node is to be created at cell barycenter
1750 bool _ownConn; //!< to delete _connectivity in destructor
1751 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1753 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1754 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1755 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1756 bool hasFacet( const TTriangleFacet& facet ) const
1758 if ( _nbCorners == 4 )
1760 const int* tetConn = _connectivity;
1761 for ( ; tetConn[0] >= 0; tetConn += 4 )
1762 if (( facet.contains( tetConn[0] ) +
1763 facet.contains( tetConn[1] ) +
1764 facet.contains( tetConn[2] ) +
1765 facet.contains( tetConn[3] )) == 3 )
1768 else // prism, _nbCorners == 6
1770 const int* prismConn = _connectivity;
1771 for ( ; prismConn[0] >= 0; prismConn += 6 )
1773 if (( facet.contains( prismConn[0] ) &&
1774 facet.contains( prismConn[1] ) &&
1775 facet.contains( prismConn[2] ))
1777 ( facet.contains( prismConn[3] ) &&
1778 facet.contains( prismConn[4] ) &&
1779 facet.contains( prismConn[5] )))
1787 //=======================================================================
1789 * \brief return TSplitMethod for the given element to split into tetrahedra
1791 //=======================================================================
1793 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1795 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1797 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1798 // an edge and a face barycenter; tertaherdons are based on triangles and
1799 // a volume barycenter
1800 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1802 // Find out how adjacent volumes are split
1804 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1805 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1806 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1808 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1809 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1810 if ( nbNodes < 4 ) continue;
1812 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1813 const int* nInd = vol.GetFaceNodesIndices( iF );
1816 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1817 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1818 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1819 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1823 int iCom = 0; // common node of triangle faces to split into
1824 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1826 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1827 nInd[ iQ * ( (iCom+1)%nbNodes )],
1828 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1829 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1830 nInd[ iQ * ( (iCom+2)%nbNodes )],
1831 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1832 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1834 triaSplits.push_back( t012 );
1835 triaSplits.push_back( t023 );
1840 if ( !triaSplits.empty() )
1841 hasAdjacentSplits = true;
1844 // Among variants of split method select one compliant with adjacent volumes
1846 TSplitMethod method;
1847 if ( !vol.Element()->IsPoly() && !is24TetMode )
1849 int nbVariants = 2, nbTet = 0;
1850 const int** connVariants = 0;
1851 switch ( vol.Element()->GetEntityType() )
1853 case SMDSEntity_Hexa:
1854 case SMDSEntity_Quad_Hexa:
1855 case SMDSEntity_TriQuad_Hexa:
1856 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1857 connVariants = theHexTo5, nbTet = 5;
1859 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1861 case SMDSEntity_Pyramid:
1862 case SMDSEntity_Quad_Pyramid:
1863 connVariants = thePyraTo2; nbTet = 2;
1865 case SMDSEntity_Penta:
1866 case SMDSEntity_Quad_Penta:
1867 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1872 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1874 // check method compliancy with adjacent tetras,
1875 // all found splits must be among facets of tetras described by this method
1876 method = TSplitMethod( nbTet, connVariants[variant] );
1877 if ( hasAdjacentSplits && method._nbSplits > 0 )
1879 bool facetCreated = true;
1880 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1882 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1883 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1884 facetCreated = method.hasFacet( *facet );
1886 if ( !facetCreated )
1887 method = TSplitMethod(0); // incompatible method
1891 if ( method._nbSplits < 1 )
1893 // No standard method is applicable, use a generic solution:
1894 // each facet of a volume is split into triangles and
1895 // each of triangles and a volume barycenter form a tetrahedron.
1897 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1899 int* connectivity = new int[ maxTetConnSize + 1 ];
1900 method._connectivity = connectivity;
1901 method._ownConn = true;
1902 method._baryNode = !isHex27; // to create central node or not
1905 int baryCenInd = vol.NbNodes() - int( isHex27 );
1906 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1908 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1909 const int* nInd = vol.GetFaceNodesIndices( iF );
1910 // find common node of triangle facets of tetra to create
1911 int iCommon = 0; // index in linear numeration
1912 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1913 if ( !triaSplits.empty() )
1916 const TTriangleFacet* facet = &triaSplits.front();
1917 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1918 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1919 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1922 else if ( nbNodes > 3 && !is24TetMode )
1924 // find the best method of splitting into triangles by aspect ratio
1925 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1926 map< double, int > badness2iCommon;
1927 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1928 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1929 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1932 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1934 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1935 nodes[ iQ*((iLast-1)%nbNodes)],
1936 nodes[ iQ*((iLast )%nbNodes)]);
1937 badness += getBadRate( &tria, aspectRatio );
1939 badness2iCommon.insert( make_pair( badness, iCommon ));
1941 // use iCommon with lowest badness
1942 iCommon = badness2iCommon.begin()->second;
1944 if ( iCommon >= nbNodes )
1945 iCommon = 0; // something wrong
1947 // fill connectivity of tetrahedra based on a current face
1948 int nbTet = nbNodes - 2;
1949 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1954 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1955 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1959 method._faceBaryNode[ iF ] = 0;
1960 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1963 for ( int i = 0; i < nbTet; ++i )
1965 int i1 = i, i2 = (i+1) % nbNodes;
1966 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1967 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1968 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1969 connectivity[ connSize++ ] = faceBaryCenInd;
1970 connectivity[ connSize++ ] = baryCenInd;
1975 for ( int i = 0; i < nbTet; ++i )
1977 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1978 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1979 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1980 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1981 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1982 connectivity[ connSize++ ] = baryCenInd;
1985 method._nbSplits += nbTet;
1987 } // loop on volume faces
1989 connectivity[ connSize++ ] = -1;
1991 } // end of generic solution
1995 //=======================================================================
1997 * \brief return TSplitMethod to split haxhedron into prisms
1999 //=======================================================================
2001 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2002 const int methodFlags,
2003 const int facetToSplit)
2005 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2007 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2009 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2011 static TSplitMethod to4methods[4]; // order BT, LR, FB
2012 if ( to4methods[iF]._nbSplits == 0 )
2016 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2017 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2018 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2021 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2022 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2023 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2026 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2027 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2028 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2030 default: return to4methods[3];
2032 to4methods[iF]._nbSplits = 4;
2033 to4methods[iF]._nbCorners = 6;
2035 return to4methods[iF];
2037 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2039 TSplitMethod method;
2041 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2043 const int nbVariants = 2, nbSplits = 2;
2044 const int** connVariants = 0;
2046 case 0: connVariants = theHexTo2Prisms_BT; break;
2047 case 1: connVariants = theHexTo2Prisms_LR; break;
2048 case 2: connVariants = theHexTo2Prisms_FB; break;
2049 default: return method;
2052 // look for prisms adjacent via facetToSplit and an opposite one
2053 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2055 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2056 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2057 if ( nbNodes != 4 ) return method;
2059 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2060 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2061 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2063 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2065 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2070 // there are adjacent prism
2071 for ( int variant = 0; variant < nbVariants; ++variant )
2073 // check method compliancy with adjacent prisms,
2074 // the found prism facets must be among facets of prisms described by current method
2075 method._nbSplits = nbSplits;
2076 method._nbCorners = 6;
2077 method._connectivity = connVariants[ variant ];
2078 if ( method.hasFacet( *t ))
2083 // No adjacent prisms. Select a variant with a best aspect ratio.
2085 double badness[2] = { 0, 0 };
2086 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2087 const SMDS_MeshNode** nodes = vol.GetNodes();
2088 for ( int variant = 0; variant < nbVariants; ++variant )
2089 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2091 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2092 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2094 method._connectivity = connVariants[ variant ];
2095 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2096 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2097 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2099 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2102 badness[ variant ] += getBadRate( &tria, aspectRatio );
2104 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2106 method._nbSplits = nbSplits;
2107 method._nbCorners = 6;
2108 method._connectivity = connVariants[ iBetter ];
2113 //================================================================================
2115 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2117 //================================================================================
2119 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2120 const SMDSAbs_GeometryType geom ) const
2122 // find the tetrahedron including the three nodes of facet
2123 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2124 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2125 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2126 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2127 while ( volIt1->more() )
2129 const SMDS_MeshElement* v = volIt1->next();
2130 if ( v->GetGeomType() != geom )
2132 const int lastCornerInd = v->NbCornerNodes() - 1;
2133 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2134 continue; // medium node not allowed
2135 const int ind2 = v->GetNodeIndex( n2 );
2136 if ( ind2 < 0 || lastCornerInd < ind2 )
2138 const int ind3 = v->GetNodeIndex( n3 );
2139 if ( ind3 < 0 || lastCornerInd < ind3 )
2146 //=======================================================================
2148 * \brief A key of a face of volume
2150 //=======================================================================
2152 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2154 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2156 TIDSortedNodeSet sortedNodes;
2157 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2158 int nbNodes = vol.NbFaceNodes( iF );
2159 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2160 for ( int i = 0; i < nbNodes; i += iQ )
2161 sortedNodes.insert( fNodes[i] );
2162 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2163 first.first = (*(n++))->GetID();
2164 first.second = (*(n++))->GetID();
2165 second.first = (*(n++))->GetID();
2166 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2171 //=======================================================================
2172 //function : SplitVolumes
2173 //purpose : Split volume elements into tetrahedra or prisms.
2174 // If facet ID < 0, element is split into tetrahedra,
2175 // else a hexahedron is split into prisms so that the given facet is
2176 // split into triangles
2177 //=======================================================================
2179 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2180 const int theMethodFlags)
2182 // std-like iterator on coordinates of nodes of mesh element
2183 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2184 NXyzIterator xyzEnd;
2186 SMDS_VolumeTool volTool;
2187 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2188 fHelper.ToFixNodeParameters( true );
2190 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2191 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2193 SMESH_SequenceOfElemPtr newNodes, newElems;
2195 // map face of volume to it's baricenrtic node
2196 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2199 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2200 for ( ; elem2facet != theElems.end(); ++elem2facet )
2202 const SMDS_MeshElement* elem = elem2facet->first;
2203 const int facetToSplit = elem2facet->second;
2204 if ( elem->GetType() != SMDSAbs_Volume )
2206 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2207 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2210 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2212 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2213 getTetraSplitMethod( volTool, theMethodFlags ) :
2214 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2215 if ( splitMethod._nbSplits < 1 ) continue;
2217 // find submesh to add new tetras to
2218 if ( !subMesh || !subMesh->Contains( elem ))
2220 int shapeID = FindShape( elem );
2221 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2222 subMesh = GetMeshDS()->MeshElements( shapeID );
2225 if ( elem->IsQuadratic() )
2228 // add quadratic links to the helper
2229 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2231 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2232 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2233 for ( int iN = 0; iN < nbN; iN += iQ )
2234 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2236 helper.SetIsQuadratic( true );
2241 helper.SetIsQuadratic( false );
2243 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2244 volTool.GetNodes() + elem->NbNodes() );
2245 helper.SetElementsOnShape( true );
2246 if ( splitMethod._baryNode )
2248 // make a node at barycenter
2249 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2250 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2251 nodes.push_back( gcNode );
2252 newNodes.Append( gcNode );
2254 if ( !splitMethod._faceBaryNode.empty() )
2256 // make or find baricentric nodes of faces
2257 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2258 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2260 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2261 volFace2BaryNode.insert
2262 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2265 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2266 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2268 nodes.push_back( iF_n->second = f_n->second );
2273 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2274 const int* volConn = splitMethod._connectivity;
2275 if ( splitMethod._nbCorners == 4 ) // tetra
2276 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2277 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2278 nodes[ volConn[1] ],
2279 nodes[ volConn[2] ],
2280 nodes[ volConn[3] ]));
2282 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2283 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2284 nodes[ volConn[1] ],
2285 nodes[ volConn[2] ],
2286 nodes[ volConn[3] ],
2287 nodes[ volConn[4] ],
2288 nodes[ volConn[5] ]));
2290 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2292 // Split faces on sides of the split volume
2294 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2295 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2297 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2298 if ( nbNodes < 4 ) continue;
2300 // find an existing face
2301 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2302 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2303 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2304 /*noMedium=*/false))
2307 helper.SetElementsOnShape( false );
2308 vector< const SMDS_MeshElement* > triangles;
2310 // find submesh to add new triangles in
2311 if ( !fSubMesh || !fSubMesh->Contains( face ))
2313 int shapeID = FindShape( face );
2314 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2316 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2317 if ( iF_n != splitMethod._faceBaryNode.end() )
2319 const SMDS_MeshNode *baryNode = iF_n->second;
2320 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2322 const SMDS_MeshNode* n1 = fNodes[iN];
2323 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2324 const SMDS_MeshNode *n3 = baryNode;
2325 if ( !volTool.IsFaceExternal( iF ))
2327 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2329 if ( fSubMesh ) // update position of the bary node on geometry
2332 subMesh->RemoveNode( baryNode, false );
2333 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2334 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2335 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2337 fHelper.SetSubShape( s );
2338 gp_XY uv( 1e100, 1e100 );
2340 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2341 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2344 // node is too far from the surface
2345 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2346 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2347 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2354 // among possible triangles create ones discribed by split method
2355 const int* nInd = volTool.GetFaceNodesIndices( iF );
2356 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2357 int iCom = 0; // common node of triangle faces to split into
2358 list< TTriangleFacet > facets;
2359 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2361 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2362 nInd[ iQ * ( (iCom+1)%nbNodes )],
2363 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2364 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2365 nInd[ iQ * ( (iCom+2)%nbNodes )],
2366 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2367 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2369 facets.push_back( t012 );
2370 facets.push_back( t023 );
2371 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2372 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2373 nInd[ iQ * ((iLast-1)%nbNodes )],
2374 nInd[ iQ * ((iLast )%nbNodes )]));
2378 list< TTriangleFacet >::iterator facet = facets.begin();
2379 if ( facet == facets.end() )
2381 for ( ; facet != facets.end(); ++facet )
2383 if ( !volTool.IsFaceExternal( iF ))
2384 swap( facet->_n2, facet->_n3 );
2385 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2386 volNodes[ facet->_n2 ],
2387 volNodes[ facet->_n3 ]));
2390 for ( int i = 0; i < triangles.size(); ++i )
2392 if ( !triangles[i] ) continue;
2394 fSubMesh->AddElement( triangles[i]);
2395 newElems.Append( triangles[i] );
2397 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2398 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2400 } // while a face based on facet nodes exists
2401 } // loop on volume faces to split them into triangles
2403 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2405 if ( geomType == SMDSEntity_TriQuad_Hexa )
2407 // remove medium nodes that could become free
2408 for ( int i = 20; i < volTool.NbNodes(); ++i )
2409 if ( volNodes[i]->NbInverseElements() == 0 )
2410 GetMeshDS()->RemoveNode( volNodes[i] );
2412 } // loop on volumes to split
2414 myLastCreatedNodes = newNodes;
2415 myLastCreatedElems = newElems;
2418 //=======================================================================
2419 //function : GetHexaFacetsToSplit
2420 //purpose : For hexahedra that will be split into prisms, finds facets to
2421 // split into triangles. Only hexahedra adjacent to the one closest
2422 // to theFacetNormal.Location() are returned.
2423 //param [in,out] theHexas - the hexahedra
2424 //param [in] theFacetNormal - facet normal
2425 //param [out] theFacets - the hexahedra and found facet IDs
2426 //=======================================================================
2428 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2429 const gp_Ax1& theFacetNormal,
2430 TFacetOfElem & theFacets)
2432 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2434 // Find a hexa closest to the location of theFacetNormal
2436 const SMDS_MeshElement* startHex;
2438 // get SMDS_ElemIteratorPtr on theHexas
2439 typedef const SMDS_MeshElement* TValue;
2440 typedef TIDSortedElemSet::iterator TSetIterator;
2441 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2442 typedef SMDS_MeshElement::GeomFilter TFilter;
2443 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2444 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2445 ( new TElemSetIter( theHexas.begin(),
2447 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2449 SMESH_ElementSearcher* searcher =
2450 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2452 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2457 throw SALOME_Exception( THIS_METHOD "startHex not found");
2460 // Select a facet of startHex by theFacetNormal
2462 SMDS_VolumeTool vTool( startHex );
2463 double norm[3], dot, maxDot = 0;
2465 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2466 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2468 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2476 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2478 // Fill theFacets starting from facetID of startHex
2480 // facets used for seach of volumes adjacent to already treated ones
2481 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2482 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2483 TFacetMap facetsToCheck;
2485 set<const SMDS_MeshNode*> facetNodes;
2486 const SMDS_MeshElement* curHex;
2488 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2492 // move in two directions from startHex via facetID
2493 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2496 int curFacet = facetID;
2497 if ( is2nd ) // do not treat startHex twice
2499 vTool.Set( curHex );
2500 if ( vTool.IsFreeFace( curFacet, &curHex ))
2506 vTool.GetFaceNodes( curFacet, facetNodes );
2507 vTool.Set( curHex );
2508 curFacet = vTool.GetFaceIndex( facetNodes );
2513 // store a facet to split
2514 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2516 theFacets.insert( make_pair( curHex, -1 ));
2519 if ( !allHex && !theHexas.count( curHex ))
2522 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2523 theFacets.insert( make_pair( curHex, curFacet ));
2524 if ( !facetIt2isNew.second )
2527 // remember not-to-split facets in facetsToCheck
2528 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2529 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2531 if ( iF == curFacet && iF == oppFacet )
2533 TVolumeFaceKey facetKey ( vTool, iF );
2534 TElemFacets elemFacet( facetIt2isNew.first, iF );
2535 pair< TFacetMap::iterator, bool > it2isnew =
2536 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2537 if ( !it2isnew.second )
2538 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2540 // pass to a volume adjacent via oppFacet
2541 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2547 // get a new curFacet
2548 vTool.GetFaceNodes( oppFacet, facetNodes );
2549 vTool.Set( curHex );
2550 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2553 } // move in two directions from startHex via facetID
2555 // Find a new startHex by facetsToCheck
2559 TFacetMap::iterator fIt = facetsToCheck.begin();
2560 while ( !startHex && fIt != facetsToCheck.end() )
2562 const TElemFacets& elemFacets = fIt->second;
2563 const SMDS_MeshElement* hex = elemFacets.first->first;
2564 int splitFacet = elemFacets.first->second;
2565 int lateralFacet = elemFacets.second;
2566 facetsToCheck.erase( fIt );
2567 fIt = facetsToCheck.begin();
2570 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2571 curHex->GetGeomType() != SMDSGeom_HEXA )
2573 if ( !allHex && !theHexas.count( curHex ))
2578 // find a facet of startHex to split
2580 set<const SMDS_MeshNode*> lateralNodes;
2581 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2582 vTool.GetFaceNodes( splitFacet, facetNodes );
2583 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2584 vTool.Set( startHex );
2585 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2587 // look for a facet of startHex having common nodes with facetNodes
2588 // but not lateralFacet
2589 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2591 if ( iF == lateralFacet )
2593 int nbCommonNodes = 0;
2594 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2595 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2596 nbCommonNodes += facetNodes.count( nn[ iN ]);
2598 if ( nbCommonNodes >= 2 )
2605 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2607 } // while ( startHex )
2610 //=======================================================================
2611 //function : AddToSameGroups
2612 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2613 //=======================================================================
2615 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2616 const SMDS_MeshElement* elemInGroups,
2617 SMESHDS_Mesh * aMesh)
2619 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2620 if (!groups.empty()) {
2621 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2622 for ( ; grIt != groups.end(); grIt++ ) {
2623 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2624 if ( group && group->Contains( elemInGroups ))
2625 group->SMDSGroup().Add( elemToAdd );
2631 //=======================================================================
2632 //function : RemoveElemFromGroups
2633 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2634 //=======================================================================
2635 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2636 SMESHDS_Mesh * aMesh)
2638 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2639 if (!groups.empty())
2641 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2642 for (; GrIt != groups.end(); GrIt++)
2644 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2645 if (!grp || grp->IsEmpty()) continue;
2646 grp->SMDSGroup().Remove(removeelem);
2651 //================================================================================
2653 * \brief Replace elemToRm by elemToAdd in the all groups
2655 //================================================================================
2657 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2658 const SMDS_MeshElement* elemToAdd,
2659 SMESHDS_Mesh * aMesh)
2661 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2662 if (!groups.empty()) {
2663 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2664 for ( ; grIt != groups.end(); grIt++ ) {
2665 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2666 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2667 group->SMDSGroup().Add( elemToAdd );
2672 //================================================================================
2674 * \brief Replace elemToRm by elemToAdd in the all groups
2676 //================================================================================
2678 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2679 const vector<const SMDS_MeshElement*>& elemToAdd,
2680 SMESHDS_Mesh * aMesh)
2682 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2683 if (!groups.empty())
2685 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2686 for ( ; grIt != groups.end(); grIt++ ) {
2687 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2688 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2689 for ( int i = 0; i < elemToAdd.size(); ++i )
2690 group->SMDSGroup().Add( elemToAdd[ i ] );
2695 //=======================================================================
2696 //function : QuadToTri
2697 //purpose : Cut quadrangles into triangles.
2698 // theCrit is used to select a diagonal to cut
2699 //=======================================================================
2701 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2702 const bool the13Diag)
2704 myLastCreatedElems.Clear();
2705 myLastCreatedNodes.Clear();
2707 MESSAGE( "::QuadToTri()" );
2709 SMESHDS_Mesh * aMesh = GetMeshDS();
2711 Handle(Geom_Surface) surface;
2712 SMESH_MesherHelper helper( *GetMesh() );
2714 TIDSortedElemSet::iterator itElem;
2715 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2716 const SMDS_MeshElement* elem = *itElem;
2717 if ( !elem || elem->GetType() != SMDSAbs_Face )
2719 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2720 if(!isquad) continue;
2722 if(elem->NbNodes()==4) {
2723 // retrieve element nodes
2724 const SMDS_MeshNode* aNodes [4];
2725 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2727 while ( itN->more() )
2728 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2730 int aShapeId = FindShape( elem );
2731 const SMDS_MeshElement* newElem1 = 0;
2732 const SMDS_MeshElement* newElem2 = 0;
2734 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2735 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2738 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2739 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2741 myLastCreatedElems.Append(newElem1);
2742 myLastCreatedElems.Append(newElem2);
2743 // put a new triangle on the same shape and add to the same groups
2746 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2747 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2749 AddToSameGroups( newElem1, elem, aMesh );
2750 AddToSameGroups( newElem2, elem, aMesh );
2751 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2752 aMesh->RemoveElement( elem );
2755 // Quadratic quadrangle
2757 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2759 // get surface elem is on
2760 int aShapeId = FindShape( elem );
2761 if ( aShapeId != helper.GetSubShapeID() ) {
2765 shape = aMesh->IndexToShape( aShapeId );
2766 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2767 TopoDS_Face face = TopoDS::Face( shape );
2768 surface = BRep_Tool::Surface( face );
2769 if ( !surface.IsNull() )
2770 helper.SetSubShape( shape );
2774 const SMDS_MeshNode* aNodes [8];
2775 const SMDS_MeshNode* inFaceNode = 0;
2776 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2778 while ( itN->more() ) {
2779 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2780 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2781 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2783 inFaceNode = aNodes[ i-1 ];
2787 // find middle point for (0,1,2,3)
2788 // and create a node in this point;
2790 if ( surface.IsNull() ) {
2792 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2796 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2799 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2801 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2803 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2804 myLastCreatedNodes.Append(newN);
2806 // create a new element
2807 const SMDS_MeshElement* newElem1 = 0;
2808 const SMDS_MeshElement* newElem2 = 0;
2810 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2811 aNodes[6], aNodes[7], newN );
2812 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2813 newN, aNodes[4], aNodes[5] );
2816 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2817 aNodes[7], aNodes[4], newN );
2818 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2819 newN, aNodes[5], aNodes[6] );
2821 myLastCreatedElems.Append(newElem1);
2822 myLastCreatedElems.Append(newElem2);
2823 // put a new triangle on the same shape and add to the same groups
2826 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2827 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2829 AddToSameGroups( newElem1, elem, aMesh );
2830 AddToSameGroups( newElem2, elem, aMesh );
2831 aMesh->RemoveElement( elem );
2838 //=======================================================================
2839 //function : getAngle
2841 //=======================================================================
2843 double getAngle(const SMDS_MeshElement * tr1,
2844 const SMDS_MeshElement * tr2,
2845 const SMDS_MeshNode * n1,
2846 const SMDS_MeshNode * n2)
2848 double angle = 2. * M_PI; // bad angle
2851 SMESH::Controls::TSequenceOfXYZ P1, P2;
2852 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2853 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2856 if(!tr1->IsQuadratic())
2857 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2859 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2860 if ( N1.SquareMagnitude() <= gp::Resolution() )
2862 if(!tr2->IsQuadratic())
2863 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2865 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2866 if ( N2.SquareMagnitude() <= gp::Resolution() )
2869 // find the first diagonal node n1 in the triangles:
2870 // take in account a diagonal link orientation
2871 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2872 for ( int t = 0; t < 2; t++ ) {
2873 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2874 int i = 0, iDiag = -1;
2875 while ( it->more()) {
2876 const SMDS_MeshElement *n = it->next();
2877 if ( n == n1 || n == n2 ) {
2881 if ( i - iDiag == 1 )
2882 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2891 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2894 angle = N1.Angle( N2 );
2899 // =================================================
2900 // class generating a unique ID for a pair of nodes
2901 // and able to return nodes by that ID
2902 // =================================================
2906 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2907 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2910 long GetLinkID (const SMDS_MeshNode * n1,
2911 const SMDS_MeshNode * n2) const
2913 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2916 bool GetNodes (const long theLinkID,
2917 const SMDS_MeshNode* & theNode1,
2918 const SMDS_MeshNode* & theNode2) const
2920 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2921 if ( !theNode1 ) return false;
2922 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2923 if ( !theNode2 ) return false;
2929 const SMESHDS_Mesh* myMesh;
2934 //=======================================================================
2935 //function : TriToQuad
2936 //purpose : Fuse neighbour triangles into quadrangles.
2937 // theCrit is used to select a neighbour to fuse with.
2938 // theMaxAngle is a max angle between element normals at which
2939 // fusion is still performed.
2940 //=======================================================================
2942 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2943 SMESH::Controls::NumericalFunctorPtr theCrit,
2944 const double theMaxAngle)
2946 myLastCreatedElems.Clear();
2947 myLastCreatedNodes.Clear();
2949 MESSAGE( "::TriToQuad()" );
2951 if ( !theCrit.get() )
2954 SMESHDS_Mesh * aMesh = GetMeshDS();
2956 // Prepare data for algo: build
2957 // 1. map of elements with their linkIDs
2958 // 2. map of linkIDs with their elements
2960 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2961 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2962 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2963 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2965 TIDSortedElemSet::iterator itElem;
2966 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2968 const SMDS_MeshElement* elem = *itElem;
2969 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2970 bool IsTria = ( elem->NbCornerNodes()==3 );
2971 if (!IsTria) continue;
2973 // retrieve element nodes
2974 const SMDS_MeshNode* aNodes [4];
2975 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2978 aNodes[ i++ ] = itN->next();
2979 aNodes[ 3 ] = aNodes[ 0 ];
2982 for ( i = 0; i < 3; i++ ) {
2983 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2984 // check if elements sharing a link can be fused
2985 itLE = mapLi_listEl.find( link );
2986 if ( itLE != mapLi_listEl.end() ) {
2987 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2989 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2990 //if ( FindShape( elem ) != FindShape( elem2 ))
2991 // continue; // do not fuse triangles laying on different shapes
2992 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2993 continue; // avoid making badly shaped quads
2994 (*itLE).second.push_back( elem );