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 );
2997 mapLi_listEl[ link ].push_back( elem );
2999 mapEl_setLi [ elem ].insert( link );
3002 // Clean the maps from the links shared by a sole element, ie
3003 // links to which only one element is bound in mapLi_listEl
3005 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3006 int nbElems = (*itLE).second.size();
3007 if ( nbElems < 2 ) {
3008 const SMDS_MeshElement* elem = (*itLE).second.front();
3009 SMESH_TLink link = (*itLE).first;
3010 mapEl_setLi[ elem ].erase( link );
3011 if ( mapEl_setLi[ elem ].empty() )
3012 mapEl_setLi.erase( elem );
3016 // Algo: fuse triangles into quadrangles
3018 while ( ! mapEl_setLi.empty() ) {
3019 // Look for the start element:
3020 // the element having the least nb of shared links
3021 const SMDS_MeshElement* startElem = 0;
3023 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3024 int nbLinks = (*itEL).second.size();
3025 if ( nbLinks < minNbLinks ) {
3026 startElem = (*itEL).first;
3027 minNbLinks = nbLinks;
3028 if ( minNbLinks == 1 )
3033 // search elements to fuse starting from startElem or links of elements
3034 // fused earlyer - startLinks
3035 list< SMESH_TLink > startLinks;
3036 while ( startElem || !startLinks.empty() ) {
3037 while ( !startElem && !startLinks.empty() ) {
3038 // Get an element to start, by a link
3039 SMESH_TLink linkId = startLinks.front();
3040 startLinks.pop_front();
3041 itLE = mapLi_listEl.find( linkId );
3042 if ( itLE != mapLi_listEl.end() ) {
3043 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3044 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3045 for ( ; itE != listElem.end() ; itE++ )
3046 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3048 mapLi_listEl.erase( itLE );
3053 // Get candidates to be fused
3054 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3055 const SMESH_TLink *link12, *link13;
3057 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3058 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3059 ASSERT( !setLi.empty() );
3060 set< SMESH_TLink >::iterator itLi;
3061 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3063 const SMESH_TLink & link = (*itLi);
3064 itLE = mapLi_listEl.find( link );
3065 if ( itLE == mapLi_listEl.end() )
3068 const SMDS_MeshElement* elem = (*itLE).second.front();
3070 elem = (*itLE).second.back();
3071 mapLi_listEl.erase( itLE );
3072 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3083 // add other links of elem to list of links to re-start from
3084 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3085 set< SMESH_TLink >::iterator it;
3086 for ( it = links.begin(); it != links.end(); it++ ) {
3087 const SMESH_TLink& link2 = (*it);
3088 if ( link2 != link )
3089 startLinks.push_back( link2 );
3093 // Get nodes of possible quadrangles
3094 const SMDS_MeshNode *n12 [4], *n13 [4];
3095 bool Ok12 = false, Ok13 = false;
3096 const SMDS_MeshNode *linkNode1, *linkNode2;
3098 linkNode1 = link12->first;
3099 linkNode2 = link12->second;
3100 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3104 linkNode1 = link13->first;
3105 linkNode2 = link13->second;
3106 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3110 // Choose a pair to fuse
3111 if ( Ok12 && Ok13 ) {
3112 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3113 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3114 double aBadRate12 = getBadRate( &quad12, theCrit );
3115 double aBadRate13 = getBadRate( &quad13, theCrit );
3116 if ( aBadRate13 < aBadRate12 )
3123 // and remove fused elems and remove links from the maps
3124 mapEl_setLi.erase( tr1 );
3127 mapEl_setLi.erase( tr2 );
3128 mapLi_listEl.erase( *link12 );
3129 if ( tr1->NbNodes() == 3 )
3131 const SMDS_MeshElement* newElem = 0;
3132 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3133 myLastCreatedElems.Append(newElem);
3134 AddToSameGroups( newElem, tr1, aMesh );
3135 int aShapeId = tr1->getshapeId();
3137 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3138 aMesh->RemoveElement( tr1 );
3139 aMesh->RemoveElement( tr2 );
3142 vector< const SMDS_MeshNode* > N1;
3143 vector< const SMDS_MeshNode* > N2;
3144 getNodesFromTwoTria(tr1,tr2,N1,N2);
3145 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3146 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3147 // i.e. first nodes from both arrays form a new diagonal
3148 const SMDS_MeshNode* aNodes[8];
3157 const SMDS_MeshElement* newElem = 0;
3158 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3159 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3162 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3163 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3164 myLastCreatedElems.Append(newElem);
3165 AddToSameGroups( newElem, tr1, aMesh );
3166 int aShapeId = tr1->getshapeId();
3168 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3169 aMesh->RemoveElement( tr1 );
3170 aMesh->RemoveElement( tr2 );
3171 // remove middle node (9)
3172 if ( N1[4]->NbInverseElements() == 0 )
3173 aMesh->RemoveNode( N1[4] );
3174 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3175 aMesh->RemoveNode( N1[6] );
3176 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3177 aMesh->RemoveNode( N2[6] );
3182 mapEl_setLi.erase( tr3 );
3183 mapLi_listEl.erase( *link13 );
3184 if ( tr1->NbNodes() == 3 ) {
3185 const SMDS_MeshElement* newElem = 0;
3186 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3187 myLastCreatedElems.Append(newElem);
3188 AddToSameGroups( newElem, tr1, aMesh );
3189 int aShapeId = tr1->getshapeId();
3191 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3192 aMesh->RemoveElement( tr1 );
3193 aMesh->RemoveElement( tr3 );
3196 vector< const SMDS_MeshNode* > N1;
3197 vector< const SMDS_MeshNode* > N2;
3198 getNodesFromTwoTria(tr1,tr3,N1,N2);
3199 // now we receive following N1 and N2 (using numeration as above image)
3200 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3201 // i.e. first nodes from both arrays form a new diagonal
3202 const SMDS_MeshNode* aNodes[8];
3211 const SMDS_MeshElement* newElem = 0;
3212 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3213 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3216 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3217 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3218 myLastCreatedElems.Append(newElem);
3219 AddToSameGroups( newElem, tr1, aMesh );
3220 int aShapeId = tr1->getshapeId();
3222 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3223 aMesh->RemoveElement( tr1 );
3224 aMesh->RemoveElement( tr3 );
3225 // remove middle node (9)
3226 if ( N1[4]->NbInverseElements() == 0 )
3227 aMesh->RemoveNode( N1[4] );
3228 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3229 aMesh->RemoveNode( N1[6] );
3230 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3231 aMesh->RemoveNode( N2[6] );
3235 // Next element to fuse: the rejected one
3237 startElem = Ok12 ? tr3 : tr2;
3239 } // if ( startElem )
3240 } // while ( startElem || !startLinks.empty() )
3241 } // while ( ! mapEl_setLi.empty() )
3247 /*#define DUMPSO(txt) \
3248 // cout << txt << endl;
3249 //=============================================================================
3253 //=============================================================================
3254 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3258 int tmp = idNodes[ i1 ];
3259 idNodes[ i1 ] = idNodes[ i2 ];
3260 idNodes[ i2 ] = tmp;
3261 gp_Pnt Ptmp = P[ i1 ];
3264 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3267 //=======================================================================
3268 //function : SortQuadNodes
3269 //purpose : Set 4 nodes of a quadrangle face in a good order.
3270 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3272 //=======================================================================
3274 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3279 for ( i = 0; i < 4; i++ ) {
3280 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3282 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3285 gp_Vec V1(P[0], P[1]);
3286 gp_Vec V2(P[0], P[2]);
3287 gp_Vec V3(P[0], P[3]);
3289 gp_Vec Cross1 = V1 ^ V2;
3290 gp_Vec Cross2 = V2 ^ V3;
3293 if (Cross1.Dot(Cross2) < 0)
3298 if (Cross1.Dot(Cross2) < 0)
3302 swap ( i, i + 1, idNodes, P );
3304 // for ( int ii = 0; ii < 4; ii++ ) {
3305 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3306 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3312 //=======================================================================
3313 //function : SortHexaNodes
3314 //purpose : Set 8 nodes of a hexahedron in a good order.
3315 // Return success status
3316 //=======================================================================
3318 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3323 DUMPSO( "INPUT: ========================================");
3324 for ( i = 0; i < 8; i++ ) {
3325 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3326 if ( !n ) return false;
3327 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3328 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3330 DUMPSO( "========================================");
3333 set<int> faceNodes; // ids of bottom face nodes, to be found
3334 set<int> checkedId1; // ids of tried 2-nd nodes
3335 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3336 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3337 int iMin, iLoop1 = 0;
3339 // Loop to try the 2-nd nodes
3341 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3343 // Find not checked 2-nd node
3344 for ( i = 1; i < 8; i++ )
3345 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3346 int id1 = idNodes[i];
3347 swap ( 1, i, idNodes, P );
3348 checkedId1.insert ( id1 );
3352 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3353 // ie that all but meybe one (id3 which is on the same face) nodes
3354 // lay on the same side from the triangle plane.
3356 bool manyInPlane = false; // more than 4 nodes lay in plane
3358 while ( ++iLoop2 < 6 ) {
3360 // get 1-2-3 plane coeffs
3361 Standard_Real A, B, C, D;
3362 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3363 if ( N.SquareMagnitude() > gp::Resolution() )
3365 gp_Pln pln ( P[0], N );
3366 pln.Coefficients( A, B, C, D );
3368 // find the node (iMin) closest to pln
3369 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3371 for ( i = 3; i < 8; i++ ) {
3372 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3373 if ( fabs( dist[i] ) < minDist ) {
3374 minDist = fabs( dist[i] );
3377 if ( fabs( dist[i] ) <= tol )
3378 idInPln.insert( idNodes[i] );
3381 // there should not be more than 4 nodes in bottom plane
3382 if ( idInPln.size() > 1 )
3384 DUMPSO( "### idInPln.size() = " << idInPln.size());
3385 // idInPlane does not contain the first 3 nodes
3386 if ( manyInPlane || idInPln.size() == 5)
3387 return false; // all nodes in one plane
3390 // set the 1-st node to be not in plane
3391 for ( i = 3; i < 8; i++ ) {
3392 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3393 DUMPSO( "### Reset 0-th node");
3394 swap( 0, i, idNodes, P );
3399 // reset to re-check second nodes
3400 leastDist = DBL_MAX;
3404 break; // from iLoop2;
3407 // check that the other 4 nodes are on the same side
3408 bool sameSide = true;
3409 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3410 for ( i = 3; sameSide && i < 8; i++ ) {
3412 sameSide = ( isNeg == dist[i] <= 0.);
3415 // keep best solution
3416 if ( sameSide && minDist < leastDist ) {
3417 leastDist = minDist;
3419 faceNodes.insert( idNodes[ 1 ] );
3420 faceNodes.insert( idNodes[ 2 ] );
3421 faceNodes.insert( idNodes[ iMin ] );
3422 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3423 << " leastDist = " << leastDist);
3424 if ( leastDist <= DBL_MIN )
3429 // set next 3-d node to check
3430 int iNext = 2 + iLoop2;
3432 DUMPSO( "Try 2-nd");
3433 swap ( 2, iNext, idNodes, P );
3435 } // while ( iLoop2 < 6 )
3438 if ( faceNodes.empty() ) return false;
3440 // Put the faceNodes in proper places
3441 for ( i = 4; i < 8; i++ ) {
3442 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3443 // find a place to put
3445 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3447 DUMPSO( "Set faceNodes");
3448 swap ( iTo, i, idNodes, P );
3453 // Set nodes of the found bottom face in good order
3454 DUMPSO( " Found bottom face: ");
3455 i = SortQuadNodes( theMesh, idNodes );
3457 gp_Pnt Ptmp = P[ i ];
3462 // for ( int ii = 0; ii < 4; ii++ ) {
3463 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3464 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3467 // Gravity center of the top and bottom faces
3468 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3469 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3471 // Get direction from the bottom to the top face
3472 gp_Vec upDir ( aGCb, aGCt );
3473 Standard_Real upDirSize = upDir.Magnitude();
3474 if ( upDirSize <= gp::Resolution() ) return false;
3477 // Assure that the bottom face normal points up
3478 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3479 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3480 if ( Nb.Dot( upDir ) < 0 ) {
3481 DUMPSO( "Reverse bottom face");
3482 swap( 1, 3, idNodes, P );
3485 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3486 Standard_Real minDist = DBL_MAX;
3487 for ( i = 4; i < 8; i++ ) {
3488 // projection of P[i] to the plane defined by P[0] and upDir
3489 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3490 Standard_Real sqDist = P[0].SquareDistance( Pp );
3491 if ( sqDist < minDist ) {
3496 DUMPSO( "Set 4-th");
3497 swap ( 4, iMin, idNodes, P );
3499 // Set nodes of the top face in good order
3500 DUMPSO( "Sort top face");
3501 i = SortQuadNodes( theMesh, &idNodes[4] );
3504 gp_Pnt Ptmp = P[ i ];
3509 // Assure that direction of the top face normal is from the bottom face
3510 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3511 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3512 if ( Nt.Dot( upDir ) < 0 ) {
3513 DUMPSO( "Reverse top face");
3514 swap( 5, 7, idNodes, P );
3517 // DUMPSO( "OUTPUT: ========================================");
3518 // for ( i = 0; i < 8; i++ ) {
3519 // float *p = ugrid->GetPoint(idNodes[i]);
3520 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3526 //================================================================================
3528 * \brief Return nodes linked to the given one
3529 * \param theNode - the node
3530 * \param linkedNodes - the found nodes
3531 * \param type - the type of elements to check
3533 * Medium nodes are ignored
3535 //================================================================================
3537 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3538 TIDSortedElemSet & linkedNodes,
3539 SMDSAbs_ElementType type )
3541 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3542 while ( elemIt->more() )
3544 const SMDS_MeshElement* elem = elemIt->next();
3545 if(elem->GetType() == SMDSAbs_0DElement)
3548 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3549 if ( elem->GetType() == SMDSAbs_Volume )
3551 SMDS_VolumeTool vol( elem );
3552 while ( nodeIt->more() ) {
3553 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3554 if ( theNode != n && vol.IsLinked( theNode, n ))
3555 linkedNodes.insert( n );
3560 for ( int i = 0; nodeIt->more(); ++i ) {
3561 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3562 if ( n == theNode ) {
3563 int iBefore = i - 1;
3565 if ( elem->IsQuadratic() ) {
3566 int nb = elem->NbNodes() / 2;
3567 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3568 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3570 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3571 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3578 //=======================================================================
3579 //function : laplacianSmooth
3580 //purpose : pulls theNode toward the center of surrounding nodes directly
3581 // connected to that node along an element edge
3582 //=======================================================================
3584 void laplacianSmooth(const SMDS_MeshNode* theNode,
3585 const Handle(Geom_Surface)& theSurface,
3586 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3588 // find surrounding nodes
3590 TIDSortedElemSet nodeSet;
3591 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3593 // compute new coodrs
3595 double coord[] = { 0., 0., 0. };
3596 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3597 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3598 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3599 if ( theSurface.IsNull() ) { // smooth in 3D
3600 coord[0] += node->X();
3601 coord[1] += node->Y();
3602 coord[2] += node->Z();
3604 else { // smooth in 2D
3605 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3606 gp_XY* uv = theUVMap[ node ];
3607 coord[0] += uv->X();
3608 coord[1] += uv->Y();
3611 int nbNodes = nodeSet.size();
3614 coord[0] /= nbNodes;
3615 coord[1] /= nbNodes;
3617 if ( !theSurface.IsNull() ) {
3618 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3619 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3620 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3626 coord[2] /= nbNodes;
3630 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3633 //=======================================================================
3634 //function : centroidalSmooth
3635 //purpose : pulls theNode toward the element-area-weighted centroid of the
3636 // surrounding elements
3637 //=======================================================================
3639 void centroidalSmooth(const SMDS_MeshNode* theNode,
3640 const Handle(Geom_Surface)& theSurface,
3641 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3643 gp_XYZ aNewXYZ(0.,0.,0.);
3644 SMESH::Controls::Area anAreaFunc;
3645 double totalArea = 0.;
3650 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3651 while ( elemIt->more() )
3653 const SMDS_MeshElement* elem = elemIt->next();
3656 gp_XYZ elemCenter(0.,0.,0.);
3657 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3658 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3659 int nn = elem->NbNodes();
3660 if(elem->IsQuadratic()) nn = nn/2;
3662 //while ( itN->more() ) {
3664 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3666 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3667 aNodePoints.push_back( aP );
3668 if ( !theSurface.IsNull() ) { // smooth in 2D
3669 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3670 gp_XY* uv = theUVMap[ aNode ];
3671 aP.SetCoord( uv->X(), uv->Y(), 0. );
3675 double elemArea = anAreaFunc.GetValue( aNodePoints );
3676 totalArea += elemArea;
3678 aNewXYZ += elemCenter * elemArea;
3680 aNewXYZ /= totalArea;
3681 if ( !theSurface.IsNull() ) {
3682 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3683 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3688 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3691 //=======================================================================
3692 //function : getClosestUV
3693 //purpose : return UV of closest projection
3694 //=======================================================================
3696 static bool getClosestUV (Extrema_GenExtPS& projector,
3697 const gp_Pnt& point,
3700 projector.Perform( point );
3701 if ( projector.IsDone() ) {
3702 double u, v, minVal = DBL_MAX;
3703 for ( int i = projector.NbExt(); i > 0; i-- )
3704 if ( projector.SquareDistance( i ) < minVal ) {
3705 minVal = projector.SquareDistance( i );
3706 projector.Point( i ).Parameter( u, v );
3708 result.SetCoord( u, v );
3714 //=======================================================================
3716 //purpose : Smooth theElements during theNbIterations or until a worst
3717 // element has aspect ratio <= theTgtAspectRatio.
3718 // Aspect Ratio varies in range [1.0, inf].
3719 // If theElements is empty, the whole mesh is smoothed.
3720 // theFixedNodes contains additionally fixed nodes. Nodes built
3721 // on edges and boundary nodes are always fixed.
3722 //=======================================================================
3724 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3725 set<const SMDS_MeshNode*> & theFixedNodes,
3726 const SmoothMethod theSmoothMethod,
3727 const int theNbIterations,
3728 double theTgtAspectRatio,
3731 myLastCreatedElems.Clear();
3732 myLastCreatedNodes.Clear();
3734 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3736 if ( theTgtAspectRatio < 1.0 )
3737 theTgtAspectRatio = 1.0;
3739 const double disttol = 1.e-16;
3741 SMESH::Controls::AspectRatio aQualityFunc;
3743 SMESHDS_Mesh* aMesh = GetMeshDS();
3745 if ( theElems.empty() ) {
3746 // add all faces to theElems
3747 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3748 while ( fIt->more() ) {
3749 const SMDS_MeshElement* face = fIt->next();
3750 theElems.insert( theElems.end(), face );
3753 // get all face ids theElems are on
3754 set< int > faceIdSet;
3755 TIDSortedElemSet::iterator itElem;
3757 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3758 int fId = FindShape( *itElem );
3759 // check that corresponding submesh exists and a shape is face
3761 faceIdSet.find( fId ) == faceIdSet.end() &&
3762 aMesh->MeshElements( fId )) {
3763 TopoDS_Shape F = aMesh->IndexToShape( fId );
3764 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3765 faceIdSet.insert( fId );
3768 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3770 // ===============================================
3771 // smooth elements on each TopoDS_Face separately
3772 // ===============================================
3774 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3775 for ( ; fId != faceIdSet.rend(); ++fId ) {
3776 // get face surface and submesh
3777 Handle(Geom_Surface) surface;
3778 SMESHDS_SubMesh* faceSubMesh = 0;
3780 double fToler2 = 0, f,l;
3781 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3782 bool isUPeriodic = false, isVPeriodic = false;
3784 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3785 surface = BRep_Tool::Surface( face );
3786 faceSubMesh = aMesh->MeshElements( *fId );
3787 fToler2 = BRep_Tool::Tolerance( face );
3788 fToler2 *= fToler2 * 10.;
3789 isUPeriodic = surface->IsUPeriodic();
3792 isVPeriodic = surface->IsVPeriodic();
3795 surface->Bounds( u1, u2, v1, v2 );
3797 // ---------------------------------------------------------
3798 // for elements on a face, find movable and fixed nodes and
3799 // compute UV for them
3800 // ---------------------------------------------------------
3801 bool checkBoundaryNodes = false;
3802 bool isQuadratic = false;
3803 set<const SMDS_MeshNode*> setMovableNodes;
3804 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3805 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3806 list< const SMDS_MeshElement* > elemsOnFace;
3808 Extrema_GenExtPS projector;
3809 GeomAdaptor_Surface surfAdaptor;
3810 if ( !surface.IsNull() ) {
3811 surfAdaptor.Load( surface );
3812 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3814 int nbElemOnFace = 0;
3815 itElem = theElems.begin();
3816 // loop on not yet smoothed elements: look for elems on a face
3817 while ( itElem != theElems.end() ) {
3818 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3819 break; // all elements found
3821 const SMDS_MeshElement* elem = *itElem;
3822 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3823 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3827 elemsOnFace.push_back( elem );
3828 theElems.erase( itElem++ );
3832 isQuadratic = elem->IsQuadratic();
3834 // get movable nodes of elem
3835 const SMDS_MeshNode* node;
3836 SMDS_TypeOfPosition posType;
3837 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3838 int nn = 0, nbn = elem->NbNodes();
3839 if(elem->IsQuadratic())
3841 while ( nn++ < nbn ) {
3842 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3843 const SMDS_PositionPtr& pos = node->GetPosition();
3844 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3845 if (posType != SMDS_TOP_EDGE &&
3846 posType != SMDS_TOP_VERTEX &&
3847 theFixedNodes.find( node ) == theFixedNodes.end())
3849 // check if all faces around the node are on faceSubMesh
3850 // because a node on edge may be bound to face
3851 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3853 if ( faceSubMesh ) {
3854 while ( eIt->more() && all ) {
3855 const SMDS_MeshElement* e = eIt->next();
3856 all = faceSubMesh->Contains( e );
3860 setMovableNodes.insert( node );
3862 checkBoundaryNodes = true;
3864 if ( posType == SMDS_TOP_3DSPACE )
3865 checkBoundaryNodes = true;
3868 if ( surface.IsNull() )
3871 // get nodes to check UV
3872 list< const SMDS_MeshNode* > uvCheckNodes;
3873 itN = elem->nodesIterator();
3874 nn = 0; nbn = elem->NbNodes();
3875 if(elem->IsQuadratic())
3877 while ( nn++ < nbn ) {
3878 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3879 if ( uvMap.find( node ) == uvMap.end() )
3880 uvCheckNodes.push_back( node );
3881 // add nodes of elems sharing node
3882 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3883 // while ( eIt->more() ) {
3884 // const SMDS_MeshElement* e = eIt->next();
3885 // if ( e != elem ) {
3886 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3887 // while ( nIt->more() ) {
3888 // const SMDS_MeshNode* n =
3889 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3890 // if ( uvMap.find( n ) == uvMap.end() )
3891 // uvCheckNodes.push_back( n );
3897 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3898 for ( ; n != uvCheckNodes.end(); ++n ) {
3901 const SMDS_PositionPtr& pos = node->GetPosition();
3902 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3904 switch ( posType ) {
3905 case SMDS_TOP_FACE: {
3906 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3907 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3910 case SMDS_TOP_EDGE: {
3911 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3912 Handle(Geom2d_Curve) pcurve;
3913 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3914 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3915 if ( !pcurve.IsNull() ) {
3916 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3917 uv = pcurve->Value( u ).XY();
3921 case SMDS_TOP_VERTEX: {
3922 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3923 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3924 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3929 // check existing UV
3930 bool project = true;
3931 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3932 double dist1 = DBL_MAX, dist2 = 0;
3933 if ( posType != SMDS_TOP_3DSPACE ) {
3934 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3935 project = dist1 > fToler2;
3937 if ( project ) { // compute new UV
3939 if ( !getClosestUV( projector, pNode, newUV )) {
3940 MESSAGE("Node Projection Failed " << node);
3944 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3946 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3948 if ( posType != SMDS_TOP_3DSPACE )
3949 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3950 if ( dist2 < dist1 )
3954 // store UV in the map
3955 listUV.push_back( uv );
3956 uvMap.insert( make_pair( node, &listUV.back() ));
3958 } // loop on not yet smoothed elements
3960 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3961 checkBoundaryNodes = true;
3963 // fix nodes on mesh boundary
3965 if ( checkBoundaryNodes ) {
3966 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3967 map< SMESH_TLink, int >::iterator link_nb;
3968 // put all elements links to linkNbMap
3969 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3970 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3971 const SMDS_MeshElement* elem = (*elemIt);
3972 int nbn = elem->NbCornerNodes();
3973 // loop on elem links: insert them in linkNbMap
3974 for ( int iN = 0; iN < nbn; ++iN ) {
3975 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3976 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3977 SMESH_TLink link( n1, n2 );
3978 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3982 // remove nodes that are in links encountered only once from setMovableNodes
3983 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3984 if ( link_nb->second == 1 ) {
3985 setMovableNodes.erase( link_nb->first.node1() );
3986 setMovableNodes.erase( link_nb->first.node2() );
3991 // -----------------------------------------------------
3992 // for nodes on seam edge, compute one more UV ( uvMap2 );
3993 // find movable nodes linked to nodes on seam and which
3994 // are to be smoothed using the second UV ( uvMap2 )
3995 // -----------------------------------------------------
3997 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3998 if ( !surface.IsNull() ) {
3999 TopExp_Explorer eExp( face, TopAbs_EDGE );
4000 for ( ; eExp.More(); eExp.Next() ) {
4001 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4002 if ( !BRep_Tool::IsClosed( edge, face ))
4004 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4005 if ( !sm ) continue;
4006 // find out which parameter varies for a node on seam
4009 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4010 if ( pcurve.IsNull() ) continue;
4011 uv1 = pcurve->Value( f );
4013 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4014 if ( pcurve.IsNull() ) continue;
4015 uv2 = pcurve->Value( f );
4016 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4018 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4019 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4021 // get nodes on seam and its vertices
4022 list< const SMDS_MeshNode* > seamNodes;
4023 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4024 while ( nSeamIt->more() ) {
4025 const SMDS_MeshNode* node = nSeamIt->next();
4026 if ( !isQuadratic || !IsMedium( node ))
4027 seamNodes.push_back( node );
4029 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4030 for ( ; vExp.More(); vExp.Next() ) {
4031 sm = aMesh->MeshElements( vExp.Current() );
4033 nSeamIt = sm->GetNodes();
4034 while ( nSeamIt->more() )
4035 seamNodes.push_back( nSeamIt->next() );
4038 // loop on nodes on seam
4039 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4040 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4041 const SMDS_MeshNode* nSeam = *noSeIt;
4042 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4043 if ( n_uv == uvMap.end() )
4046 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4047 // set the second UV
4048 listUV.push_back( *n_uv->second );
4049 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4050 if ( uvMap2.empty() )
4051 uvMap2 = uvMap; // copy the uvMap contents
4052 uvMap2[ nSeam ] = &listUV.back();
4054 // collect movable nodes linked to ones on seam in nodesNearSeam
4055 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4056 while ( eIt->more() ) {
4057 const SMDS_MeshElement* e = eIt->next();
4058 int nbUseMap1 = 0, nbUseMap2 = 0;
4059 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4060 int nn = 0, nbn = e->NbNodes();
4061 if(e->IsQuadratic()) nbn = nbn/2;
4062 while ( nn++ < nbn )
4064 const SMDS_MeshNode* n =
4065 static_cast<const SMDS_MeshNode*>( nIt->next() );
4067 setMovableNodes.find( n ) == setMovableNodes.end() )
4069 // add only nodes being closer to uv2 than to uv1
4070 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4071 0.5 * ( n->Y() + nSeam->Y() ),
4072 0.5 * ( n->Z() + nSeam->Z() ));
4074 getClosestUV( projector, pMid, uv );
4075 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4076 nodesNearSeam.insert( n );
4082 // for centroidalSmooth all element nodes must
4083 // be on one side of a seam
4084 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4085 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4087 while ( nn++ < nbn ) {
4088 const SMDS_MeshNode* n =
4089 static_cast<const SMDS_MeshNode*>( nIt->next() );
4090 setMovableNodes.erase( n );
4094 } // loop on nodes on seam
4095 } // loop on edge of a face
4096 } // if ( !face.IsNull() )
4098 if ( setMovableNodes.empty() ) {
4099 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4100 continue; // goto next face
4108 double maxRatio = -1., maxDisplacement = -1.;
4109 set<const SMDS_MeshNode*>::iterator nodeToMove;
4110 for ( it = 0; it < theNbIterations; it++ ) {
4111 maxDisplacement = 0.;
4112 nodeToMove = setMovableNodes.begin();
4113 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4114 const SMDS_MeshNode* node = (*nodeToMove);
4115 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4118 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4119 if ( theSmoothMethod == LAPLACIAN )
4120 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4122 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4124 // node displacement
4125 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4126 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4127 if ( aDispl > maxDisplacement )
4128 maxDisplacement = aDispl;
4130 // no node movement => exit
4131 //if ( maxDisplacement < 1.e-16 ) {
4132 if ( maxDisplacement < disttol ) {
4133 MESSAGE("-- no node movement --");
4137 // check elements quality
4139 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4140 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4141 const SMDS_MeshElement* elem = (*elemIt);
4142 if ( !elem || elem->GetType() != SMDSAbs_Face )
4144 SMESH::Controls::TSequenceOfXYZ aPoints;
4145 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4146 double aValue = aQualityFunc.GetValue( aPoints );
4147 if ( aValue > maxRatio )
4151 if ( maxRatio <= theTgtAspectRatio ) {
4152 MESSAGE("-- quality achived --");
4155 if (it+1 == theNbIterations) {
4156 MESSAGE("-- Iteration limit exceeded --");
4158 } // smoothing iterations
4160 MESSAGE(" Face id: " << *fId <<
4161 " Nb iterstions: " << it <<
4162 " Displacement: " << maxDisplacement <<
4163 " Aspect Ratio " << maxRatio);
4165 // ---------------------------------------
4166 // new nodes positions are computed,
4167 // record movement in DS and set new UV
4168 // ---------------------------------------
4169 nodeToMove = setMovableNodes.begin();
4170 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4171 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4172 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4173 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4174 if ( node_uv != uvMap.end() ) {
4175 gp_XY* uv = node_uv->second;
4177 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4181 // move medium nodes of quadratic elements
4184 SMESH_MesherHelper helper( *GetMesh() );
4185 helper.SetSubShape( face );
4186 vector<const SMDS_MeshNode*> nodes;
4188 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4189 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4191 const SMDS_MeshElement* QF = *elemIt;
4192 if ( QF->IsQuadratic() )
4194 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4195 SMDS_MeshElement::iterator() );
4196 nodes.push_back( nodes[0] );
4198 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4200 if ( !surface.IsNull() )
4202 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4203 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4204 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4205 xyz = surface->Value( uv.X(), uv.Y() );
4208 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4210 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4211 // we have to move a medium node
4212 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4218 } // loop on face ids
4222 //=======================================================================
4223 //function : isReverse
4224 //purpose : Return true if normal of prevNodes is not co-directied with
4225 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4226 // iNotSame is where prevNodes and nextNodes are different.
4227 // If result is true then future volume orientation is OK
4228 //=======================================================================
4230 static bool isReverse(const SMDS_MeshElement* face,
4231 const vector<const SMDS_MeshNode*>& prevNodes,
4232 const vector<const SMDS_MeshNode*>& nextNodes,
4236 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4237 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4238 gp_XYZ extrDir( pN - pP ), faceNorm;
4239 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4241 return faceNorm * extrDir < 0.0;
4244 //=======================================================================
4246 * \brief Create elements by sweeping an element
4247 * \param elem - element to sweep
4248 * \param newNodesItVec - nodes generated from each node of the element
4249 * \param newElems - generated elements
4250 * \param nbSteps - number of sweeping steps
4251 * \param srcElements - to append elem for each generated element
4253 //=======================================================================
4255 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4256 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4257 list<const SMDS_MeshElement*>& newElems,
4259 SMESH_SequenceOfElemPtr& srcElements)
4261 //MESSAGE("sweepElement " << nbSteps);
4262 SMESHDS_Mesh* aMesh = GetMeshDS();
4264 const int nbNodes = elem->NbNodes();
4265 const int nbCorners = elem->NbCornerNodes();
4266 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4267 polyhedron creation !!! */
4268 // Loop on elem nodes:
4269 // find new nodes and detect same nodes indices
4270 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4271 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4272 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4273 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4275 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4276 vector<int> sames(nbNodes);
4277 vector<bool> isSingleNode(nbNodes);
4279 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4280 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4281 const SMDS_MeshNode* node = nnIt->first;
4282 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4283 if ( listNewNodes.empty() )
4286 itNN [ iNode ] = listNewNodes.begin();
4287 prevNod[ iNode ] = node;
4288 nextNod[ iNode ] = listNewNodes.front();
4290 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4291 corner node of linear */
4292 if ( prevNod[ iNode ] != nextNod [ iNode ])
4293 nbDouble += !isSingleNode[iNode];
4295 if( iNode < nbCorners ) { // check corners only
4296 if ( prevNod[ iNode ] == nextNod [ iNode ])
4297 sames[nbSame++] = iNode;
4299 iNotSameNode = iNode;
4303 if ( nbSame == nbNodes || nbSame > 2) {
4304 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4308 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4310 // fix nodes order to have bottom normal external
4311 if ( baseType == SMDSEntity_Polygon )
4313 std::reverse( itNN.begin(), itNN.end() );
4314 std::reverse( prevNod.begin(), prevNod.end() );
4315 std::reverse( midlNod.begin(), midlNod.end() );
4316 std::reverse( nextNod.begin(), nextNod.end() );
4317 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4321 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4322 SMDS_MeshCell::applyInterlace( ind, itNN );
4323 SMDS_MeshCell::applyInterlace( ind, prevNod );
4324 SMDS_MeshCell::applyInterlace( ind, nextNod );
4325 SMDS_MeshCell::applyInterlace( ind, midlNod );
4326 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4329 sames[nbSame] = iNotSameNode;
4330 for ( int j = 0; j <= nbSame; ++j )
4331 for ( size_t i = 0; i < ind.size(); ++i )
4332 if ( ind[i] == sames[j] )
4337 iNotSameNode = sames[nbSame];
4342 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4344 iSameNode = sames[ nbSame-1 ];
4345 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4346 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4347 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4350 // make new elements
4351 for (int iStep = 0; iStep < nbSteps; iStep++ )
4354 for ( iNode = 0; iNode < nbNodes; iNode++ )
4356 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4357 nextNod[ iNode ] = *itNN[ iNode ]++;
4360 SMDS_MeshElement* aNewElem = 0;
4361 /*if(!elem->IsPoly())*/ {
4362 switch ( baseType ) {
4364 case SMDSEntity_Node: { // sweep NODE
4365 if ( nbSame == 0 ) {
4366 if ( isSingleNode[0] )
4367 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4369 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4375 case SMDSEntity_Edge: { // sweep EDGE
4376 if ( nbDouble == 0 )
4378 if ( nbSame == 0 ) // ---> quadrangle
4379 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4380 nextNod[ 1 ], nextNod[ 0 ] );
4381 else // ---> triangle
4382 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4383 nextNod[ iNotSameNode ] );
4385 else // ---> polygon
4387 vector<const SMDS_MeshNode*> poly_nodes;
4388 poly_nodes.push_back( prevNod[0] );
4389 poly_nodes.push_back( prevNod[1] );
4390 if ( prevNod[1] != nextNod[1] )
4392 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4393 poly_nodes.push_back( nextNod[1] );
4395 if ( prevNod[0] != nextNod[0] )
4397 poly_nodes.push_back( nextNod[0] );
4398 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4400 switch ( poly_nodes.size() ) {
4402 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4405 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4406 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4409 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4414 case SMDSEntity_Triangle: // TRIANGLE --->
4416 if ( nbDouble > 0 ) break;
4417 if ( nbSame == 0 ) // ---> pentahedron
4418 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4419 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4421 else if ( nbSame == 1 ) // ---> pyramid
4422 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4423 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4424 nextNod[ iSameNode ]);
4426 else // 2 same nodes: ---> tetrahedron
4427 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4428 nextNod[ iNotSameNode ]);
4431 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4435 if ( nbDouble+nbSame == 2 )
4437 if(nbSame==0) { // ---> quadratic quadrangle
4438 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4439 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4441 else { //(nbSame==1) // ---> quadratic triangle
4443 return; // medium node on axis
4445 else if(sames[0]==0)
4446 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4447 nextNod[2], midlNod[1], prevNod[2]);
4449 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4450 midlNod[0], nextNod[2], prevNod[2]);
4453 else if ( nbDouble == 3 )
4455 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4456 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4457 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4464 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4465 if ( nbDouble > 0 ) break;
4467 if ( nbSame == 0 ) // ---> hexahedron
4468 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4469 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4471 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4472 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4473 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4474 nextNod[ iSameNode ]);
4475 newElems.push_back( aNewElem );
4476 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4477 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4478 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4480 else if ( nbSame == 2 ) { // ---> pentahedron
4481 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4482 // iBeforeSame is same too
4483 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4484 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4485 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4487 // iAfterSame is same too
4488 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4489 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4490 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4494 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4495 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4496 if ( nbDouble+nbSame != 3 ) break;
4498 // ---> pentahedron with 15 nodes
4499 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4500 nextNod[0], nextNod[1], nextNod[2],
4501 prevNod[3], prevNod[4], prevNod[5],
4502 nextNod[3], nextNod[4], nextNod[5],
4503 midlNod[0], midlNod[1], midlNod[2]);
4505 else if(nbSame==1) {
4506 // ---> 2d order pyramid of 13 nodes
4507 int apex = iSameNode;
4508 int i0 = ( apex + 1 ) % nbCorners;
4509 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4513 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4514 nextNod[i0], nextNod[i1], prevNod[apex],
4515 prevNod[i01], midlNod[i0],
4516 nextNod[i01], midlNod[i1],
4517 prevNod[i1a], prevNod[i0a],
4518 nextNod[i0a], nextNod[i1a]);
4520 else if(nbSame==2) {
4521 // ---> 2d order tetrahedron of 10 nodes
4522 int n1 = iNotSameNode;
4523 int n2 = ( n1 + 1 ) % nbCorners;
4524 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4528 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4529 prevNod[n12], prevNod[n23], prevNod[n31],
4530 midlNod[n1], nextNod[n12], nextNod[n31]);
4534 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4536 if ( nbDouble != 4 ) break;
4537 // ---> hexahedron with 20 nodes
4538 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4539 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4540 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4541 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4542 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4544 else if(nbSame==1) {
4545 // ---> pyramid + pentahedron - can not be created since it is needed
4546 // additional middle node at the center of face
4547 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4550 else if( nbSame == 2 ) {
4551 if ( nbDouble != 2 ) break;
4552 // ---> 2d order Pentahedron with 15 nodes
4554 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4555 // iBeforeSame is same too
4562 // iAfterSame is same too
4572 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4573 prevNod[n4], prevNod[n5], nextNod[n5],
4574 prevNod[n12], midlNod[n2], nextNod[n12],
4575 prevNod[n45], midlNod[n5], nextNod[n45],
4576 prevNod[n14], prevNod[n25], nextNod[n25]);
4580 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4582 if( nbSame == 0 && nbDouble == 9 ) {
4583 // ---> tri-quadratic hexahedron with 27 nodes
4584 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4585 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4586 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4587 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4588 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4589 prevNod[8], // bottom center
4590 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4591 nextNod[8], // top center
4592 midlNod[8]);// elem center
4600 case SMDSEntity_Polygon: { // sweep POLYGON
4602 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4603 // ---> hexagonal prism
4604 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4605 prevNod[3], prevNod[4], prevNod[5],
4606 nextNod[0], nextNod[1], nextNod[2],
4607 nextNod[3], nextNod[4], nextNod[5]);
4611 case SMDSEntity_Ball:
4619 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4621 if ( baseType != SMDSEntity_Polygon )
4623 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4624 SMDS_MeshCell::applyInterlace( ind, prevNod );
4625 SMDS_MeshCell::applyInterlace( ind, nextNod );
4626 SMDS_MeshCell::applyInterlace( ind, midlNod );
4627 SMDS_MeshCell::applyInterlace( ind, itNN );
4628 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4629 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4631 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4632 vector<int> quantities (nbNodes + 2);
4633 polyedre_nodes.clear();
4637 for (int inode = 0; inode < nbNodes; inode++)
4638 polyedre_nodes.push_back( prevNod[inode] );
4639 quantities.push_back( nbNodes );
4642 polyedre_nodes.push_back( nextNod[0] );
4643 for (int inode = nbNodes; inode-1; --inode )
4644 polyedre_nodes.push_back( nextNod[inode-1] );
4645 quantities.push_back( nbNodes );
4648 for (int iface = 0; iface < nbNodes; iface++)
4650 const int prevNbNodes = polyedre_nodes.size();
4651 int inextface = (iface+1) % nbNodes;
4652 polyedre_nodes.push_back( prevNod[inextface] );
4653 polyedre_nodes.push_back( prevNod[iface] );
4654 if ( prevNod[iface] != nextNod[iface] )
4656 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4657 polyedre_nodes.push_back( nextNod[iface] );
4659 if ( prevNod[inextface] != nextNod[inextface] )
4661 polyedre_nodes.push_back( nextNod[inextface] );
4662 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4664 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4665 if ( nbFaceNodes > 2 )
4666 quantities.push_back( nbFaceNodes );
4667 else // degenerated face
4668 polyedre_nodes.resize( prevNbNodes );
4670 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4674 newElems.push_back( aNewElem );
4675 myLastCreatedElems.Append(aNewElem);
4676 srcElements.Append( elem );
4679 // set new prev nodes
4680 for ( iNode = 0; iNode < nbNodes; iNode++ )
4681 prevNod[ iNode ] = nextNod[ iNode ];
4686 //=======================================================================
4688 * \brief Create 1D and 2D elements around swept elements
4689 * \param mapNewNodes - source nodes and ones generated from them
4690 * \param newElemsMap - source elements and ones generated from them
4691 * \param elemNewNodesMap - nodes generated from each node of each element
4692 * \param elemSet - all swept elements
4693 * \param nbSteps - number of sweeping steps
4694 * \param srcElements - to append elem for each generated element
4696 //=======================================================================
4698 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4699 TTElemOfElemListMap & newElemsMap,
4700 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4701 TIDSortedElemSet& elemSet,
4703 SMESH_SequenceOfElemPtr& srcElements)
4705 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4706 SMESHDS_Mesh* aMesh = GetMeshDS();
4708 // Find nodes belonging to only one initial element - sweep them into edges.
4710 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4711 for ( ; nList != mapNewNodes.end(); nList++ )
4713 const SMDS_MeshNode* node =
4714 static_cast<const SMDS_MeshNode*>( nList->first );
4715 if ( newElemsMap.count( node ))
4716 continue; // node was extruded into edge
4717 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4718 int nbInitElems = 0;
4719 const SMDS_MeshElement* el = 0;
4720 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4721 while ( eIt->more() && nbInitElems < 2 ) {
4723 SMDSAbs_ElementType type = el->GetType();
4724 if ( type == SMDSAbs_Volume || type < highType ) continue;
4725 if ( type > highType ) {
4729 nbInitElems += elemSet.count(el);
4731 if ( nbInitElems < 2 ) {
4732 bool NotCreateEdge = el && el->IsMediumNode(node);
4733 if(!NotCreateEdge) {
4734 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4735 list<const SMDS_MeshElement*> newEdges;
4736 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4741 // Make a ceiling for each element ie an equal element of last new nodes.
4742 // Find free links of faces - make edges and sweep them into faces.
4744 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4745 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4746 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4748 const SMDS_MeshElement* elem = itElem->first;
4749 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4751 if(itElem->second.size()==0) continue;
4753 const bool isQuadratic = elem->IsQuadratic();
4755 if ( elem->GetType() == SMDSAbs_Edge ) {
4756 // create a ceiling edge
4757 if ( !isQuadratic ) {
4758 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4759 vecNewNodes[ 1 ]->second.back())) {
4760 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4761 vecNewNodes[ 1 ]->second.back()));
4762 srcElements.Append( elem );
4766 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4767 vecNewNodes[ 1 ]->second.back(),
4768 vecNewNodes[ 2 ]->second.back())) {
4769 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4770 vecNewNodes[ 1 ]->second.back(),
4771 vecNewNodes[ 2 ]->second.back()));
4772 srcElements.Append( elem );
4776 if ( elem->GetType() != SMDSAbs_Face )
4779 bool hasFreeLinks = false;
4781 TIDSortedElemSet avoidSet;
4782 avoidSet.insert( elem );
4784 set<const SMDS_MeshNode*> aFaceLastNodes;
4785 int iNode, nbNodes = vecNewNodes.size();
4786 if ( !isQuadratic ) {
4787 // loop on the face nodes
4788 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4789 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4790 // look for free links of the face
4791 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4792 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4793 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4794 // check if a link n1-n2 is free
4795 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4796 hasFreeLinks = true;
4797 // make a new edge and a ceiling for a new edge
4798 const SMDS_MeshElement* edge;
4799 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4800 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4801 srcElements.Append( myLastCreatedElems.Last() );
4803 n1 = vecNewNodes[ iNode ]->second.back();
4804 n2 = vecNewNodes[ iNext ]->second.back();
4805 if ( !aMesh->FindEdge( n1, n2 )) {
4806 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4807 srcElements.Append( edge );
4812 else { // elem is quadratic face
4813 int nbn = nbNodes/2;
4814 for ( iNode = 0; iNode < nbn; iNode++ ) {
4815 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4816 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4817 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4818 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4819 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4820 // check if a link is free
4821 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4822 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4823 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4824 hasFreeLinks = true;
4825 // make an edge and a ceiling for a new edge
4827 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4828 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4829 srcElements.Append( elem );
4831 n1 = vecNewNodes[ iNode ]->second.back();
4832 n2 = vecNewNodes[ iNext ]->second.back();
4833 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4834 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4835 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4836 srcElements.Append( elem );
4840 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4841 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4845 // sweep free links into faces
4847 if ( hasFreeLinks ) {
4848 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4849 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4851 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4852 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4853 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4854 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4855 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4857 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4858 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4859 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4861 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4862 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4863 std::advance( v, volNb );
4864 // find indices of free faces of a volume and their source edges
4865 list< int > freeInd;
4866 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4867 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4868 int iF, nbF = vTool.NbFaces();
4869 for ( iF = 0; iF < nbF; iF ++ ) {
4870 if (vTool.IsFreeFace( iF ) &&
4871 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4872 initNodeSet != faceNodeSet) // except an initial face
4874 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4876 if ( faceNodeSet == initNodeSetNoCenter )
4878 freeInd.push_back( iF );
4879 // find source edge of a free face iF
4880 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4881 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4882 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4883 initNodeSet.begin(), initNodeSet.end(),
4884 commonNodes.begin());
4885 if ( (*v)->IsQuadratic() )
4886 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4888 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4890 if ( !srcEdges.back() )
4892 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4893 << iF << " of volume #" << vTool.ID() << endl;
4898 if ( freeInd.empty() )
4901 // create faces for all steps;
4902 // if such a face has been already created by sweep of edge,
4903 // assure that its orientation is OK
4904 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4905 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4906 vTool.SetExternalNormal();
4907 const int nextShift = vTool.IsForward() ? +1 : -1;
4908 list< int >::iterator ind = freeInd.begin();
4909 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4910 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4912 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4913 int nbn = vTool.NbFaceNodes( *ind );
4914 const SMDS_MeshElement * f = 0;
4915 if ( nbn == 3 ) ///// triangle
4917 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4919 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4921 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4923 nodes[ 1 + nextShift ] };
4925 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4927 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4931 else if ( nbn == 4 ) ///// quadrangle
4933 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4935 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4937 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4938 nodes[ 2 ], nodes[ 2+nextShift ] };
4940 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4942 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4943 newOrder[ 2 ], newOrder[ 3 ]));
4946 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4948 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4950 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4952 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4954 nodes[2 + 2*nextShift],
4955 nodes[3 - 2*nextShift],
4957 nodes[3 + 2*nextShift]};
4959 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4961 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4969 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4971 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4972 nodes[1], nodes[3], nodes[5], nodes[7] );
4974 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4976 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4977 nodes[4 - 2*nextShift],
4979 nodes[4 + 2*nextShift],
4981 nodes[5 - 2*nextShift],
4983 nodes[5 + 2*nextShift] };
4985 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4987 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4988 newOrder[ 2 ], newOrder[ 3 ],
4989 newOrder[ 4 ], newOrder[ 5 ],
4990 newOrder[ 6 ], newOrder[ 7 ]));
4993 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4995 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4996 SMDSAbs_Face, /*noMedium=*/false);
4998 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5000 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5001 nodes[4 - 2*nextShift],
5003 nodes[4 + 2*nextShift],
5005 nodes[5 - 2*nextShift],
5007 nodes[5 + 2*nextShift],
5010 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5012 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5013 newOrder[ 2 ], newOrder[ 3 ],
5014 newOrder[ 4 ], newOrder[ 5 ],
5015 newOrder[ 6 ], newOrder[ 7 ],
5019 else //////// polygon
5021 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5022 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5024 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5026 if ( !vTool.IsForward() )
5027 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5029 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5031 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5035 while ( srcElements.Length() < myLastCreatedElems.Length() )
5036 srcElements.Append( *srcEdge );
5038 } // loop on free faces
5040 // go to the next volume
5042 while ( iVol++ < nbVolumesByStep ) v++;
5045 } // loop on volumes of one step
5046 } // sweep free links into faces
5048 // Make a ceiling face with a normal external to a volume
5050 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5051 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5052 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5054 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5055 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5056 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5059 lastVol.SetExternalNormal();
5060 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5061 int nbn = lastVol.NbFaceNodes( iF );
5062 // we do not use this->AddElement() because nodes are interlaced
5063 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5064 if ( !hasFreeLinks ||
5065 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5068 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5070 else if ( nbn == 4 )
5071 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5073 else if ( nbn == 6 && isQuadratic )
5074 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5075 nodes[1], nodes[3], nodes[5]));
5076 else if ( nbn == 7 && isQuadratic )
5077 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5078 nodes[1], nodes[3], nodes[5], nodes[6]));
5079 else if ( nbn == 8 && isQuadratic )
5080 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5081 nodes[1], nodes[3], nodes[5], nodes[7]));
5082 else if ( nbn == 9 && isQuadratic )
5083 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5084 nodes[1], nodes[3], nodes[5], nodes[7],
5087 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5089 while ( srcElements.Length() < myLastCreatedElems.Length() )
5090 srcElements.Append( elem );
5093 } // loop on swept elements
5096 //=======================================================================
5097 //function : RotationSweep
5099 //=======================================================================
5101 SMESH_MeshEditor::PGroupIDs
5102 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5103 const gp_Ax1& theAxis,
5104 const double theAngle,
5105 const int theNbSteps,
5106 const double theTol,
5107 const bool theMakeGroups,
5108 const bool theMakeWalls)
5110 myLastCreatedElems.Clear();
5111 myLastCreatedNodes.Clear();
5113 // source elements for each generated one
5114 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5116 MESSAGE( "RotationSweep()");
5118 aTrsf.SetRotation( theAxis, theAngle );
5120 aTrsf2.SetRotation( theAxis, theAngle/2. );
5122 gp_Lin aLine( theAxis );
5123 double aSqTol = theTol * theTol;
5125 SMESHDS_Mesh* aMesh = GetMeshDS();
5127 TNodeOfNodeListMap mapNewNodes;
5128 TElemOfVecOfNnlmiMap mapElemNewNodes;
5129 TTElemOfElemListMap newElemsMap;
5131 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5132 myMesh->NbFaces(ORDER_QUADRATIC) +
5133 myMesh->NbVolumes(ORDER_QUADRATIC) );
5135 TIDSortedElemSet::iterator itElem;
5136 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5137 const SMDS_MeshElement* elem = *itElem;
5138 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5140 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5141 newNodesItVec.reserve( elem->NbNodes() );
5143 // loop on elem nodes
5144 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5145 while ( itN->more() )
5147 // check if a node has been already sweeped
5148 const SMDS_MeshNode* node = cast2Node( itN->next() );
5150 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5152 aXYZ.Coord( coord[0], coord[1], coord[2] );
5153 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5155 TNodeOfNodeListMapItr nIt =
5156 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5157 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5158 if ( listNewNodes.empty() )
5160 // check if we are to create medium nodes between corner ones
5161 bool needMediumNodes = false;
5162 if ( isQuadraticMesh )
5164 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5165 while (it->more() && !needMediumNodes )
5167 const SMDS_MeshElement* invElem = it->next();
5168 if ( invElem != elem && !theElems.count( invElem )) continue;
5169 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5170 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5171 needMediumNodes = true;
5176 const SMDS_MeshNode * newNode = node;
5177 for ( int i = 0; i < theNbSteps; i++ ) {
5179 if ( needMediumNodes ) // create a medium node
5181 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5182 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5183 myLastCreatedNodes.Append(newNode);
5184 srcNodes.Append( node );
5185 listNewNodes.push_back( newNode );
5186 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5189 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5191 // create a corner node
5192 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5193 myLastCreatedNodes.Append(newNode);
5194 srcNodes.Append( node );
5195 listNewNodes.push_back( newNode );
5198 listNewNodes.push_back( newNode );
5199 // if ( needMediumNodes )
5200 // listNewNodes.push_back( newNode );
5204 newNodesItVec.push_back( nIt );
5206 // make new elements
5207 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5211 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5213 PGroupIDs newGroupIDs;
5214 if ( theMakeGroups )
5215 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5221 //=======================================================================
5222 //function : CreateNode
5224 //=======================================================================
5225 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5228 const double tolnode,
5229 SMESH_SequenceOfNode& aNodes)
5231 // myLastCreatedElems.Clear();
5232 // myLastCreatedNodes.Clear();
5235 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5237 // try to search in sequence of existing nodes
5238 // if aNodes.Length()>0 we 'nave to use given sequence
5239 // else - use all nodes of mesh
5240 if(aNodes.Length()>0) {
5242 for(i=1; i<=aNodes.Length(); i++) {
5243 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5244 if(P1.Distance(P2)<tolnode)
5245 return aNodes.Value(i);
5249 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5250 while(itn->more()) {
5251 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5252 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5253 if(P1.Distance(P2)<tolnode)
5258 // create new node and return it
5259 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5260 //myLastCreatedNodes.Append(NewNode);
5265 //=======================================================================
5266 //function : ExtrusionSweep
5268 //=======================================================================
5270 SMESH_MeshEditor::PGroupIDs
5271 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5272 const gp_Vec& theStep,
5273 const int theNbSteps,
5274 TTElemOfElemListMap& newElemsMap,
5275 const bool theMakeGroups,
5277 const double theTolerance)
5279 ExtrusParam aParams;
5280 aParams.myDir = gp_Dir(theStep);
5281 aParams.myNodes.Clear();
5282 aParams.mySteps = new TColStd_HSequenceOfReal;
5284 for(i=1; i<=theNbSteps; i++)
5285 aParams.mySteps->Append(theStep.Magnitude());
5288 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5292 //=======================================================================
5293 //function : ExtrusionSweep
5295 //=======================================================================
5297 SMESH_MeshEditor::PGroupIDs
5298 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5299 ExtrusParam& theParams,
5300 TTElemOfElemListMap& newElemsMap,
5301 const bool theMakeGroups,
5303 const double theTolerance)
5305 myLastCreatedElems.Clear();
5306 myLastCreatedNodes.Clear();
5308 // source elements for each generated one
5309 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5311 SMESHDS_Mesh* aMesh = GetMeshDS();
5313 int nbsteps = theParams.mySteps->Length();
5315 TNodeOfNodeListMap mapNewNodes;
5316 //TNodeOfNodeVecMap mapNewNodes;
5317 TElemOfVecOfNnlmiMap mapElemNewNodes;
5318 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5320 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5321 myMesh->NbFaces(ORDER_QUADRATIC) +
5322 myMesh->NbVolumes(ORDER_QUADRATIC) );
5324 TIDSortedElemSet::iterator itElem;
5325 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5326 // check element type
5327 const SMDS_MeshElement* elem = *itElem;
5328 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5331 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5332 newNodesItVec.reserve( elem->NbNodes() );
5334 // loop on elem nodes
5335 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5336 while ( itN->more() )
5338 // check if a node has been already sweeped
5339 const SMDS_MeshNode* node = cast2Node( itN->next() );
5340 TNodeOfNodeListMap::iterator nIt =
5341 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5342 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5343 if ( listNewNodes.empty() )
5347 // check if we are to create medium nodes between corner ones
5348 bool needMediumNodes = false;
5349 if ( isQuadraticMesh )
5351 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5352 while (it->more() && !needMediumNodes )
5354 const SMDS_MeshElement* invElem = it->next();
5355 if ( invElem != elem && !theElems.count( invElem )) continue;
5356 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5357 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5358 needMediumNodes = true;
5362 double coord[] = { node->X(), node->Y(), node->Z() };
5363 for ( int i = 0; i < nbsteps; i++ )
5365 if ( needMediumNodes ) // create a medium node
5367 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5368 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5369 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5370 if( theFlags & EXTRUSION_FLAG_SEW ) {
5371 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5372 theTolerance, theParams.myNodes);
5373 listNewNodes.push_back( newNode );
5376 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5377 myLastCreatedNodes.Append(newNode);
5378 srcNodes.Append( node );
5379 listNewNodes.push_back( newNode );
5382 // create a corner node
5383 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5384 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5385 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5386 if( theFlags & EXTRUSION_FLAG_SEW ) {
5387 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5388 theTolerance, theParams.myNodes);
5389 listNewNodes.push_back( newNode );
5392 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5393 myLastCreatedNodes.Append(newNode);
5394 srcNodes.Append( node );
5395 listNewNodes.push_back( newNode );
5399 newNodesItVec.push_back( nIt );
5401 // make new elements
5402 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5405 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5406 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5408 PGroupIDs newGroupIDs;
5409 if ( theMakeGroups )
5410 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5415 //=======================================================================
5416 //function : ExtrusionAlongTrack
5418 //=======================================================================
5419 SMESH_MeshEditor::Extrusion_Error
5420 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5421 SMESH_subMesh* theTrack,
5422 const SMDS_MeshNode* theN1,
5423 const bool theHasAngles,
5424 list<double>& theAngles,
5425 const bool theLinearVariation,
5426 const bool theHasRefPoint,
5427 const gp_Pnt& theRefPoint,
5428 const bool theMakeGroups)
5430 MESSAGE("ExtrusionAlongTrack");
5431 myLastCreatedElems.Clear();
5432 myLastCreatedNodes.Clear();
5435 std::list<double> aPrms;
5436 TIDSortedElemSet::iterator itElem;
5439 TopoDS_Edge aTrackEdge;
5440 TopoDS_Vertex aV1, aV2;
5442 SMDS_ElemIteratorPtr aItE;
5443 SMDS_NodeIteratorPtr aItN;
5444 SMDSAbs_ElementType aTypeE;
5446 TNodeOfNodeListMap mapNewNodes;
5449 aNbE = theElements.size();
5452 return EXTR_NO_ELEMENTS;
5454 // 1.1 Track Pattern
5457 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5459 aItE = pSubMeshDS->GetElements();
5460 while ( aItE->more() ) {
5461 const SMDS_MeshElement* pE = aItE->next();
5462 aTypeE = pE->GetType();
5463 // Pattern must contain links only
5464 if ( aTypeE != SMDSAbs_Edge )
5465 return EXTR_PATH_NOT_EDGE;
5468 list<SMESH_MeshEditor_PathPoint> fullList;
5470 const TopoDS_Shape& aS = theTrack->GetSubShape();
5471 // Sub-shape for the Pattern must be an Edge or Wire
5472 if( aS.ShapeType() == TopAbs_EDGE ) {
5473 aTrackEdge = TopoDS::Edge( aS );
5474 // the Edge must not be degenerated
5475 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5476 return EXTR_BAD_PATH_SHAPE;
5477 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5478 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5479 const SMDS_MeshNode* aN1 = aItN->next();
5480 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5481 const SMDS_MeshNode* aN2 = aItN->next();
5482 // starting node must be aN1 or aN2
5483 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5484 return EXTR_BAD_STARTING_NODE;
5485 aItN = pSubMeshDS->GetNodes();
5486 while ( aItN->more() ) {
5487 const SMDS_MeshNode* pNode = aItN->next();
5488 const SMDS_EdgePosition* pEPos =
5489 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5490 double aT = pEPos->GetUParameter();
5491 aPrms.push_back( aT );
5493 //Extrusion_Error err =
5494 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5495 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5496 list< SMESH_subMesh* > LSM;
5497 TopTools_SequenceOfShape Edges;
5498 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5499 while(itSM->more()) {
5500 SMESH_subMesh* SM = itSM->next();
5502 const TopoDS_Shape& aS = SM->GetSubShape();
5505 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5506 int startNid = theN1->GetID();
5507 TColStd_MapOfInteger UsedNums;
5509 int NbEdges = Edges.Length();
5511 for(; i<=NbEdges; i++) {
5513 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5514 for(; itLSM!=LSM.end(); itLSM++) {
5516 if(UsedNums.Contains(k)) continue;
5517 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5518 SMESH_subMesh* locTrack = *itLSM;
5519 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5520 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5521 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5522 const SMDS_MeshNode* aN1 = aItN->next();
5523 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5524 const SMDS_MeshNode* aN2 = aItN->next();
5525 // starting node must be aN1 or aN2
5526 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5527 // 2. Collect parameters on the track edge
5529 aItN = locMeshDS->GetNodes();
5530 while ( aItN->more() ) {
5531 const SMDS_MeshNode* pNode = aItN->next();
5532 const SMDS_EdgePosition* pEPos =
5533 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5534 double aT = pEPos->GetUParameter();
5535 aPrms.push_back( aT );
5537 list<SMESH_MeshEditor_PathPoint> LPP;
5538 //Extrusion_Error err =
5539 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5540 LLPPs.push_back(LPP);
5542 // update startN for search following egde
5543 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5544 else startNid = aN1->GetID();
5548 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5549 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5550 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5551 for(; itPP!=firstList.end(); itPP++) {
5552 fullList.push_back( *itPP );
5554 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5555 fullList.pop_back();
5557 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5558 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5559 itPP = currList.begin();
5560 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5561 gp_Dir D1 = PP1.Tangent();
5562 gp_Dir D2 = PP2.Tangent();
5563 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5564 (D1.Z()+D2.Z())/2 ) );
5565 PP1.SetTangent(Dnew);
5566 fullList.push_back(PP1);
5568 for(; itPP!=firstList.end(); itPP++) {
5569 fullList.push_back( *itPP );
5571 PP1 = fullList.back();
5572 fullList.pop_back();
5574 // if wire not closed
5575 fullList.push_back(PP1);
5579 return EXTR_BAD_PATH_SHAPE;
5582 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5583 theHasRefPoint, theRefPoint, theMakeGroups);
5587 //=======================================================================
5588 //function : ExtrusionAlongTrack
5590 //=======================================================================
5591 SMESH_MeshEditor::Extrusion_Error
5592 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5593 SMESH_Mesh* theTrack,
5594 const SMDS_MeshNode* theN1,
5595 const bool theHasAngles,
5596 list<double>& theAngles,
5597 const bool theLinearVariation,
5598 const bool theHasRefPoint,
5599 const gp_Pnt& theRefPoint,
5600 const bool theMakeGroups)
5602 myLastCreatedElems.Clear();
5603 myLastCreatedNodes.Clear();
5606 std::list<double> aPrms;
5607 TIDSortedElemSet::iterator itElem;
5610 TopoDS_Edge aTrackEdge;
5611 TopoDS_Vertex aV1, aV2;
5613 SMDS_ElemIteratorPtr aItE;
5614 SMDS_NodeIteratorPtr aItN;
5615 SMDSAbs_ElementType aTypeE;
5617 TNodeOfNodeListMap mapNewNodes;
5620 aNbE = theElements.size();
5623 return EXTR_NO_ELEMENTS;
5625 // 1.1 Track Pattern
5628 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5630 aItE = pMeshDS->elementsIterator();
5631 while ( aItE->more() ) {
5632 const SMDS_MeshElement* pE = aItE->next();
5633 aTypeE = pE->GetType();
5634 // Pattern must contain links only
5635 if ( aTypeE != SMDSAbs_Edge )
5636 return EXTR_PATH_NOT_EDGE;
5639 list<SMESH_MeshEditor_PathPoint> fullList;
5641 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5643 if ( !theTrack->HasShapeToMesh() ) {
5644 //Mesh without shape
5645 const SMDS_MeshNode* currentNode = NULL;
5646 const SMDS_MeshNode* prevNode = theN1;
5647 std::vector<const SMDS_MeshNode*> aNodesList;
5648 aNodesList.push_back(theN1);
5649 int nbEdges = 0, conn=0;
5650 const SMDS_MeshElement* prevElem = NULL;
5651 const SMDS_MeshElement* currentElem = NULL;
5652 int totalNbEdges = theTrack->NbEdges();
5653 SMDS_ElemIteratorPtr nIt;
5656 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5657 return EXTR_BAD_STARTING_NODE;
5660 conn = nbEdgeConnectivity(theN1);
5662 return EXTR_PATH_NOT_EDGE;
5664 aItE = theN1->GetInverseElementIterator();
5665 prevElem = aItE->next();
5666 currentElem = prevElem;
5668 if(totalNbEdges == 1 ) {
5669 nIt = currentElem->nodesIterator();
5670 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5671 if(currentNode == prevNode)
5672 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5673 aNodesList.push_back(currentNode);
5675 nIt = currentElem->nodesIterator();
5676 while( nIt->more() ) {
5677 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5678 if(currentNode == prevNode)
5679 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5680 aNodesList.push_back(currentNode);
5682 //case of the closed mesh
5683 if(currentNode == theN1) {
5688 conn = nbEdgeConnectivity(currentNode);
5690 return EXTR_PATH_NOT_EDGE;
5691 }else if( conn == 1 && nbEdges > 0 ) {
5696 prevNode = currentNode;
5697 aItE = currentNode->GetInverseElementIterator();
5698 currentElem = aItE->next();
5699 if( currentElem == prevElem)
5700 currentElem = aItE->next();
5701 nIt = currentElem->nodesIterator();
5702 prevElem = currentElem;
5708 if(nbEdges != totalNbEdges)
5709 return EXTR_PATH_NOT_EDGE;
5711 TopTools_SequenceOfShape Edges;
5712 double x1,x2,y1,y2,z1,z2;
5713 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5714 int startNid = theN1->GetID();
5715 for(int i = 1; i < aNodesList.size(); i++) {
5716 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5717 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5718 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5719 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5720 list<SMESH_MeshEditor_PathPoint> LPP;
5722 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5723 LLPPs.push_back(LPP);
5724 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5725 else startNid = aNodesList[i-1]->GetID();
5729 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5730 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5731 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5732 for(; itPP!=firstList.end(); itPP++) {
5733 fullList.push_back( *itPP );
5736 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5737 SMESH_MeshEditor_PathPoint PP2;
5738 fullList.pop_back();
5740 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5741 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5742 itPP = currList.begin();
5743 PP2 = currList.front();
5744 gp_Dir D1 = PP1.Tangent();
5745 gp_Dir D2 = PP2.Tangent();
5746 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5747 (D1.Z()+D2.Z())/2 ) );
5748 PP1.SetTangent(Dnew);
5749 fullList.push_back(PP1);
5751 for(; itPP!=currList.end(); itPP++) {
5752 fullList.push_back( *itPP );
5754 PP1 = fullList.back();
5755 fullList.pop_back();
5757 fullList.push_back(PP1);
5759 } // Sub-shape for the Pattern must be an Edge or Wire
5760 else if( aS.ShapeType() == TopAbs_EDGE ) {
5761 aTrackEdge = TopoDS::Edge( aS );
5762 // the Edge must not be degenerated
5763 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5764 return EXTR_BAD_PATH_SHAPE;
5765 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5766 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5767 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5768 // starting node must be aN1 or aN2
5769 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5770 return EXTR_BAD_STARTING_NODE;
5771 aItN = pMeshDS->nodesIterator();
5772 while ( aItN->more() ) {
5773 const SMDS_MeshNode* pNode = aItN->next();
5774 if( pNode==aN1 || pNode==aN2 ) continue;
5775 const SMDS_EdgePosition* pEPos =
5776 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5777 double aT = pEPos->GetUParameter();
5778 aPrms.push_back( aT );
5780 //Extrusion_Error err =
5781 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5783 else if( aS.ShapeType() == TopAbs_WIRE ) {
5784 list< SMESH_subMesh* > LSM;
5785 TopTools_SequenceOfShape Edges;
5786 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5787 for(; eExp.More(); eExp.Next()) {
5788 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5789 if( SMESH_Algo::isDegenerated(E) ) continue;
5790 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5796 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5797 TopoDS_Vertex aVprev;
5798 TColStd_MapOfInteger UsedNums;
5799 int NbEdges = Edges.Length();
5801 for(; i<=NbEdges; i++) {
5803 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5804 for(; itLSM!=LSM.end(); itLSM++) {
5806 if(UsedNums.Contains(k)) continue;
5807 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5808 SMESH_subMesh* locTrack = *itLSM;
5809 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5810 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5811 bool aN1isOK = false, aN2isOK = false;
5812 if ( aVprev.IsNull() ) {
5813 // if previous vertex is not yet defined, it means that we in the beginning of wire
5814 // and we have to find initial vertex corresponding to starting node theN1
5815 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5816 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5817 // starting node must be aN1 or aN2
5818 aN1isOK = ( aN1 && aN1 == theN1 );
5819 aN2isOK = ( aN2 && aN2 == theN1 );
5822 // we have specified ending vertex of the previous edge on the previous iteration
5823 // and we have just to check that it corresponds to any vertex in current segment
5824 aN1isOK = aVprev.IsSame( aV1 );
5825 aN2isOK = aVprev.IsSame( aV2 );
5827 if ( !aN1isOK && !aN2isOK ) continue;
5828 // 2. Collect parameters on the track edge
5830 aItN = locMeshDS->GetNodes();
5831 while ( aItN->more() ) {
5832 const SMDS_MeshNode* pNode = aItN->next();
5833 const SMDS_EdgePosition* pEPos =
5834 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5835 double aT = pEPos->GetUParameter();
5836 aPrms.push_back( aT );
5838 list<SMESH_MeshEditor_PathPoint> LPP;
5839 //Extrusion_Error err =
5840 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5841 LLPPs.push_back(LPP);
5843 // update startN for search following egde
5844 if ( aN1isOK ) aVprev = aV2;
5849 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5850 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5851 fullList.splice( fullList.end(), firstList );
5853 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5854 fullList.pop_back();
5856 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5857 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5858 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5859 gp_Dir D1 = PP1.Tangent();
5860 gp_Dir D2 = PP2.Tangent();
5861 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5862 PP1.SetTangent(Dnew);
5863 fullList.push_back(PP1);
5864 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5865 PP1 = fullList.back();
5866 fullList.pop_back();
5868 // if wire not closed
5869 fullList.push_back(PP1);
5873 return EXTR_BAD_PATH_SHAPE;
5876 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5877 theHasRefPoint, theRefPoint, theMakeGroups);
5881 //=======================================================================
5882 //function : MakeEdgePathPoints
5883 //purpose : auxilary for ExtrusionAlongTrack
5884 //=======================================================================
5885 SMESH_MeshEditor::Extrusion_Error
5886 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5887 const TopoDS_Edge& aTrackEdge,
5889 list<SMESH_MeshEditor_PathPoint>& LPP)
5891 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5893 aTolVec2=aTolVec*aTolVec;
5895 TopoDS_Vertex aV1, aV2;
5896 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5897 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5898 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5899 // 2. Collect parameters on the track edge
5900 aPrms.push_front( aT1 );
5901 aPrms.push_back( aT2 );
5904 if( FirstIsStart ) {
5915 SMESH_MeshEditor_PathPoint aPP;
5916 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5917 std::list<double>::iterator aItD = aPrms.begin();
5918 for(; aItD != aPrms.end(); ++aItD) {
5922 aC3D->D1( aT, aP3D, aVec );
5923 aL2 = aVec.SquareMagnitude();
5924 if ( aL2 < aTolVec2 )
5925 return EXTR_CANT_GET_TANGENT;
5926 gp_Dir aTgt( aVec );
5928 aPP.SetTangent( aTgt );
5929 aPP.SetParameter( aT );
5936 //=======================================================================
5937 //function : MakeExtrElements
5938 //purpose : auxilary for ExtrusionAlongTrack
5939 //=======================================================================
5940 SMESH_MeshEditor::Extrusion_Error
5941 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5942 list<SMESH_MeshEditor_PathPoint>& fullList,
5943 const bool theHasAngles,
5944 list<double>& theAngles,
5945 const bool theLinearVariation,
5946 const bool theHasRefPoint,
5947 const gp_Pnt& theRefPoint,
5948 const bool theMakeGroups)
5950 const int aNbTP = fullList.size();
5952 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5953 LinearAngleVariation(aNbTP-1, theAngles);
5954 // fill vector of path points with angles
5955 vector<SMESH_MeshEditor_PathPoint> aPPs;
5956 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5957 list<double>::iterator itAngles = theAngles.begin();
5958 aPPs.push_back( *itPP++ );
5959 for( ; itPP != fullList.end(); itPP++) {
5960 aPPs.push_back( *itPP );
5961 if ( theHasAngles && itAngles != theAngles.end() )
5962 aPPs.back().SetAngle( *itAngles++ );
5965 TNodeOfNodeListMap mapNewNodes;
5966 TElemOfVecOfNnlmiMap mapElemNewNodes;
5967 TTElemOfElemListMap newElemsMap;
5968 TIDSortedElemSet::iterator itElem;
5969 // source elements for each generated one
5970 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5972 // 3. Center of rotation aV0
5973 gp_Pnt aV0 = theRefPoint;
5974 if ( !theHasRefPoint )
5976 gp_XYZ aGC( 0.,0.,0. );
5977 TIDSortedElemSet newNodes;
5979 itElem = theElements.begin();
5980 for ( ; itElem != theElements.end(); itElem++ ) {
5981 const SMDS_MeshElement* elem = *itElem;
5983 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5984 while ( itN->more() ) {
5985 const SMDS_MeshElement* node = itN->next();
5986 if ( newNodes.insert( node ).second )
5987 aGC += SMESH_TNodeXYZ( node );
5990 aGC /= newNodes.size();
5992 } // if (!theHasRefPoint) {
5994 // 4. Processing the elements
5995 SMESHDS_Mesh* aMesh = GetMeshDS();
5997 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5998 // check element type
5999 const SMDS_MeshElement* elem = *itElem;
6000 SMDSAbs_ElementType aTypeE = elem->GetType();
6001 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6004 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6005 newNodesItVec.reserve( elem->NbNodes() );
6007 // loop on elem nodes
6009 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6010 while ( itN->more() )
6013 // check if a node has been already processed
6014 const SMDS_MeshNode* node =
6015 static_cast<const SMDS_MeshNode*>( itN->next() );
6016 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6017 if ( nIt == mapNewNodes.end() ) {
6018 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6019 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6022 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6023 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6024 gp_Ax1 anAx1, anAxT1T0;
6025 gp_Dir aDT1x, aDT0x, aDT1T0;
6030 aPN0 = SMESH_TNodeXYZ( node );
6032 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6034 aDT0x= aPP0.Tangent();
6035 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6037 for ( int j = 1; j < aNbTP; ++j ) {
6038 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6040 aDT1x = aPP1.Tangent();
6041 aAngle1x = aPP1.Angle();
6043 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6045 gp_Vec aV01x( aP0x, aP1x );
6046 aTrsf.SetTranslation( aV01x );
6049 aV1x = aV0x.Transformed( aTrsf );
6050 aPN1 = aPN0.Transformed( aTrsf );
6052 // rotation 1 [ T1,T0 ]
6053 aAngleT1T0=-aDT1x.Angle( aDT0x );
6054 if (fabs(aAngleT1T0) > aTolAng) {
6056 anAxT1T0.SetLocation( aV1x );
6057 anAxT1T0.SetDirection( aDT1T0 );
6058 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6060 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6064 if ( theHasAngles ) {
6065 anAx1.SetLocation( aV1x );
6066 anAx1.SetDirection( aDT1x );
6067 aTrsfRot.SetRotation( anAx1, aAngle1x );
6069 aPN1 = aPN1.Transformed( aTrsfRot );
6073 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6074 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6075 // create additional node
6076 double x = ( aPN1.X() + aPN0.X() )/2.;
6077 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6078 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6079 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6080 myLastCreatedNodes.Append(newNode);
6081 srcNodes.Append( node );
6082 listNewNodes.push_back( newNode );
6084 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6085 myLastCreatedNodes.Append(newNode);
6086 srcNodes.Append( node );
6087 listNewNodes.push_back( newNode );
6097 // if current elem is quadratic and current node is not medium
6098 // we have to check - may be it is needed to insert additional nodes
6099 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6100 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6101 if(listNewNodes.size()==aNbTP-1) {
6102 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6103 gp_XYZ P(node->X(), node->Y(), node->Z());
6104 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6106 for(i=0; i<aNbTP-1; i++) {
6107 const SMDS_MeshNode* N = *it;
6108 double x = ( N->X() + P.X() )/2.;
6109 double y = ( N->Y() + P.Y() )/2.;
6110 double z = ( N->Z() + P.Z() )/2.;
6111 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6112 srcNodes.Append( node );
6113 myLastCreatedNodes.Append(newN);
6116 P = gp_XYZ(N->X(),N->Y(),N->Z());
6118 listNewNodes.clear();
6119 for(i=0; i<2*(aNbTP-1); i++) {
6120 listNewNodes.push_back(aNodes[i]);
6126 newNodesItVec.push_back( nIt );
6128 // make new elements
6129 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6130 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6131 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6134 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6136 if ( theMakeGroups )
6137 generateGroups( srcNodes, srcElems, "extruded");
6143 //=======================================================================
6144 //function : LinearAngleVariation
6145 //purpose : auxilary for ExtrusionAlongTrack
6146 //=======================================================================
6147 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6148 list<double>& Angles)
6150 int nbAngles = Angles.size();
6151 if( nbSteps > nbAngles ) {
6152 vector<double> theAngles(nbAngles);
6153 list<double>::iterator it = Angles.begin();
6155 for(; it!=Angles.end(); it++) {
6157 theAngles[i] = (*it);
6160 double rAn2St = double( nbAngles ) / double( nbSteps );
6161 double angPrev = 0, angle;
6162 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6163 double angCur = rAn2St * ( iSt+1 );
6164 double angCurFloor = floor( angCur );
6165 double angPrevFloor = floor( angPrev );
6166 if ( angPrevFloor == angCurFloor )
6167 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6169 int iP = int( angPrevFloor );
6170 double angPrevCeil = ceil(angPrev);
6171 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6173 int iC = int( angCurFloor );
6174 if ( iC < nbAngles )
6175 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6177 iP = int( angPrevCeil );
6179 angle += theAngles[ iC ];
6181 res.push_back(angle);
6186 for(; it!=res.end(); it++)
6187 Angles.push_back( *it );
6192 //================================================================================
6194 * \brief Move or copy theElements applying theTrsf to their nodes
6195 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6196 * \param theTrsf - transformation to apply
6197 * \param theCopy - if true, create translated copies of theElems
6198 * \param theMakeGroups - if true and theCopy, create translated groups
6199 * \param theTargetMesh - mesh to copy translated elements into
6200 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6202 //================================================================================
6204 SMESH_MeshEditor::PGroupIDs
6205 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6206 const gp_Trsf& theTrsf,
6208 const bool theMakeGroups,
6209 SMESH_Mesh* theTargetMesh)
6211 myLastCreatedElems.Clear();
6212 myLastCreatedNodes.Clear();
6214 bool needReverse = false;
6215 string groupPostfix;
6216 switch ( theTrsf.Form() ) {
6218 MESSAGE("gp_PntMirror");
6220 groupPostfix = "mirrored";
6223 MESSAGE("gp_Ax1Mirror");
6224 groupPostfix = "mirrored";
6227 MESSAGE("gp_Ax2Mirror");
6229 groupPostfix = "mirrored";
6232 MESSAGE("gp_Rotation");
6233 groupPostfix = "rotated";
6235 case gp_Translation:
6236 MESSAGE("gp_Translation");
6237 groupPostfix = "translated";
6240 MESSAGE("gp_Scale");
6241 groupPostfix = "scaled";
6243 case gp_CompoundTrsf: // different scale by axis
6244 MESSAGE("gp_CompoundTrsf");
6245 groupPostfix = "scaled";
6249 needReverse = false;
6250 groupPostfix = "transformed";
6253 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6254 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6255 SMESHDS_Mesh* aMesh = GetMeshDS();
6258 // map old node to new one
6259 TNodeNodeMap nodeMap;
6261 // elements sharing moved nodes; those of them which have all
6262 // nodes mirrored but are not in theElems are to be reversed
6263 TIDSortedElemSet inverseElemSet;
6265 // source elements for each generated one
6266 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6268 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6269 TIDSortedElemSet orphanNode;
6271 if ( theElems.empty() ) // transform the whole mesh
6274 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6275 while ( eIt->more() ) theElems.insert( eIt->next() );
6277 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6278 while ( nIt->more() )
6280 const SMDS_MeshNode* node = nIt->next();
6281 if ( node->NbInverseElements() == 0)
6282 orphanNode.insert( node );
6286 // loop on elements to transform nodes : first orphan nodes then elems
6287 TIDSortedElemSet::iterator itElem;
6288 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6289 for (int i=0; i<2; i++)
6290 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6291 const SMDS_MeshElement* elem = *itElem;
6295 // loop on elem nodes
6296 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6297 while ( itN->more() ) {
6299 const SMDS_MeshNode* node = cast2Node( itN->next() );
6300 // check if a node has been already transformed
6301 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6302 nodeMap.insert( make_pair ( node, node ));
6303 if ( !n2n_isnew.second )
6307 coord[0] = node->X();
6308 coord[1] = node->Y();
6309 coord[2] = node->Z();
6310 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6311 if ( theTargetMesh ) {
6312 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6313 n2n_isnew.first->second = newNode;
6314 myLastCreatedNodes.Append(newNode);
6315 srcNodes.Append( node );
6317 else if ( theCopy ) {
6318 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6319 n2n_isnew.first->second = newNode;
6320 myLastCreatedNodes.Append(newNode);
6321 srcNodes.Append( node );
6324 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6325 // node position on shape becomes invalid
6326 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6327 ( SMDS_SpacePosition::originSpacePosition() );
6330 // keep inverse elements
6331 if ( !theCopy && !theTargetMesh && needReverse ) {
6332 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6333 while ( invElemIt->more() ) {
6334 const SMDS_MeshElement* iel = invElemIt->next();
6335 inverseElemSet.insert( iel );
6341 // either create new elements or reverse mirrored ones
6342 if ( !theCopy && !needReverse && !theTargetMesh )
6345 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6346 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6347 theElems.insert( *invElemIt );
6349 // Replicate or reverse elements
6351 std::vector<int> iForw;
6352 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6354 const SMDS_MeshElement* elem = *itElem;
6355 if ( !elem ) continue;
6357 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6358 int nbNodes = elem->NbNodes();
6359 if ( geomType == SMDSGeom_NONE ) continue; // node
6361 switch ( geomType ) {
6363 case SMDSGeom_POLYGON: // ---------------------- polygon
6365 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6367 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6368 while (itN->more()) {
6369 const SMDS_MeshNode* node =
6370 static_cast<const SMDS_MeshNode*>(itN->next());
6371 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6372 if (nodeMapIt == nodeMap.end())
6373 break; // not all nodes transformed
6375 // reverse mirrored faces and volumes
6376 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6378 poly_nodes[iNode] = (*nodeMapIt).second;
6382 if ( iNode != nbNodes )
6383 continue; // not all nodes transformed
6385 if ( theTargetMesh ) {
6386 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6387 srcElems.Append( elem );
6389 else if ( theCopy ) {
6390 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6391 srcElems.Append( elem );
6394 aMesh->ChangePolygonNodes(elem, poly_nodes);
6399 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6401 const SMDS_VtkVolume* aPolyedre =
6402 dynamic_cast<const SMDS_VtkVolume*>( elem );
6404 MESSAGE("Warning: bad volumic element");
6408 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6409 vector<int> quantities; quantities.reserve( nbNodes );
6411 bool allTransformed = true;
6412 int nbFaces = aPolyedre->NbFaces();
6413 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6414 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6415 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6416 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6417 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6418 if (nodeMapIt == nodeMap.end()) {
6419 allTransformed = false; // not all nodes transformed
6421 poly_nodes.push_back((*nodeMapIt).second);
6423 if ( needReverse && allTransformed )
6424 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6426 quantities.push_back(nbFaceNodes);
6428 if ( !allTransformed )
6429 continue; // not all nodes transformed
6431 if ( theTargetMesh ) {
6432 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6433 srcElems.Append( elem );
6435 else if ( theCopy ) {
6436 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6437 srcElems.Append( elem );
6440 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6445 case SMDSGeom_BALL: // -------------------- Ball
6447 if ( !theCopy && !theTargetMesh ) continue;
6449 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6450 if (nodeMapIt == nodeMap.end())
6451 continue; // not all nodes transformed
6453 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6454 if ( theTargetMesh ) {
6455 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6456 srcElems.Append( elem );
6459 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6460 srcElems.Append( elem );
6465 default: // ----------------------- Regular elements
6467 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6468 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6469 const std::vector<int>& i = needReverse ? iRev : iForw;
6471 // find transformed nodes
6472 vector<const SMDS_MeshNode*> nodes(nbNodes);
6474 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6475 while ( itN->more() ) {
6476 const SMDS_MeshNode* node =
6477 static_cast<const SMDS_MeshNode*>( itN->next() );
6478 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6479 if ( nodeMapIt == nodeMap.end() )
6480 break; // not all nodes transformed
6481 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6483 if ( iNode != nbNodes )
6484 continue; // not all nodes transformed
6486 if ( theTargetMesh ) {
6487 if ( SMDS_MeshElement* copy =
6488 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6489 myLastCreatedElems.Append( copy );
6490 srcElems.Append( elem );
6493 else if ( theCopy ) {
6494 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6495 srcElems.Append( elem );
6498 // reverse element as it was reversed by transformation
6500 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6502 } // switch ( geomType )
6504 } // loop on elements
6506 PGroupIDs newGroupIDs;
6508 if ( ( theMakeGroups && theCopy ) ||
6509 ( theMakeGroups && theTargetMesh ) )
6510 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6515 //=======================================================================
6517 * \brief Create groups of elements made during transformation
6518 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6519 * \param elemGens - elements making corresponding myLastCreatedElems
6520 * \param postfix - to append to names of new groups
6521 * \param targetMesh - mesh to create groups in
6522 * \param topPresent - is there "top" elements that are created by sweeping
6524 //=======================================================================
6526 SMESH_MeshEditor::PGroupIDs
6527 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6528 const SMESH_SequenceOfElemPtr& elemGens,
6529 const std::string& postfix,
6530 SMESH_Mesh* targetMesh,
6531 const bool topPresent)
6533 PGroupIDs newGroupIDs( new list<int> );
6534 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6536 // Sort existing groups by types and collect their names
6538 // containers to store an old group and generated new ones;
6539 // 1st new group is for result elems of different type than a source one;
6540 // 2nd new group is for same type result elems ("top" group at extrusion)
6542 using boost::make_tuple;
6543 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6544 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6545 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6547 set< string > groupNames;
6549 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6550 if ( !groupIt->more() ) return newGroupIDs;
6552 int newGroupID = mesh->GetGroupIds().back()+1;
6553 while ( groupIt->more() )
6555 SMESH_Group * group = groupIt->next();
6556 if ( !group ) continue;
6557 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6558 if ( !groupDS || groupDS->IsEmpty() ) continue;
6559 groupNames.insert ( group->GetName() );
6560 groupDS->SetStoreName( group->GetName() );
6561 const SMDSAbs_ElementType type = groupDS->GetType();
6562 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6563 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6564 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6565 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6568 // Loop on nodes and elements to add them in new groups
6570 vector< const SMDS_MeshElement* > resultElems;
6571 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6573 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6574 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6575 if ( gens.Length() != elems.Length() )
6576 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6578 // loop on created elements
6579 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6581 const SMDS_MeshElement* sourceElem = gens( iElem );
6582 if ( !sourceElem ) {
6583 MESSAGE("generateGroups(): NULL source element");
6586 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6587 if ( groupsOldNew.empty() ) { // no groups of this type at all
6588 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6589 ++iElem; // skip all elements made by sourceElem
6592 // collect all elements made by the iElem-th sourceElem
6593 resultElems.clear();
6594 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6595 if ( resElem != sourceElem )
6596 resultElems.push_back( resElem );
6597 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6598 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6599 if ( resElem != sourceElem )
6600 resultElems.push_back( resElem );
6602 const SMDS_MeshElement* topElem = 0;
6603 if ( isNodes ) // there must be a top element
6605 topElem = resultElems.back();
6606 resultElems.pop_back();
6610 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6611 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6612 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6614 topElem = *resElemIt;
6615 *resElemIt = 0; // erase *resElemIt
6619 // add resultElems to groups originted from ones the sourceElem belongs to
6620 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6621 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6623 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6624 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6626 // fill in a new group
6627 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6628 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6629 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6631 newGroup.Add( *resElemIt );
6633 // fill a "top" group
6636 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6637 newTopGroup.Add( topElem );
6641 } // loop on created elements
6642 }// loop on nodes and elements
6644 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6646 list<int> topGrouIds;
6647 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6649 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6650 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6651 orderedOldNewGroups[i]->get<2>() };
6652 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6654 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6655 if ( newGroupDS->IsEmpty() )
6657 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6662 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6665 const bool isTop = ( topPresent &&
6666 newGroupDS->GetType() == oldGroupDS->GetType() &&
6669 string name = oldGroupDS->GetStoreName();
6670 { // remove trailing whitespaces (issue 22599)
6671 size_t size = name.size();
6672 while ( size > 1 && isspace( name[ size-1 ]))
6674 if ( size != name.size() )
6676 name.resize( size );
6677 oldGroupDS->SetStoreName( name.c_str() );
6680 if ( !targetMesh ) {
6681 string suffix = ( isTop ? "top": postfix.c_str() );
6685 while ( !groupNames.insert( name ).second ) // name exists
6686 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6691 newGroupDS->SetStoreName( name.c_str() );
6693 // make a SMESH_Groups
6694 mesh->AddGroup( newGroupDS );
6696 topGrouIds.push_back( newGroupDS->GetID() );
6698 newGroupIDs->push_back( newGroupDS->GetID() );
6702 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6707 //================================================================================
6709 * \brief Return list of group of nodes close to each other within theTolerance
6710 * Search among theNodes or in the whole mesh if theNodes is empty using
6711 * an Octree algorithm
6713 //================================================================================
6715 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6716 const double theTolerance,
6717 TListOfListOfNodes & theGroupsOfNodes)
6719 myLastCreatedElems.Clear();
6720 myLastCreatedNodes.Clear();
6722 if ( theNodes.empty() )
6723 { // get all nodes in the mesh
6724 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6725 while ( nIt->more() )
6726 theNodes.insert( theNodes.end(),nIt->next());
6729 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6732 //=======================================================================
6733 //function : SimplifyFace
6735 //=======================================================================
6737 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6738 vector<const SMDS_MeshNode *>& poly_nodes,
6739 vector<int>& quantities) const
6741 int nbNodes = faceNodes.size();
6746 set<const SMDS_MeshNode*> nodeSet;
6748 // get simple seq of nodes
6749 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6750 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6751 int iSimple = 0, nbUnique = 0;
6753 simpleNodes[iSimple++] = faceNodes[0];
6755 for (int iCur = 1; iCur < nbNodes; iCur++) {
6756 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6757 simpleNodes[iSimple++] = faceNodes[iCur];
6758 if (nodeSet.insert( faceNodes[iCur] ).second)
6762 int nbSimple = iSimple;
6763 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6773 bool foundLoop = (nbSimple > nbUnique);
6776 set<const SMDS_MeshNode*> loopSet;
6777 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6778 const SMDS_MeshNode* n = simpleNodes[iSimple];
6779 if (!loopSet.insert( n ).second) {
6783 int iC = 0, curLast = iSimple;
6784 for (; iC < curLast; iC++) {
6785 if (simpleNodes[iC] == n) break;
6787 int loopLen = curLast - iC;
6789 // create sub-element
6791 quantities.push_back(loopLen);
6792 for (; iC < curLast; iC++) {
6793 poly_nodes.push_back(simpleNodes[iC]);
6796 // shift the rest nodes (place from the first loop position)
6797 for (iC = curLast + 1; iC < nbSimple; iC++) {
6798 simpleNodes[iC - loopLen] = simpleNodes[iC];
6800 nbSimple -= loopLen;
6803 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6804 } // while (foundLoop)
6808 quantities.push_back(iSimple);
6809 for (int i = 0; i < iSimple; i++)
6810 poly_nodes.push_back(simpleNodes[i]);
6816 //=======================================================================
6817 //function : MergeNodes
6818 //purpose : In each group, the cdr of nodes are substituted by the first one
6820 //=======================================================================
6822 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6824 MESSAGE("MergeNodes");
6825 myLastCreatedElems.Clear();
6826 myLastCreatedNodes.Clear();
6828 SMESHDS_Mesh* aMesh = GetMeshDS();
6830 TNodeNodeMap nodeNodeMap; // node to replace - new node
6831 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6832 list< int > rmElemIds, rmNodeIds;
6834 // Fill nodeNodeMap and elems
6836 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6837 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6838 list<const SMDS_MeshNode*>& nodes = *grIt;
6839 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6840 const SMDS_MeshNode* nToKeep = *nIt;
6841 //MESSAGE("node to keep " << nToKeep->GetID());
6842 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6843 const SMDS_MeshNode* nToRemove = *nIt;
6844 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6845 if ( nToRemove != nToKeep ) {
6846 //MESSAGE(" node to remove " << nToRemove->GetID());
6847 rmNodeIds.push_back( nToRemove->GetID() );
6848 AddToSameGroups( nToKeep, nToRemove, aMesh );
6849 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6850 // after MergeNodes() w/o creating node in place of merged ones.
6851 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6852 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6853 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6854 sm->SetIsAlwaysComputed( true );
6857 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6858 while ( invElemIt->more() ) {
6859 const SMDS_MeshElement* elem = invElemIt->next();
6864 // Change element nodes or remove an element
6866 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6867 for ( ; eIt != elems.end(); eIt++ ) {
6868 const SMDS_MeshElement* elem = *eIt;
6869 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6870 int nbNodes = elem->NbNodes();
6871 int aShapeId = FindShape( elem );
6873 set<const SMDS_MeshNode*> nodeSet;
6874 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6875 int iUnique = 0, iCur = 0, nbRepl = 0;
6876 vector<int> iRepl( nbNodes );
6878 // get new seq of nodes
6879 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6880 while ( itN->more() ) {
6881 const SMDS_MeshNode* n =
6882 static_cast<const SMDS_MeshNode*>( itN->next() );
6884 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6885 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6887 // BUG 0020185: begin
6889 bool stopRecur = false;
6890 set<const SMDS_MeshNode*> nodesRecur;
6891 nodesRecur.insert(n);
6892 while (!stopRecur) {
6893 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6894 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6895 n = (*nnIt_i).second;
6896 if (!nodesRecur.insert(n).second) {
6897 // error: recursive dependancy
6907 curNodes[ iCur ] = n;
6908 bool isUnique = nodeSet.insert( n ).second;
6910 uniqueNodes[ iUnique++ ] = n;
6912 iRepl[ nbRepl++ ] = iCur;
6916 // Analyse element topology after replacement
6919 int nbUniqueNodes = nodeSet.size();
6920 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6921 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6922 // Polygons and Polyhedral volumes
6923 if (elem->IsPoly()) {
6925 if (elem->GetType() == SMDSAbs_Face) {
6927 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6929 for (; inode < nbNodes; inode++) {
6930 face_nodes[inode] = curNodes[inode];
6933 vector<const SMDS_MeshNode *> polygons_nodes;
6934 vector<int> quantities;
6935 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6938 for (int iface = 0; iface < nbNew; iface++) {
6939 int nbNodes = quantities[iface];
6940 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6941 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6942 poly_nodes[ii] = polygons_nodes[inode];
6944 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6945 myLastCreatedElems.Append(newElem);
6947 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6950 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6951 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6952 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6954 if (nbNew > 0) quid = nbNew - 1;
6955 vector<int> newquant(quantities.begin()+quid, quantities.end());
6956 const SMDS_MeshElement* newElem = 0;
6957 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6958 myLastCreatedElems.Append(newElem);
6959 if ( aShapeId && newElem )
6960 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6961 rmElemIds.push_back(elem->GetID());
6964 rmElemIds.push_back(elem->GetID());
6968 else if (elem->GetType() == SMDSAbs_Volume) {
6969 // Polyhedral volume
6970 if (nbUniqueNodes < 4) {
6971 rmElemIds.push_back(elem->GetID());
6974 // each face has to be analyzed in order to check volume validity
6975 const SMDS_VtkVolume* aPolyedre =
6976 dynamic_cast<const SMDS_VtkVolume*>( elem );
6978 int nbFaces = aPolyedre->NbFaces();
6980 vector<const SMDS_MeshNode *> poly_nodes;
6981 vector<int> quantities;
6983 for (int iface = 1; iface <= nbFaces; iface++) {
6984 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6985 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6987 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6988 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6989 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6990 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6991 faceNode = (*nnIt).second;
6993 faceNodes[inode - 1] = faceNode;
6996 SimplifyFace(faceNodes, poly_nodes, quantities);
6999 if (quantities.size() > 3) {
7000 // to be done: remove coincident faces
7003 if (quantities.size() > 3)
7005 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7006 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7007 const SMDS_MeshElement* newElem = 0;
7008 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7009 myLastCreatedElems.Append(newElem);
7010 if ( aShapeId && newElem )
7011 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7012 rmElemIds.push_back(elem->GetID());
7016 rmElemIds.push_back(elem->GetID());
7027 // TODO not all the possible cases are solved. Find something more generic?
7028 switch ( nbNodes ) {
7029 case 2: ///////////////////////////////////// EDGE
7030 isOk = false; break;
7031 case 3: ///////////////////////////////////// TRIANGLE
7032 isOk = false; break;
7034 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7036 else { //////////////////////////////////// QUADRANGLE
7037 if ( nbUniqueNodes < 3 )
7039 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7040 isOk = false; // opposite nodes stick
7041 //MESSAGE("isOk " << isOk);
7044 case 6: ///////////////////////////////////// PENTAHEDRON
7045 if ( nbUniqueNodes == 4 ) {
7046 // ---------------------------------> tetrahedron
7048 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7049 // all top nodes stick: reverse a bottom
7050 uniqueNodes[ 0 ] = curNodes [ 1 ];
7051 uniqueNodes[ 1 ] = curNodes [ 0 ];
7053 else if (nbRepl == 3 &&
7054 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7055 // all bottom nodes stick: set a top before
7056 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7057 uniqueNodes[ 0 ] = curNodes [ 3 ];
7058 uniqueNodes[ 1 ] = curNodes [ 4 ];
7059 uniqueNodes[ 2 ] = curNodes [ 5 ];
7061 else if (nbRepl == 4 &&
7062 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7063 // a lateral face turns into a line: reverse a bottom
7064 uniqueNodes[ 0 ] = curNodes [ 1 ];
7065 uniqueNodes[ 1 ] = curNodes [ 0 ];
7070 else if ( nbUniqueNodes == 5 ) {
7071 // PENTAHEDRON --------------------> 2 tetrahedrons
7072 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7073 // a bottom node sticks with a linked top one
7075 SMDS_MeshElement* newElem =
7076 aMesh->AddVolume(curNodes[ 3 ],
7079 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7080 myLastCreatedElems.Append(newElem);
7082 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7083 // 2. : reverse a bottom
7084 uniqueNodes[ 0 ] = curNodes [ 1 ];
7085 uniqueNodes[ 1 ] = curNodes [ 0 ];
7095 if(elem->IsQuadratic()) { // Quadratic quadrangle
7107 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7110 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7112 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7113 uniqueNodes[0] = curNodes[0];
7114 uniqueNodes[1] = curNodes[2];
7115 uniqueNodes[2] = curNodes[3];
7116 uniqueNodes[3] = curNodes[5];
7117 uniqueNodes[4] = curNodes[6];
7118 uniqueNodes[5] = curNodes[7];
7121 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7122 uniqueNodes[0] = curNodes[0];
7123 uniqueNodes[1] = curNodes[1];
7124 uniqueNodes[2] = curNodes[2];
7125 uniqueNodes[3] = curNodes[4];
7126 uniqueNodes[4] = curNodes[5];
7127 uniqueNodes[5] = curNodes[6];
7130 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7131 uniqueNodes[0] = curNodes[1];
7132 uniqueNodes[1] = curNodes[2];
7133 uniqueNodes[2] = curNodes[3];
7134 uniqueNodes[3] = curNodes[5];
7135 uniqueNodes[4] = curNodes[6];
7136 uniqueNodes[5] = curNodes[0];
7139 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7140 uniqueNodes[0] = curNodes[0];
7141 uniqueNodes[1] = curNodes[1];
7142 uniqueNodes[2] = curNodes[3];
7143 uniqueNodes[3] = curNodes[4];
7144 uniqueNodes[4] = curNodes[6];
7145 uniqueNodes[5] = curNodes[7];
7148 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7149 uniqueNodes[0] = curNodes[0];
7150 uniqueNodes[1] = curNodes[2];
7151 uniqueNodes[2] = curNodes[3];
7152 uniqueNodes[3] = curNodes[1];
7153 uniqueNodes[4] = curNodes[6];
7154 uniqueNodes[5] = curNodes[7];
7157 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7158 uniqueNodes[0] = curNodes[0];
7159 uniqueNodes[1] = curNodes[1];
7160 uniqueNodes[2] = curNodes[2];
7161 uniqueNodes[3] = curNodes[4];
7162 uniqueNodes[4] = curNodes[5];
7163 uniqueNodes[5] = curNodes[7];
7166 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7167 uniqueNodes[0] = curNodes[0];
7168 uniqueNodes[1] = curNodes[1];
7169 uniqueNodes[2] = curNodes[3];
7170 uniqueNodes[3] = curNodes[4];
7171 uniqueNodes[4] = curNodes[2];
7172 uniqueNodes[5] = curNodes[7];
7175 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7176 uniqueNodes[0] = curNodes[0];
7177 uniqueNodes[1] = curNodes[1];
7178 uniqueNodes[2] = curNodes[2];
7179 uniqueNodes[3] = curNodes[4];
7180 uniqueNodes[4] = curNodes[5];
7181 uniqueNodes[5] = curNodes[3];
7186 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7189 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7193 //////////////////////////////////// HEXAHEDRON
7195 SMDS_VolumeTool hexa (elem);
7196 hexa.SetExternalNormal();
7197 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7198 //////////////////////// HEX ---> 1 tetrahedron
7199 for ( int iFace = 0; iFace < 6; iFace++ ) {
7200 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7201 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7202 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7203 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7204 // one face turns into a point ...
7205 int iOppFace = hexa.GetOppFaceIndex( iFace );
7206 ind = hexa.GetFaceNodesIndices( iOppFace );
7208 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7209 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7212 if ( nbStick == 1 ) {
7213 // ... and the opposite one - into a triangle.
7215 ind = hexa.GetFaceNodesIndices( iFace );
7216 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7223 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7224 //////////////////////// HEX ---> 1 prism
7225 int nbTria = 0, iTria[3];
7226 const int *ind; // indices of face nodes
7227 // look for triangular faces
7228 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7229 ind = hexa.GetFaceNodesIndices( iFace );
7230 TIDSortedNodeSet faceNodes;
7231 for ( iCur = 0; iCur < 4; iCur++ )
7232 faceNodes.insert( curNodes[ind[iCur]] );
7233 if ( faceNodes.size() == 3 )
7234 iTria[ nbTria++ ] = iFace;
7236 // check if triangles are opposite
7237 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7240 // set nodes of the bottom triangle
7241 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7243 for ( iCur = 0; iCur < 4; iCur++ )
7244 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7245 indB.push_back( ind[iCur] );
7246 if ( !hexa.IsForward() )
7247 std::swap( indB[0], indB[2] );
7248 for ( iCur = 0; iCur < 3; iCur++ )
7249 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7250 // set nodes of the top triangle
7251 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7252 for ( iCur = 0; iCur < 3; ++iCur )
7253 for ( int j = 0; j < 4; ++j )
7254 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7256 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7262 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7263 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7264 for ( int iFace = 0; iFace < 6; iFace++ ) {
7265 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7266 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7267 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7268 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7269 // one face turns into a point ...
7270 int iOppFace = hexa.GetOppFaceIndex( iFace );
7271 ind = hexa.GetFaceNodesIndices( iOppFace );
7273 iUnique = 2; // reverse a tetrahedron 1 bottom
7274 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7275 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7277 else if ( iUnique >= 0 )
7278 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7280 if ( nbStick == 0 ) {
7281 // ... and the opposite one is a quadrangle
7283 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7284 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7287 SMDS_MeshElement* newElem =
7288 aMesh->AddVolume(curNodes[ind[ 0 ]],
7291 curNodes[indTop[ 0 ]]);
7292 myLastCreatedElems.Append(newElem);
7294 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7301 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7302 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7303 // find indices of quad and tri faces
7304 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7305 for ( iFace = 0; iFace < 6; iFace++ ) {
7306 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7308 for ( iCur = 0; iCur < 4; iCur++ )
7309 nodeSet.insert( curNodes[ind[ iCur ]] );
7310 nbUniqueNodes = nodeSet.size();
7311 if ( nbUniqueNodes == 3 )
7312 iTriFace[ nbTri++ ] = iFace;
7313 else if ( nbUniqueNodes == 4 )
7314 iQuadFace[ nbQuad++ ] = iFace;
7316 if (nbQuad == 2 && nbTri == 4 &&
7317 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7318 // 2 opposite quadrangles stuck with a diagonal;
7319 // sample groups of merged indices: (0-4)(2-6)
7320 // --------------------------------------------> 2 tetrahedrons
7321 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7322 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7323 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7324 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7325 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7326 // stuck with 0-2 diagonal
7334 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7335 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7336 // stuck with 1-3 diagonal
7348 uniqueNodes[ 0 ] = curNodes [ i0 ];
7349 uniqueNodes[ 1 ] = curNodes [ i1d ];
7350 uniqueNodes[ 2 ] = curNodes [ i3d ];
7351 uniqueNodes[ 3 ] = curNodes [ i0t ];
7354 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7358 myLastCreatedElems.Append(newElem);
7360 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7363 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7364 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7365 // --------------------------------------------> prism
7366 // find 2 opposite triangles
7368 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7369 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7370 // find indices of kept and replaced nodes
7371 // and fill unique nodes of 2 opposite triangles
7372 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7373 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7374 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7375 // fill unique nodes
7378 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7379 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7380 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7382 // iCur of a linked node of the opposite face (make normals co-directed):
7383 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7384 // check that correspondent corners of triangles are linked
7385 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7388 uniqueNodes[ iUnique ] = n;
7389 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7398 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7401 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7408 } // switch ( nbNodes )
7410 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7412 if ( isOk ) { // the elem remains valid after sticking nodes
7413 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7415 // Change nodes of polyedre
7416 const SMDS_VtkVolume* aPolyedre =
7417 dynamic_cast<const SMDS_VtkVolume*>( elem );
7419 int nbFaces = aPolyedre->NbFaces();
7421 vector<const SMDS_MeshNode *> poly_nodes;
7422 vector<int> quantities (nbFaces);
7424 for (int iface = 1; iface <= nbFaces; iface++) {
7425 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7426 quantities[iface - 1] = nbFaceNodes;
7428 for (inode = 1; inode <= nbFaceNodes; inode++) {
7429 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7431 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7432 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7433 curNode = (*nnIt).second;
7435 poly_nodes.push_back(curNode);
7438 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7441 else // replace non-polyhedron elements
7443 const SMDSAbs_ElementType etyp = elem->GetType();
7444 const int elemId = elem->GetID();
7445 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7446 uniqueNodes.resize(nbUniqueNodes);
7448 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7450 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7451 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7452 if ( sm && newElem )
7453 sm->AddElement( newElem );
7454 if ( elem != newElem )
7455 ReplaceElemInGroups( elem, newElem, aMesh );
7459 // Remove invalid regular element or invalid polygon
7460 rmElemIds.push_back( elem->GetID() );
7463 } // loop on elements
7465 // Remove bad elements, then equal nodes (order important)
7467 Remove( rmElemIds, false );
7468 Remove( rmNodeIds, true );
7473 // ========================================================
7474 // class : SortableElement
7475 // purpose : allow sorting elements basing on their nodes
7476 // ========================================================
7477 class SortableElement : public set <const SMDS_MeshElement*>
7481 SortableElement( const SMDS_MeshElement* theElem )
7484 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7485 while ( nodeIt->more() )
7486 this->insert( nodeIt->next() );
7489 const SMDS_MeshElement* Get() const
7492 void Set(const SMDS_MeshElement* e) const
7497 mutable const SMDS_MeshElement* myElem;
7500 //=======================================================================
7501 //function : FindEqualElements
7502 //purpose : Return list of group of elements built on the same nodes.
7503 // Search among theElements or in the whole mesh if theElements is empty
7504 //=======================================================================
7506 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7507 TListOfListOfElementsID & theGroupsOfElementsID)
7509 myLastCreatedElems.Clear();
7510 myLastCreatedNodes.Clear();
7512 typedef map< SortableElement, int > TMapOfNodeSet;
7513 typedef list<int> TGroupOfElems;
7515 if ( theElements.empty() )
7516 { // get all elements in the mesh
7517 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7518 while ( eIt->more() )
7519 theElements.insert( theElements.end(), eIt->next());
7522 vector< TGroupOfElems > arrayOfGroups;
7523 TGroupOfElems groupOfElems;
7524 TMapOfNodeSet mapOfNodeSet;
7526 TIDSortedElemSet::iterator elemIt = theElements.begin();
7527 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7528 const SMDS_MeshElement* curElem = *elemIt;
7529 SortableElement SE(curElem);
7532 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7533 if( !(pp.second) ) {
7534 TMapOfNodeSet::iterator& itSE = pp.first;
7535 ind = (*itSE).second;
7536 arrayOfGroups[ind].push_back(curElem->GetID());
7539 groupOfElems.clear();
7540 groupOfElems.push_back(curElem->GetID());
7541 arrayOfGroups.push_back(groupOfElems);
7546 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7547 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7548 groupOfElems = *groupIt;
7549 if ( groupOfElems.size() > 1 ) {
7550 groupOfElems.sort();
7551 theGroupsOfElementsID.push_back(groupOfElems);
7556 //=======================================================================
7557 //function : MergeElements
7558 //purpose : In each given group, substitute all elements by the first one.
7559 //=======================================================================
7561 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7563 myLastCreatedElems.Clear();
7564 myLastCreatedNodes.Clear();
7566 typedef list<int> TListOfIDs;
7567 TListOfIDs rmElemIds; // IDs of elems to remove
7569 SMESHDS_Mesh* aMesh = GetMeshDS();
7571 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7572 while ( groupsIt != theGroupsOfElementsID.end() ) {
7573 TListOfIDs& aGroupOfElemID = *groupsIt;
7574 aGroupOfElemID.sort();
7575 int elemIDToKeep = aGroupOfElemID.front();
7576 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7577 aGroupOfElemID.pop_front();
7578 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7579 while ( idIt != aGroupOfElemID.end() ) {
7580 int elemIDToRemove = *idIt;
7581 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7582 // add the kept element in groups of removed one (PAL15188)
7583 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7584 rmElemIds.push_back( elemIDToRemove );
7590 Remove( rmElemIds, false );
7593 //=======================================================================
7594 //function : MergeEqualElements
7595 //purpose : Remove all but one of elements built on the same nodes.
7596 //=======================================================================
7598 void SMESH_MeshEditor::MergeEqualElements()
7600 TIDSortedElemSet aMeshElements; /* empty input ==
7601 to merge equal elements in the whole mesh */
7602 TListOfListOfElementsID aGroupsOfElementsID;
7603 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7604 MergeElements(aGroupsOfElementsID);
7607 //=======================================================================
7608 //function : findAdjacentFace
7610 //=======================================================================
7612 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7613 const SMDS_MeshNode* n2,
7614 const SMDS_MeshElement* elem)
7616 TIDSortedElemSet elemSet, avoidSet;
7618 avoidSet.insert ( elem );
7619 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7622 //=======================================================================
7623 //function : FindFreeBorder
7625 //=======================================================================
7627 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7629 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7630 const SMDS_MeshNode* theSecondNode,
7631 const SMDS_MeshNode* theLastNode,
7632 list< const SMDS_MeshNode* > & theNodes,
7633 list< const SMDS_MeshElement* >& theFaces)
7635 if ( !theFirstNode || !theSecondNode )
7637 // find border face between theFirstNode and theSecondNode
7638 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7642 theFaces.push_back( curElem );
7643 theNodes.push_back( theFirstNode );
7644 theNodes.push_back( theSecondNode );
7646 //vector<const SMDS_MeshNode*> nodes;
7647 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7648 TIDSortedElemSet foundElems;
7649 bool needTheLast = ( theLastNode != 0 );
7651 while ( nStart != theLastNode ) {
7652 if ( nStart == theFirstNode )
7653 return !needTheLast;
7655 // find all free border faces sharing form nStart
7657 list< const SMDS_MeshElement* > curElemList;
7658 list< const SMDS_MeshNode* > nStartList;
7659 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7660 while ( invElemIt->more() ) {
7661 const SMDS_MeshElement* e = invElemIt->next();
7662 if ( e == curElem || foundElems.insert( e ).second ) {
7664 int iNode = 0, nbNodes = e->NbNodes();
7665 //const SMDS_MeshNode* nodes[nbNodes+1];
7666 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7668 if(e->IsQuadratic()) {
7669 const SMDS_VtkFace* F =
7670 dynamic_cast<const SMDS_VtkFace*>(e);
7671 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7672 // use special nodes iterator
7673 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7674 while( anIter->more() ) {
7675 nodes[ iNode++ ] = cast2Node(anIter->next());
7679 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7680 while ( nIt->more() )
7681 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7683 nodes[ iNode ] = nodes[ 0 ];
7685 for ( iNode = 0; iNode < nbNodes; iNode++ )
7686 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7687 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7688 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7690 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7691 curElemList.push_back( e );
7695 // analyse the found
7697 int nbNewBorders = curElemList.size();
7698 if ( nbNewBorders == 0 ) {
7699 // no free border furthermore
7700 return !needTheLast;
7702 else if ( nbNewBorders == 1 ) {
7703 // one more element found
7705 nStart = nStartList.front();
7706 curElem = curElemList.front();
7707 theFaces.push_back( curElem );
7708 theNodes.push_back( nStart );
7711 // several continuations found
7712 list< const SMDS_MeshElement* >::iterator curElemIt;
7713 list< const SMDS_MeshNode* >::iterator nStartIt;
7714 // check if one of them reached the last node
7715 if ( needTheLast ) {
7716 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7717 curElemIt!= curElemList.end();
7718 curElemIt++, nStartIt++ )
7719 if ( *nStartIt == theLastNode ) {
7720 theFaces.push_back( *curElemIt );
7721 theNodes.push_back( *nStartIt );
7725 // find the best free border by the continuations
7726 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7727 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7728 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7729 curElemIt!= curElemList.end();
7730 curElemIt++, nStartIt++ )
7732 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7733 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7734 // find one more free border
7735 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7739 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7740 // choice: clear a worse one
7741 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7742 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7743 contNodes[ iWorse ].clear();
7744 contFaces[ iWorse ].clear();
7747 if ( contNodes[0].empty() && contNodes[1].empty() )
7750 // append the best free border
7751 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7752 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7753 theNodes.pop_back(); // remove nIgnore
7754 theNodes.pop_back(); // remove nStart
7755 theFaces.pop_back(); // remove curElem
7756 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7757 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7758 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7759 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7762 } // several continuations found
7763 } // while ( nStart != theLastNode )
7768 //=======================================================================
7769 //function : CheckFreeBorderNodes
7770 //purpose : Return true if the tree nodes are on a free border
7771 //=======================================================================
7773 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7774 const SMDS_MeshNode* theNode2,
7775 const SMDS_MeshNode* theNode3)
7777 list< const SMDS_MeshNode* > nodes;
7778 list< const SMDS_MeshElement* > faces;
7779 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7782 //=======================================================================
7783 //function : SewFreeBorder
7785 //=======================================================================
7787 SMESH_MeshEditor::Sew_Error
7788 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7789 const SMDS_MeshNode* theBordSecondNode,
7790 const SMDS_MeshNode* theBordLastNode,
7791 const SMDS_MeshNode* theSideFirstNode,
7792 const SMDS_MeshNode* theSideSecondNode,
7793 const SMDS_MeshNode* theSideThirdNode,
7794 const bool theSideIsFreeBorder,
7795 const bool toCreatePolygons,
7796 const bool toCreatePolyedrs)
7798 myLastCreatedElems.Clear();
7799 myLastCreatedNodes.Clear();
7801 MESSAGE("::SewFreeBorder()");
7802 Sew_Error aResult = SEW_OK;
7804 // ====================================
7805 // find side nodes and elements
7806 // ====================================
7808 list< const SMDS_MeshNode* > nSide[ 2 ];
7809 list< const SMDS_MeshElement* > eSide[ 2 ];
7810 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7811 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7815 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7816 nSide[0], eSide[0])) {
7817 MESSAGE(" Free Border 1 not found " );
7818 aResult = SEW_BORDER1_NOT_FOUND;
7820 if (theSideIsFreeBorder) {
7823 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7824 nSide[1], eSide[1])) {
7825 MESSAGE(" Free Border 2 not found " );
7826 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7829 if ( aResult != SEW_OK )
7832 if (!theSideIsFreeBorder) {
7836 // -------------------------------------------------------------------------
7838 // 1. If nodes to merge are not coincident, move nodes of the free border
7839 // from the coord sys defined by the direction from the first to last
7840 // nodes of the border to the correspondent sys of the side 2
7841 // 2. On the side 2, find the links most co-directed with the correspondent
7842 // links of the free border
7843 // -------------------------------------------------------------------------
7845 // 1. Since sewing may break if there are volumes to split on the side 2,
7846 // we wont move nodes but just compute new coordinates for them
7847 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7848 TNodeXYZMap nBordXYZ;
7849 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7850 list< const SMDS_MeshNode* >::iterator nBordIt;
7852 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7853 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7854 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7855 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7856 double tol2 = 1.e-8;
7857 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7858 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7859 // Need node movement.
7861 // find X and Z axes to create trsf
7862 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7864 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7866 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7869 gp_Ax3 toBordAx( Pb1, Zb, X );
7870 gp_Ax3 fromSideAx( Ps1, Zs, X );
7871 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7873 gp_Trsf toBordSys, fromSide2Sys;
7874 toBordSys.SetTransformation( toBordAx );
7875 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7876 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7879 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7880 const SMDS_MeshNode* n = *nBordIt;
7881 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7882 toBordSys.Transforms( xyz );
7883 fromSide2Sys.Transforms( xyz );
7884 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7888 // just insert nodes XYZ in the nBordXYZ map
7889 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7890 const SMDS_MeshNode* n = *nBordIt;
7891 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7895 // 2. On the side 2, find the links most co-directed with the correspondent
7896 // links of the free border
7898 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7899 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7900 sideNodes.push_back( theSideFirstNode );
7902 bool hasVolumes = false;
7903 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7904 set<long> foundSideLinkIDs, checkedLinkIDs;
7905 SMDS_VolumeTool volume;
7906 //const SMDS_MeshNode* faceNodes[ 4 ];
7908 const SMDS_MeshNode* sideNode;
7909 const SMDS_MeshElement* sideElem;
7910 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7911 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7912 nBordIt = bordNodes.begin();
7914 // border node position and border link direction to compare with
7915 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7916 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7917 // choose next side node by link direction or by closeness to
7918 // the current border node:
7919 bool searchByDir = ( *nBordIt != theBordLastNode );
7921 // find the next node on the Side 2
7923 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7925 checkedLinkIDs.clear();
7926 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7928 // loop on inverse elements of current node (prevSideNode) on the Side 2
7929 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7930 while ( invElemIt->more() )
7932 const SMDS_MeshElement* elem = invElemIt->next();
7933 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7934 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7935 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7936 bool isVolume = volume.Set( elem );
7937 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7938 if ( isVolume ) // --volume
7940 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7941 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7942 if(elem->IsQuadratic()) {
7943 const SMDS_VtkFace* F =
7944 dynamic_cast<const SMDS_VtkFace*>(elem);
7945 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7946 // use special nodes iterator
7947 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7948 while( anIter->more() ) {
7949 nodes[ iNode ] = cast2Node(anIter->next());
7950 if ( nodes[ iNode++ ] == prevSideNode )
7951 iPrevNode = iNode - 1;
7955 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7956 while ( nIt->more() ) {
7957 nodes[ iNode ] = cast2Node( nIt->next() );
7958 if ( nodes[ iNode++ ] == prevSideNode )
7959 iPrevNode = iNode - 1;
7962 // there are 2 links to check
7967 // loop on links, to be precise, on the second node of links
7968 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7969 const SMDS_MeshNode* n = nodes[ iNode ];
7971 if ( !volume.IsLinked( n, prevSideNode ))
7975 if ( iNode ) // a node before prevSideNode
7976 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7977 else // a node after prevSideNode
7978 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7980 // check if this link was already used
7981 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7982 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7983 if (!isJustChecked &&
7984 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7986 // test a link geometrically
7987 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7988 bool linkIsBetter = false;
7989 double dot = 0.0, dist = 0.0;
7990 if ( searchByDir ) { // choose most co-directed link
7991 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7992 linkIsBetter = ( dot > maxDot );
7994 else { // choose link with the node closest to bordPos
7995 dist = ( nextXYZ - bordPos ).SquareModulus();
7996 linkIsBetter = ( dist < minDist );
7998 if ( linkIsBetter ) {
8007 } // loop on inverse elements of prevSideNode
8010 MESSAGE(" Cant find path by links of the Side 2 ");
8011 return SEW_BAD_SIDE_NODES;
8013 sideNodes.push_back( sideNode );
8014 sideElems.push_back( sideElem );
8015 foundSideLinkIDs.insert ( linkID );
8016 prevSideNode = sideNode;
8018 if ( *nBordIt == theBordLastNode )
8019 searchByDir = false;
8021 // find the next border link to compare with
8022 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8023 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8024 // move to next border node if sideNode is before forward border node (bordPos)
8025 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8026 prevBordNode = *nBordIt;
8028 bordPos = nBordXYZ[ *nBordIt ];
8029 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8030 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8034 while ( sideNode != theSideSecondNode );
8036 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8037 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8038 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8040 } // end nodes search on the side 2
8042 // ============================
8043 // sew the border to the side 2
8044 // ============================
8046 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8047 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8049 TListOfListOfNodes nodeGroupsToMerge;
8050 if ( nbNodes[0] == nbNodes[1] ||
8051 ( theSideIsFreeBorder && !theSideThirdNode)) {
8053 // all nodes are to be merged
8055 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8056 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8057 nIt[0]++, nIt[1]++ )
8059 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8060 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8061 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8066 // insert new nodes into the border and the side to get equal nb of segments
8068 // get normalized parameters of nodes on the borders
8069 //double param[ 2 ][ maxNbNodes ];
8071 param[0] = new double [ maxNbNodes ];
8072 param[1] = new double [ maxNbNodes ];
8074 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8075 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8076 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8077 const SMDS_MeshNode* nPrev = *nIt;
8078 double bordLength = 0;
8079 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8080 const SMDS_MeshNode* nCur = *nIt;
8081 gp_XYZ segment (nCur->X() - nPrev->X(),
8082 nCur->Y() - nPrev->Y(),
8083 nCur->Z() - nPrev->Z());
8084 double segmentLen = segment.Modulus();
8085 bordLength += segmentLen;
8086 param[ iBord ][ iNode ] = bordLength;
8089 // normalize within [0,1]
8090 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8091 param[ iBord ][ iNode ] /= bordLength;
8095 // loop on border segments
8096 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8097 int i[ 2 ] = { 0, 0 };
8098 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8099 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8101 TElemOfNodeListMap insertMap;
8102 TElemOfNodeListMap::iterator insertMapIt;
8104 // key: elem to insert nodes into
8105 // value: 2 nodes to insert between + nodes to be inserted
8107 bool next[ 2 ] = { false, false };
8109 // find min adjacent segment length after sewing
8110 double nextParam = 10., prevParam = 0;
8111 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8112 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8113 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8114 if ( i[ iBord ] > 0 )
8115 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8117 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8118 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8119 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8121 // choose to insert or to merge nodes
8122 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8123 if ( Abs( du ) <= minSegLen * 0.2 ) {
8126 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8127 const SMDS_MeshNode* n0 = *nIt[0];
8128 const SMDS_MeshNode* n1 = *nIt[1];
8129 nodeGroupsToMerge.back().push_back( n1 );
8130 nodeGroupsToMerge.back().push_back( n0 );
8131 // position of node of the border changes due to merge
8132 param[ 0 ][ i[0] ] += du;
8133 // move n1 for the sake of elem shape evaluation during insertion.
8134 // n1 will be removed by MergeNodes() anyway
8135 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8136 next[0] = next[1] = true;
8141 int intoBord = ( du < 0 ) ? 0 : 1;
8142 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8143 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8144 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8145 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8146 if ( intoBord == 1 ) {
8147 // move node of the border to be on a link of elem of the side
8148 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8149 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8150 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8151 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8152 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8154 insertMapIt = insertMap.find( elem );
8155 bool notFound = ( insertMapIt == insertMap.end() );
8156 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8158 // insert into another link of the same element:
8159 // 1. perform insertion into the other link of the elem
8160 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8161 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8162 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8163 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8164 // 2. perform insertion into the link of adjacent faces
8166 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8168 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8172 if (toCreatePolyedrs) {
8173 // perform insertion into the links of adjacent volumes
8174 UpdateVolumes(n12, n22, nodeList);
8176 // 3. find an element appeared on n1 and n2 after the insertion
8177 insertMap.erase( elem );
8178 elem = findAdjacentFace( n1, n2, 0 );
8180 if ( notFound || otherLink ) {
8181 // add element and nodes of the side into the insertMap
8182 insertMapIt = insertMap.insert
8183 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8184 (*insertMapIt).second.push_back( n1 );
8185 (*insertMapIt).second.push_back( n2 );
8187 // add node to be inserted into elem
8188 (*insertMapIt).second.push_back( nIns );
8189 next[ 1 - intoBord ] = true;
8192 // go to the next segment
8193 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8194 if ( next[ iBord ] ) {
8195 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8197 nPrev[ iBord ] = *nIt[ iBord ];
8198 nIt[ iBord ]++; i[ iBord ]++;
8202 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8204 // perform insertion of nodes into elements
8206 for (insertMapIt = insertMap.begin();
8207 insertMapIt != insertMap.end();
8210 const SMDS_MeshElement* elem = (*insertMapIt).first;
8211 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8212 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8213 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8215 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8217 if ( !theSideIsFreeBorder ) {
8218 // look for and insert nodes into the faces adjacent to elem
8220 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8222 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8227 if (toCreatePolyedrs) {
8228 // perform insertion into the links of adjacent volumes
8229 UpdateVolumes(n1, n2, nodeList);
8235 } // end: insert new nodes
8237 MergeNodes ( nodeGroupsToMerge );
8242 //=======================================================================
8243 //function : InsertNodesIntoLink
8244 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8245 // and theBetweenNode2 and split theElement
8246 //=======================================================================
8248 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8249 const SMDS_MeshNode* theBetweenNode1,
8250 const SMDS_MeshNode* theBetweenNode2,
8251 list<const SMDS_MeshNode*>& theNodesToInsert,
8252 const bool toCreatePoly)
8254 if ( theFace->GetType() != SMDSAbs_Face ) return;
8256 // find indices of 2 link nodes and of the rest nodes
8257 int iNode = 0, il1, il2, i3, i4;
8258 il1 = il2 = i3 = i4 = -1;
8259 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8260 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8262 if(theFace->IsQuadratic()) {
8263 const SMDS_VtkFace* F =
8264 dynamic_cast<const SMDS_VtkFace*>(theFace);
8265 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8266 // use special nodes iterator
8267 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8268 while( anIter->more() ) {
8269 const SMDS_MeshNode* n = cast2Node(anIter->next());
8270 if ( n == theBetweenNode1 )
8272 else if ( n == theBetweenNode2 )
8278 nodes[ iNode++ ] = n;
8282 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8283 while ( nodeIt->more() ) {
8284 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8285 if ( n == theBetweenNode1 )
8287 else if ( n == theBetweenNode2 )
8293 nodes[ iNode++ ] = n;
8296 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8299 // arrange link nodes to go one after another regarding the face orientation
8300 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8301 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8306 aNodesToInsert.reverse();
8308 // check that not link nodes of a quadrangles are in good order
8309 int nbFaceNodes = theFace->NbNodes();
8310 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8316 if (toCreatePoly || theFace->IsPoly()) {
8319 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8321 // add nodes of face up to first node of link
8324 if(theFace->IsQuadratic()) {
8325 const SMDS_VtkFace* F =
8326 dynamic_cast<const SMDS_VtkFace*>(theFace);
8327 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8328 // use special nodes iterator
8329 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8330 while( anIter->more() && !isFLN ) {
8331 const SMDS_MeshNode* n = cast2Node(anIter->next());
8332 poly_nodes[iNode++] = n;
8333 if (n == nodes[il1]) {
8337 // add nodes to insert
8338 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8339 for (; nIt != aNodesToInsert.end(); nIt++) {
8340 poly_nodes[iNode++] = *nIt;
8342 // add nodes of face starting from last node of link
8343 while ( anIter->more() ) {
8344 poly_nodes[iNode++] = cast2Node(anIter->next());
8348 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8349 while ( nodeIt->more() && !isFLN ) {
8350 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8351 poly_nodes[iNode++] = n;
8352 if (n == nodes[il1]) {
8356 // add nodes to insert
8357 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8358 for (; nIt != aNodesToInsert.end(); nIt++) {
8359 poly_nodes[iNode++] = *nIt;
8361 // add nodes of face starting from last node of link
8362 while ( nodeIt->more() ) {
8363 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8364 poly_nodes[iNode++] = n;
8368 // edit or replace the face
8369 SMESHDS_Mesh *aMesh = GetMeshDS();
8371 if (theFace->IsPoly()) {
8372 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8375 int aShapeId = FindShape( theFace );
8377 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8378 myLastCreatedElems.Append(newElem);
8379 if ( aShapeId && newElem )
8380 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8382 aMesh->RemoveElement(theFace);
8387 SMESHDS_Mesh *aMesh = GetMeshDS();
8388 if( !theFace->IsQuadratic() ) {
8390 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8391 int nbLinkNodes = 2 + aNodesToInsert.size();
8392 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8393 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8394 linkNodes[ 0 ] = nodes[ il1 ];
8395 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8396 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8397 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8398 linkNodes[ iNode++ ] = *nIt;
8400 // decide how to split a quadrangle: compare possible variants
8401 // and choose which of splits to be a quadrangle
8402 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8403 if ( nbFaceNodes == 3 ) {
8404 iBestQuad = nbSplits;
8407 else if ( nbFaceNodes == 4 ) {
8408 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8409 double aBestRate = DBL_MAX;
8410 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8412 double aBadRate = 0;
8413 // evaluate elements quality
8414 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8415 if ( iSplit == iQuad ) {
8416 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8420 aBadRate += getBadRate( &quad, aCrit );
8423 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8425 nodes[ iSplit < iQuad ? i4 : i3 ]);
8426 aBadRate += getBadRate( &tria, aCrit );
8430 if ( aBadRate < aBestRate ) {
8432 aBestRate = aBadRate;
8437 // create new elements
8438 int aShapeId = FindShape( theFace );
8441 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8442 SMDS_MeshElement* newElem = 0;
8443 if ( iSplit == iBestQuad )
8444 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8449 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8451 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8452 myLastCreatedElems.Append(newElem);
8453 if ( aShapeId && newElem )
8454 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8457 // change nodes of theFace
8458 const SMDS_MeshNode* newNodes[ 4 ];
8459 newNodes[ 0 ] = linkNodes[ i1 ];
8460 newNodes[ 1 ] = linkNodes[ i2 ];
8461 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8462 newNodes[ 3 ] = nodes[ i4 ];
8463 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8464 const SMDS_MeshElement* newElem = 0;
8465 if (iSplit == iBestQuad)
8466 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8468 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8469 myLastCreatedElems.Append(newElem);
8470 if ( aShapeId && newElem )
8471 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8472 } // end if(!theFace->IsQuadratic())
8473 else { // theFace is quadratic
8474 // we have to split theFace on simple triangles and one simple quadrangle
8476 int nbshift = tmp*2;
8477 // shift nodes in nodes[] by nbshift
8479 for(i=0; i<nbshift; i++) {
8480 const SMDS_MeshNode* n = nodes[0];
8481 for(j=0; j<nbFaceNodes-1; j++) {
8482 nodes[j] = nodes[j+1];
8484 nodes[nbFaceNodes-1] = n;
8486 il1 = il1 - nbshift;
8487 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8488 // n0 n1 n2 n0 n1 n2
8489 // +-----+-----+ +-----+-----+
8498 // create new elements
8499 int aShapeId = FindShape( theFace );
8502 if(nbFaceNodes==6) { // quadratic triangle
8503 SMDS_MeshElement* newElem =
8504 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8505 myLastCreatedElems.Append(newElem);
8506 if ( aShapeId && newElem )
8507 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8508 if(theFace->IsMediumNode(nodes[il1])) {
8509 // create quadrangle
8510 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8511 myLastCreatedElems.Append(newElem);
8512 if ( aShapeId && newElem )
8513 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8519 // create quadrangle
8520 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8521 myLastCreatedElems.Append(newElem);
8522 if ( aShapeId && newElem )
8523 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8529 else { // nbFaceNodes==8 - quadratic quadrangle
8530 SMDS_MeshElement* newElem =
8531 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8532 myLastCreatedElems.Append(newElem);
8533 if ( aShapeId && newElem )
8534 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8535 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8536 myLastCreatedElems.Append(newElem);
8537 if ( aShapeId && newElem )
8538 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8539 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8540 myLastCreatedElems.Append(newElem);
8541 if ( aShapeId && newElem )
8542 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8543 if(theFace->IsMediumNode(nodes[il1])) {
8544 // create quadrangle
8545 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8546 myLastCreatedElems.Append(newElem);
8547 if ( aShapeId && newElem )
8548 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8554 // create quadrangle
8555 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8556 myLastCreatedElems.Append(newElem);
8557 if ( aShapeId && newElem )
8558 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8564 // create needed triangles using n1,n2,n3 and inserted nodes
8565 int nbn = 2 + aNodesToInsert.size();
8566 //const SMDS_MeshNode* aNodes[nbn];
8567 vector<const SMDS_MeshNode*> aNodes(nbn);
8568 aNodes[0] = nodes[n1];
8569 aNodes[nbn-1] = nodes[n2];
8570 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8571 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8572 aNodes[iNode++] = *nIt;
8574 for(i=1; i<nbn; i++) {
8575 SMDS_MeshElement* newElem =
8576 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8577 myLastCreatedElems.Append(newElem);
8578 if ( aShapeId && newElem )
8579 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8583 aMesh->RemoveElement(theFace);
8586 //=======================================================================
8587 //function : UpdateVolumes
8589 //=======================================================================
8590 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8591 const SMDS_MeshNode* theBetweenNode2,
8592 list<const SMDS_MeshNode*>& theNodesToInsert)
8594 myLastCreatedElems.Clear();
8595 myLastCreatedNodes.Clear();
8597 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8598 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8599 const SMDS_MeshElement* elem = invElemIt->next();
8601 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8602 SMDS_VolumeTool aVolume (elem);
8603 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8606 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8607 int iface, nbFaces = aVolume.NbFaces();
8608 vector<const SMDS_MeshNode *> poly_nodes;
8609 vector<int> quantities (nbFaces);
8611 for (iface = 0; iface < nbFaces; iface++) {
8612 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8613 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8614 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8616 for (int inode = 0; inode < nbFaceNodes; inode++) {
8617 poly_nodes.push_back(faceNodes[inode]);
8619 if (nbInserted == 0) {
8620 if (faceNodes[inode] == theBetweenNode1) {
8621 if (faceNodes[inode + 1] == theBetweenNode2) {
8622 nbInserted = theNodesToInsert.size();
8624 // add nodes to insert
8625 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8626 for (; nIt != theNodesToInsert.end(); nIt++) {
8627 poly_nodes.push_back(*nIt);
8631 else if (faceNodes[inode] == theBetweenNode2) {
8632 if (faceNodes[inode + 1] == theBetweenNode1) {
8633 nbInserted = theNodesToInsert.size();
8635 // add nodes to insert in reversed order
8636 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8638 for (; nIt != theNodesToInsert.begin(); nIt--) {
8639 poly_nodes.push_back(*nIt);
8641 poly_nodes.push_back(*nIt);
8648 quantities[iface] = nbFaceNodes + nbInserted;
8651 // Replace or update the volume
8652 SMESHDS_Mesh *aMesh = GetMeshDS();
8654 if (elem->IsPoly()) {
8655 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8659 int aShapeId = FindShape( elem );
8661 SMDS_MeshElement* newElem =
8662 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8663 myLastCreatedElems.Append(newElem);
8664 if (aShapeId && newElem)
8665 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8667 aMesh->RemoveElement(elem);
8674 //================================================================================
8676 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8678 //================================================================================
8680 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8681 vector<const SMDS_MeshNode *> & nodes,
8682 vector<int> & nbNodeInFaces )
8685 nbNodeInFaces.clear();
8686 SMDS_VolumeTool vTool ( elem );
8687 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8689 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8690 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8691 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8696 //=======================================================================
8698 * \brief Convert elements contained in a submesh to quadratic
8699 * \return int - nb of checked elements
8701 //=======================================================================
8703 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8704 SMESH_MesherHelper& theHelper,
8705 const bool theForce3d)
8708 if( !theSm ) return nbElem;
8710 vector<int> nbNodeInFaces;
8711 vector<const SMDS_MeshNode *> nodes;
8712 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8713 while(ElemItr->more())
8716 const SMDS_MeshElement* elem = ElemItr->next();
8717 if( !elem ) continue;
8719 // analyse a necessity of conversion
8720 const SMDSAbs_ElementType aType = elem->GetType();
8721 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8723 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8724 bool hasCentralNodes = false;
8725 if ( elem->IsQuadratic() )
8728 switch ( aGeomType ) {
8729 case SMDSEntity_Quad_Triangle:
8730 case SMDSEntity_Quad_Quadrangle:
8731 case SMDSEntity_Quad_Hexa:
8732 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8734 case SMDSEntity_BiQuad_Triangle:
8735 case SMDSEntity_BiQuad_Quadrangle:
8736 case SMDSEntity_TriQuad_Hexa:
8737 alreadyOK = theHelper.GetIsBiQuadratic();
8738 hasCentralNodes = true;
8743 // take into account already present modium nodes
8745 case SMDSAbs_Volume:
8746 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8748 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8750 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8756 // get elem data needed to re-create it
8758 const int id = elem->GetID();
8759 const int nbNodes = elem->NbCornerNodes();
8760 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8761 if ( aGeomType == SMDSEntity_Polyhedra )
8762 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8763 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8764 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8766 // remove a linear element
8767 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8769 // remove central nodes of biquadratic elements (biquad->quad convertion)
8770 if ( hasCentralNodes )
8771 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8772 if ( nodes[i]->NbInverseElements() == 0 )
8773 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8775 const SMDS_MeshElement* NewElem = 0;
8781 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8789 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8792 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8795 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8799 case SMDSAbs_Volume :
8803 case SMDSEntity_Tetra:
8804 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8806 case SMDSEntity_Pyramid:
8807 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8809 case SMDSEntity_Penta:
8810 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8812 case SMDSEntity_Hexa:
8813 case SMDSEntity_Quad_Hexa:
8814 case SMDSEntity_TriQuad_Hexa:
8815 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8816 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8818 case SMDSEntity_Hexagonal_Prism:
8820 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8827 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8828 if( NewElem && NewElem->getshapeId() < 1 )
8829 theSm->AddElement( NewElem );
8833 //=======================================================================
8834 //function : ConvertToQuadratic
8836 //=======================================================================
8838 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8840 SMESHDS_Mesh* meshDS = GetMeshDS();
8842 SMESH_MesherHelper aHelper(*myMesh);
8844 aHelper.SetIsQuadratic( true );
8845 aHelper.SetIsBiQuadratic( theToBiQuad );
8846 aHelper.SetElementsOnShape(true);
8847 aHelper.ToFixNodeParameters( true );
8849 // convert elements assigned to sub-meshes
8850 int nbCheckedElems = 0;
8851 if ( myMesh->HasShapeToMesh() )
8853 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8855 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8856 while ( smIt->more() ) {
8857 SMESH_subMesh* sm = smIt->next();
8858 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8859 aHelper.SetSubShape( sm->GetSubShape() );
8860 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8866 // convert elements NOT assigned to sub-meshes
8867 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8868 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8870 aHelper.SetElementsOnShape(false);
8871 SMESHDS_SubMesh *smDS = 0;
8874 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8875 while( aEdgeItr->more() )
8877 const SMDS_MeshEdge* edge = aEdgeItr->next();
8878 if ( !edge->IsQuadratic() )
8880 int id = edge->GetID();
8881 const SMDS_MeshNode* n1 = edge->GetNode(0);
8882 const SMDS_MeshNode* n2 = edge->GetNode(1);
8884 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8886 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8887 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8891 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8896 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8897 while( aFaceItr->more() )
8899 const SMDS_MeshFace* face = aFaceItr->next();
8900 if ( !face ) continue;
8902 const SMDSAbs_EntityType type = face->GetEntityType();
8906 case SMDSEntity_Quad_Triangle:
8907 case SMDSEntity_Quad_Quadrangle:
8908 alreadyOK = !theToBiQuad;
8909 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8911 case SMDSEntity_BiQuad_Triangle:
8912 case SMDSEntity_BiQuad_Quadrangle:
8913 alreadyOK = theToBiQuad;
8914 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8916 default: alreadyOK = false;
8921 const int id = face->GetID();
8922 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8924 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8926 SMDS_MeshFace * NewFace = 0;
8929 case SMDSEntity_Triangle:
8930 case SMDSEntity_Quad_Triangle:
8931 case SMDSEntity_BiQuad_Triangle:
8932 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8933 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8934 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8937 case SMDSEntity_Quadrangle:
8938 case SMDSEntity_Quad_Quadrangle:
8939 case SMDSEntity_BiQuad_Quadrangle:
8940 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8941 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8942 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8946 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8948 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8952 vector<int> nbNodeInFaces;
8953 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8954 while(aVolumeItr->more())
8956 const SMDS_MeshVolume* volume = aVolumeItr->next();
8957 if ( !volume ) continue;
8959 const SMDSAbs_EntityType type = volume->GetEntityType();
8960 if ( volume->IsQuadratic() )
8965 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8966 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8967 default: alreadyOK = true;
8971 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8975 const int id = volume->GetID();
8976 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8977 if ( type == SMDSEntity_Polyhedra )
8978 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8979 else if ( type == SMDSEntity_Hexagonal_Prism )
8980 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8982 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8984 SMDS_MeshVolume * NewVolume = 0;
8987 case SMDSEntity_Tetra:
8988 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8990 case SMDSEntity_Hexa:
8991 case SMDSEntity_Quad_Hexa:
8992 case SMDSEntity_TriQuad_Hexa:
8993 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8994 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8995 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8996 if ( nodes[i]->NbInverseElements() == 0 )
8997 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8999 case SMDSEntity_Pyramid:
9000 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9001 nodes[3], nodes[4], id, theForce3d);
9003 case SMDSEntity_Penta:
9004 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9005 nodes[3], nodes[4], nodes[5], id, theForce3d);
9007 case SMDSEntity_Hexagonal_Prism:
9009 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9011 ReplaceElemInGroups(volume, NewVolume, meshDS);
9016 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9017 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9018 // aHelper.FixQuadraticElements(myError);
9019 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9023 //================================================================================
9025 * \brief Makes given elements quadratic
9026 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9027 * \param theElements - elements to make quadratic
9029 //================================================================================
9031 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9032 TIDSortedElemSet& theElements,
9033 const bool theToBiQuad)
9035 if ( theElements.empty() ) return;
9037 // we believe that all theElements are of the same type
9038 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9040 // get all nodes shared by theElements
9041 TIDSortedNodeSet allNodes;
9042 TIDSortedElemSet::iterator eIt = theElements.begin();
9043 for ( ; eIt != theElements.end(); ++eIt )
9044 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9046 // complete theElements with elements of lower dim whose all nodes are in allNodes
9048 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9049 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9050 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9051 for ( ; nIt != allNodes.end(); ++nIt )
9053 const SMDS_MeshNode* n = *nIt;
9054 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9055 while ( invIt->more() )
9057 const SMDS_MeshElement* e = invIt->next();
9058 const SMDSAbs_ElementType type = e->GetType();
9059 if ( e->IsQuadratic() )
9061 quadAdjacentElems[ type ].insert( e );
9064 switch ( e->GetEntityType() ) {
9065 case SMDSEntity_Quad_Triangle:
9066 case SMDSEntity_Quad_Quadrangle:
9067 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9068 case SMDSEntity_BiQuad_Triangle:
9069 case SMDSEntity_BiQuad_Quadrangle:
9070 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9071 default: alreadyOK = true;
9076 if ( type >= elemType )
9077 continue; // same type or more complex linear element
9079 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9080 continue; // e is already checked
9084 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9085 while ( nodeIt->more() && allIn )
9086 allIn = allNodes.count( nodeIt->next() );
9088 theElements.insert(e );
9092 SMESH_MesherHelper helper(*myMesh);
9093 helper.SetIsQuadratic( true );
9094 helper.SetIsBiQuadratic( theToBiQuad );
9096 // add links of quadratic adjacent elements to the helper
9098 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9099 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9100 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9102 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9104 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9105 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9106 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9108 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9110 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9111 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9112 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9114 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9117 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9119 SMESHDS_Mesh* meshDS = GetMeshDS();
9120 SMESHDS_SubMesh* smDS = 0;
9121 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9123 const SMDS_MeshElement* elem = *eIt;
9126 int nbCentralNodes = 0;
9127 switch ( elem->GetEntityType() ) {
9128 // linear convertible
9129 case SMDSEntity_Edge:
9130 case SMDSEntity_Triangle:
9131 case SMDSEntity_Quadrangle:
9132 case SMDSEntity_Tetra:
9133 case SMDSEntity_Pyramid:
9134 case SMDSEntity_Hexa:
9135 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9136 // quadratic that can become bi-quadratic
9137 case SMDSEntity_Quad_Triangle:
9138 case SMDSEntity_Quad_Quadrangle:
9139 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9141 case SMDSEntity_BiQuad_Triangle:
9142 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9143 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9145 default: alreadyOK = true;
9147 if ( alreadyOK ) continue;
9149 const SMDSAbs_ElementType type = elem->GetType();
9150 const int id = elem->GetID();
9151 const int nbNodes = elem->NbCornerNodes();
9152 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9154 helper.SetSubShape( elem->getshapeId() );
9156 if ( !smDS || !smDS->Contains( elem ))
9157 smDS = meshDS->MeshElements( elem->getshapeId() );
9158 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9160 SMDS_MeshElement * newElem = 0;
9163 case 4: // cases for most frequently used element types go first (for optimization)
9164 if ( type == SMDSAbs_Volume )
9165 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9167 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9170 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9171 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9174 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9177 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9180 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9181 nodes[4], id, theForce3d);
9184 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9185 nodes[4], nodes[5], id, theForce3d);
9189 ReplaceElemInGroups( elem, newElem, meshDS);
9190 if( newElem && smDS )
9191 smDS->AddElement( newElem );
9193 // remove central nodes
9194 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9195 if ( nodes[i]->NbInverseElements() == 0 )
9196 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9198 } // loop on theElements
9201 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9202 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9203 // helper.FixQuadraticElements( myError );
9204 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9208 //=======================================================================
9210 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9211 * \return int - nb of checked elements
9213 //=======================================================================
9215 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9216 SMDS_ElemIteratorPtr theItr,
9217 const int theShapeID)
9220 SMESHDS_Mesh* meshDS = GetMeshDS();
9222 while( theItr->more() )
9224 const SMDS_MeshElement* elem = theItr->next();
9226 if( elem && elem->IsQuadratic())
9228 int id = elem->GetID();
9229 int nbCornerNodes = elem->NbCornerNodes();
9230 SMDSAbs_ElementType aType = elem->GetType();
9232 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9234 //remove a quadratic element
9235 if ( !theSm || !theSm->Contains( elem ))
9236 theSm = meshDS->MeshElements( elem->getshapeId() );
9237 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9239 // remove medium nodes
9240 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9241 if ( nodes[i]->NbInverseElements() == 0 )
9242 meshDS->RemoveFreeNode( nodes[i], theSm );
9244 // add a linear element
9245 nodes.resize( nbCornerNodes );
9246 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9247 ReplaceElemInGroups(elem, newElem, meshDS);
9248 if( theSm && newElem )
9249 theSm->AddElement( newElem );
9255 //=======================================================================
9256 //function : ConvertFromQuadratic
9258 //=======================================================================
9260 bool SMESH_MeshEditor::ConvertFromQuadratic()
9262 int nbCheckedElems = 0;
9263 if ( myMesh->HasShapeToMesh() )
9265 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9267 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9268 while ( smIt->more() ) {
9269 SMESH_subMesh* sm = smIt->next();
9270 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9271 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9277 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9278 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9280 SMESHDS_SubMesh *aSM = 0;
9281 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9289 //================================================================================
9291 * \brief Return true if all medium nodes of the element are in the node set
9293 //================================================================================
9295 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9297 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9298 if ( !nodeSet.count( elem->GetNode(i) ))
9304 //================================================================================
9306 * \brief Makes given elements linear
9308 //================================================================================
9310 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9312 if ( theElements.empty() ) return;
9314 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9315 set<int> mediumNodeIDs;
9316 TIDSortedElemSet::iterator eIt = theElements.begin();
9317 for ( ; eIt != theElements.end(); ++eIt )
9319 const SMDS_MeshElement* e = *eIt;
9320 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9321 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9324 // replace given elements by linear ones
9325 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9326 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9328 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9329 // except those elements sharing medium nodes of quadratic element whose medium nodes
9330 // are not all in mediumNodeIDs
9332 // get remaining medium nodes
9333 TIDSortedNodeSet mediumNodes;
9334 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9335 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9336 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9337 mediumNodes.insert( mediumNodes.end(), n );
9339 // find more quadratic elements to convert
9340 TIDSortedElemSet moreElemsToConvert;
9341 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9342 for ( ; nIt != mediumNodes.end(); ++nIt )
9344 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9345 while ( invIt->more() )
9347 const SMDS_MeshElement* e = invIt->next();
9348 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9350 // find a more complex element including e and
9351 // whose medium nodes are not in mediumNodes
9352 bool complexFound = false;
9353 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9355 SMDS_ElemIteratorPtr invIt2 =
9356 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9357 while ( invIt2->more() )
9359 const SMDS_MeshElement* eComplex = invIt2->next();
9360 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9362 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9363 if ( nbCommonNodes == e->NbNodes())
9365 complexFound = true;
9366 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9372 if ( !complexFound )
9373 moreElemsToConvert.insert( e );
9377 elemIt = elemSetIterator( moreElemsToConvert );
9378 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9381 //=======================================================================
9382 //function : SewSideElements
9384 //=======================================================================
9386 SMESH_MeshEditor::Sew_Error
9387 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9388 TIDSortedElemSet& theSide2,
9389 const SMDS_MeshNode* theFirstNode1,
9390 const SMDS_MeshNode* theFirstNode2,
9391 const SMDS_MeshNode* theSecondNode1,
9392 const SMDS_MeshNode* theSecondNode2)
9394 myLastCreatedElems.Clear();
9395 myLastCreatedNodes.Clear();
9397 MESSAGE ("::::SewSideElements()");
9398 if ( theSide1.size() != theSide2.size() )
9399 return SEW_DIFF_NB_OF_ELEMENTS;
9401 Sew_Error aResult = SEW_OK;
9403 // 1. Build set of faces representing each side
9404 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9405 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9407 // =======================================================================
9408 // 1. Build set of faces representing each side:
9409 // =======================================================================
9410 // a. build set of nodes belonging to faces
9411 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9412 // c. create temporary faces representing side of volumes if correspondent
9413 // face does not exist
9415 SMESHDS_Mesh* aMesh = GetMeshDS();
9416 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9417 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9418 TIDSortedElemSet faceSet1, faceSet2;
9419 set<const SMDS_MeshElement*> volSet1, volSet2;
9420 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9421 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9422 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9423 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9424 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9425 int iSide, iFace, iNode;
9427 list<const SMDS_MeshElement* > tempFaceList;
9428 for ( iSide = 0; iSide < 2; iSide++ ) {
9429 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9430 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9431 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9432 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9433 set<const SMDS_MeshElement*>::iterator vIt;
9434 TIDSortedElemSet::iterator eIt;
9435 set<const SMDS_MeshNode*>::iterator nIt;
9437 // check that given nodes belong to given elements
9438 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9439 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9440 int firstIndex = -1, secondIndex = -1;
9441 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9442 const SMDS_MeshElement* elem = *eIt;
9443 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9444 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9445 if ( firstIndex > -1 && secondIndex > -1 ) break;
9447 if ( firstIndex < 0 || secondIndex < 0 ) {
9448 // we can simply return until temporary faces created
9449 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9452 // -----------------------------------------------------------
9453 // 1a. Collect nodes of existing faces
9454 // and build set of face nodes in order to detect missing
9455 // faces corresponding to sides of volumes
9456 // -----------------------------------------------------------
9458 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9460 // loop on the given element of a side
9461 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9462 //const SMDS_MeshElement* elem = *eIt;
9463 const SMDS_MeshElement* elem = *eIt;
9464 if ( elem->GetType() == SMDSAbs_Face ) {
9465 faceSet->insert( elem );
9466 set <const SMDS_MeshNode*> faceNodeSet;
9467 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9468 while ( nodeIt->more() ) {
9469 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9470 nodeSet->insert( n );
9471 faceNodeSet.insert( n );
9473 setOfFaceNodeSet.insert( faceNodeSet );
9475 else if ( elem->GetType() == SMDSAbs_Volume )
9476 volSet->insert( elem );
9478 // ------------------------------------------------------------------------------
9479 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9480 // ------------------------------------------------------------------------------
9482 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9483 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9484 while ( fIt->more() ) { // loop on faces sharing a node
9485 const SMDS_MeshElement* f = fIt->next();
9486 if ( faceSet->find( f ) == faceSet->end() ) {
9487 // check if all nodes are in nodeSet and
9488 // complete setOfFaceNodeSet if they are
9489 set <const SMDS_MeshNode*> faceNodeSet;
9490 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9491 bool allInSet = true;
9492 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9493 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9494 if ( nodeSet->find( n ) == nodeSet->end() )
9497 faceNodeSet.insert( n );
9500 faceSet->insert( f );
9501 setOfFaceNodeSet.insert( faceNodeSet );
9507 // -------------------------------------------------------------------------
9508 // 1c. Create temporary faces representing sides of volumes if correspondent
9509 // face does not exist
9510 // -------------------------------------------------------------------------
9512 if ( !volSet->empty() ) {
9513 //int nodeSetSize = nodeSet->size();
9515 // loop on given volumes
9516 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9517 SMDS_VolumeTool vol (*vIt);
9518 // loop on volume faces: find free faces
9519 // --------------------------------------
9520 list<const SMDS_MeshElement* > freeFaceList;
9521 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9522 if ( !vol.IsFreeFace( iFace ))
9524 // check if there is already a face with same nodes in a face set
9525 const SMDS_MeshElement* aFreeFace = 0;
9526 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9527 int nbNodes = vol.NbFaceNodes( iFace );
9528 set <const SMDS_MeshNode*> faceNodeSet;
9529 vol.GetFaceNodes( iFace, faceNodeSet );
9530 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9532 // no such a face is given but it still can exist, check it
9533 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9534 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9537 // create a temporary face
9538 if ( nbNodes == 3 ) {
9539 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9540 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9542 else if ( nbNodes == 4 ) {
9543 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9544 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9547 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9548 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9549 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9552 tempFaceList.push_back( aFreeFace );
9556 freeFaceList.push_back( aFreeFace );
9558 } // loop on faces of a volume
9560 // choose one of several free faces of a volume
9561 // --------------------------------------------
9562 if ( freeFaceList.size() > 1 ) {
9563 // choose a face having max nb of nodes shared by other elems of a side
9564 int maxNbNodes = -1;
9565 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9566 while ( fIt != freeFaceList.end() ) { // loop on free faces
9567 int nbSharedNodes = 0;
9568 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9569 while ( nodeIt->more() ) { // loop on free face nodes
9570 const SMDS_MeshNode* n =
9571 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9572 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9573 while ( invElemIt->more() ) {
9574 const SMDS_MeshElement* e = invElemIt->next();
9575 nbSharedNodes += faceSet->count( e );
9576 nbSharedNodes += elemSet->count( e );
9579 if ( nbSharedNodes > maxNbNodes ) {
9580 maxNbNodes = nbSharedNodes;
9581 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9583 else if ( nbSharedNodes == maxNbNodes ) {
9587 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9590 if ( freeFaceList.size() > 1 )
9592 // could not choose one face, use another way
9593 // choose a face most close to the bary center of the opposite side
9594 gp_XYZ aBC( 0., 0., 0. );
9595 set <const SMDS_MeshNode*> addedNodes;
9596 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9597 eIt = elemSet2->begin();
9598 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9599 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9600 while ( nodeIt->more() ) { // loop on free face nodes
9601 const SMDS_MeshNode* n =
9602 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9603 if ( addedNodes.insert( n ).second )
9604 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9607 aBC /= addedNodes.size();
9608 double minDist = DBL_MAX;
9609 fIt = freeFaceList.begin();
9610 while ( fIt != freeFaceList.end() ) { // loop on free faces
9612 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9613 while ( nodeIt->more() ) { // loop on free face nodes
9614 const SMDS_MeshNode* n =
9615 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9616 gp_XYZ p( n->X(),n->Y(),n->Z() );
9617 dist += ( aBC - p ).SquareModulus();
9619 if ( dist < minDist ) {
9621 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9624 fIt = freeFaceList.erase( fIt++ );
9627 } // choose one of several free faces of a volume
9629 if ( freeFaceList.size() == 1 ) {
9630 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9631 faceSet->insert( aFreeFace );
9632 // complete a node set with nodes of a found free face
9633 // for ( iNode = 0; iNode < ; iNode++ )
9634 // nodeSet->insert( fNodes[ iNode ] );
9637 } // loop on volumes of a side
9639 // // complete a set of faces if new nodes in a nodeSet appeared
9640 // // ----------------------------------------------------------
9641 // if ( nodeSetSize != nodeSet->size() ) {
9642 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9643 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9644 // while ( fIt->more() ) { // loop on faces sharing a node
9645 // const SMDS_MeshElement* f = fIt->next();
9646 // if ( faceSet->find( f ) == faceSet->end() ) {
9647 // // check if all nodes are in nodeSet and
9648 // // complete setOfFaceNodeSet if they are
9649 // set <const SMDS_MeshNode*> faceNodeSet;
9650 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9651 // bool allInSet = true;
9652 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9653 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9654 // if ( nodeSet->find( n ) == nodeSet->end() )
9655 // allInSet = false;
9657 // faceNodeSet.insert( n );
9659 // if ( allInSet ) {
9660 // faceSet->insert( f );
9661 // setOfFaceNodeSet.insert( faceNodeSet );
9667 } // Create temporary faces, if there are volumes given
9670 if ( faceSet1.size() != faceSet2.size() ) {
9671 // delete temporary faces: they are in reverseElements of actual nodes
9672 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9673 // while ( tmpFaceIt->more() )
9674 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9675 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9676 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9677 // aMesh->RemoveElement(*tmpFaceIt);
9678 MESSAGE("Diff nb of faces");
9679 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9682 // ============================================================
9683 // 2. Find nodes to merge:
9684 // bind a node to remove to a node to put instead
9685 // ============================================================
9687 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9688 if ( theFirstNode1 != theFirstNode2 )
9689 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9690 if ( theSecondNode1 != theSecondNode2 )
9691 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9693 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9694 set< long > linkIdSet; // links to process
9695 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9697 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9698 list< NLink > linkList[2];
9699 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9700 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9701 // loop on links in linkList; find faces by links and append links
9702 // of the found faces to linkList
9703 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9704 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9706 NLink link[] = { *linkIt[0], *linkIt[1] };
9707 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9708 if ( !linkIdSet.count( linkID ) )
9711 // by links, find faces in the face sets,
9712 // and find indices of link nodes in the found faces;
9713 // in a face set, there is only one or no face sharing a link
9714 // ---------------------------------------------------------------
9716 const SMDS_MeshElement* face[] = { 0, 0 };
9717 vector<const SMDS_MeshNode*> fnodes[2];
9718 int iLinkNode[2][2];
9719 TIDSortedElemSet avoidSet;
9720 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9721 const SMDS_MeshNode* n1 = link[iSide].first;
9722 const SMDS_MeshNode* n2 = link[iSide].second;
9723 //cout << "Side " << iSide << " ";
9724 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9725 // find a face by two link nodes
9726 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9727 *faceSetPtr[ iSide ], avoidSet,
9728 &iLinkNode[iSide][0],
9729 &iLinkNode[iSide][1] );
9732 //cout << " F " << face[ iSide]->GetID() <<endl;
9733 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9734 // put face nodes to fnodes
9735 if ( face[ iSide ]->IsQuadratic() )
9737 // use interlaced nodes iterator
9738 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9739 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9740 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9741 while ( nIter->more() )
9742 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9746 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9747 face[ iSide ]->end_nodes() );
9749 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9753 // check similarity of elements of the sides
9754 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9755 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9756 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9757 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9760 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9762 break; // do not return because it's necessary to remove tmp faces
9765 // set nodes to merge
9766 // -------------------
9768 if ( face[0] && face[1] ) {
9769 const int nbNodes = face[0]->NbNodes();
9770 if ( nbNodes != face[1]->NbNodes() ) {
9771 MESSAGE("Diff nb of face nodes");
9772 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9773 break; // do not return because it s necessary to remove tmp faces
9775 bool reverse[] = { false, false }; // order of nodes in the link
9776 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9777 // analyse link orientation in faces
9778 int i1 = iLinkNode[ iSide ][ 0 ];
9779 int i2 = iLinkNode[ iSide ][ 1 ];
9780 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9782 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9783 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9784 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9786 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9787 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9790 // add other links of the faces to linkList
9791 // -----------------------------------------
9793 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9794 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9795 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9796 if ( !iter_isnew.second ) { // already in a set: no need to process
9797 linkIdSet.erase( iter_isnew.first );
9799 else // new in set == encountered for the first time: add
9801 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9802 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9803 linkList[0].push_back ( NLink( n1, n2 ));
9804 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9809 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9812 } // loop on link lists
9814 if ( aResult == SEW_OK &&
9815 ( //linkIt[0] != linkList[0].end() ||
9816 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9817 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9818 " " << (faceSetPtr[1]->empty()));
9819 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9822 // ====================================================================
9823 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9824 // ====================================================================
9826 // delete temporary faces
9827 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9828 // while ( tmpFaceIt->more() )
9829 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9830 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9831 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9832 aMesh->RemoveElement(*tmpFaceIt);
9834 if ( aResult != SEW_OK)
9837 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9838 // loop on nodes replacement map
9839 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9840 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9841 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9842 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9843 nodeIDsToRemove.push_back( nToRemove->GetID() );
9844 // loop on elements sharing nToRemove
9845 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9846 while ( invElemIt->more() ) {
9847 const SMDS_MeshElement* e = invElemIt->next();
9848 // get a new suite of nodes: make replacement
9849 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9850 vector< const SMDS_MeshNode*> nodes( nbNodes );
9851 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9852 while ( nIt->more() ) {
9853 const SMDS_MeshNode* n =
9854 static_cast<const SMDS_MeshNode*>( nIt->next() );
9855 nnIt = nReplaceMap.find( n );
9856 if ( nnIt != nReplaceMap.end() ) {
9862 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9863 // elemIDsToRemove.push_back( e->GetID() );
9867 SMDSAbs_ElementType etyp = e->GetType();
9868 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9871 myLastCreatedElems.Append(newElem);
9872 AddToSameGroups(newElem, e, aMesh);
9873 int aShapeId = e->getshapeId();
9876 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9879 aMesh->RemoveElement(e);
9884 Remove( nodeIDsToRemove, true );
9889 //================================================================================
9891 * \brief Find corresponding nodes in two sets of faces
9892 * \param theSide1 - first face set
9893 * \param theSide2 - second first face
9894 * \param theFirstNode1 - a boundary node of set 1
9895 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9896 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9897 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9898 * \param nReplaceMap - output map of corresponding nodes
9899 * \return bool - is a success or not
9901 //================================================================================
9904 //#define DEBUG_MATCHING_NODES
9907 SMESH_MeshEditor::Sew_Error
9908 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9909 set<const SMDS_MeshElement*>& theSide2,
9910 const SMDS_MeshNode* theFirstNode1,
9911 const SMDS_MeshNode* theFirstNode2,
9912 const SMDS_MeshNode* theSecondNode1,
9913 const SMDS_MeshNode* theSecondNode2,
9914 TNodeNodeMap & nReplaceMap)
9916 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9918 nReplaceMap.clear();
9919 if ( theFirstNode1 != theFirstNode2 )
9920 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9921 if ( theSecondNode1 != theSecondNode2 )
9922 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9924 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9925 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9927 list< NLink > linkList[2];
9928 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9929 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9931 // loop on links in linkList; find faces by links and append links
9932 // of the found faces to linkList
9933 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9934 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9935 NLink link[] = { *linkIt[0], *linkIt[1] };
9936 if ( linkSet.find( link[0] ) == linkSet.end() )
9939 // by links, find faces in the face sets,
9940 // and find indices of link nodes in the found faces;
9941 // in a face set, there is only one or no face sharing a link
9942 // ---------------------------------------------------------------
9944 const SMDS_MeshElement* face[] = { 0, 0 };
9945 list<const SMDS_MeshNode*> notLinkNodes[2];
9946 //bool reverse[] = { false, false }; // order of notLinkNodes
9948 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9950 const SMDS_MeshNode* n1 = link[iSide].first;
9951 const SMDS_MeshNode* n2 = link[iSide].second;
9952 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9953 set< const SMDS_MeshElement* > facesOfNode1;
9954 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9956 // during a loop of the first node, we find all faces around n1,
9957 // during a loop of the second node, we find one face sharing both n1 and n2
9958 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9959 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9960 while ( fIt->more() ) { // loop on faces sharing a node
9961 const SMDS_MeshElement* f = fIt->next();
9962 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9963 ! facesOfNode1.insert( f ).second ) // f encounters twice
9965 if ( face[ iSide ] ) {
9966 MESSAGE( "2 faces per link " );
9967 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9970 faceSet->erase( f );
9972 // get not link nodes
9973 int nbN = f->NbNodes();
9974 if ( f->IsQuadratic() )
9976 nbNodes[ iSide ] = nbN;
9977 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9978 int i1 = f->GetNodeIndex( n1 );
9979 int i2 = f->GetNodeIndex( n2 );
9980 int iEnd = nbN, iBeg = -1, iDelta = 1;
9981 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9983 std::swap( iEnd, iBeg ); iDelta = -1;
9988 if ( i == iEnd ) i = iBeg + iDelta;
9989 if ( i == i1 ) break;
9990 nodes.push_back ( f->GetNode( i ) );
9996 // check similarity of elements of the sides
9997 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9998 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9999 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10000 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10003 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10007 // set nodes to merge
10008 // -------------------
10010 if ( face[0] && face[1] ) {
10011 if ( nbNodes[0] != nbNodes[1] ) {
10012 MESSAGE("Diff nb of face nodes");
10013 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10015 #ifdef DEBUG_MATCHING_NODES
10016 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10017 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10018 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10020 int nbN = nbNodes[0];
10022 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10023 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10024 for ( int i = 0 ; i < nbN - 2; ++i ) {
10025 #ifdef DEBUG_MATCHING_NODES
10026 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10028 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10032 // add other links of the face 1 to linkList
10033 // -----------------------------------------
10035 const SMDS_MeshElement* f0 = face[0];
10036 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10037 for ( int i = 0; i < nbN; i++ )
10039 const SMDS_MeshNode* n2 = f0->GetNode( i );
10040 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10041 linkSet.insert( SMESH_TLink( n1, n2 ));
10042 if ( !iter_isnew.second ) { // already in a set: no need to process
10043 linkSet.erase( iter_isnew.first );
10045 else // new in set == encountered for the first time: add
10047 #ifdef DEBUG_MATCHING_NODES
10048 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10049 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10051 linkList[0].push_back ( NLink( n1, n2 ));
10052 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10057 } // loop on link lists
10062 //================================================================================
10064 * \brief Create elements equal (on same nodes) to given ones
10065 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10066 * elements of the uppest dimension are duplicated.
10068 //================================================================================
10070 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10072 CrearLastCreated();
10073 SMESHDS_Mesh* mesh = GetMeshDS();
10075 // get an element type and an iterator over elements
10077 SMDSAbs_ElementType type;
10078 SMDS_ElemIteratorPtr elemIt;
10079 vector< const SMDS_MeshElement* > allElems;
10080 if ( theElements.empty() )
10082 if ( mesh->NbNodes() == 0 )
10084 // get most complex type
10085 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10086 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10087 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10089 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10090 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10095 // put all elements in the vector <allElems>
10096 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10097 elemIt = mesh->elementsIterator( type );
10098 while ( elemIt->more() )
10099 allElems.push_back( elemIt->next());
10100 elemIt = elemSetIterator( allElems );
10104 type = (*theElements.begin())->GetType();
10105 elemIt = elemSetIterator( theElements );
10108 // duplicate elements
10110 if ( type == SMDSAbs_Ball )
10112 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10113 while ( elemIt->more() )
10115 const SMDS_MeshElement* elem = elemIt->next();
10116 if ( elem->GetType() != SMDSAbs_Ball )
10118 if (( elem = mesh->AddBall( elem->GetNode(0),
10119 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10120 myLastCreatedElems.Append( elem );
10125 vector< const SMDS_MeshNode* > nodes;
10126 while ( elemIt->more() )
10128 const SMDS_MeshElement* elem = elemIt->next();
10129 if ( elem->GetType() != type )
10132 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10134 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10136 std::vector<int> quantities =
10137 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10138 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10142 AddElement( nodes, type, elem->IsPoly() );
10143 elem = 0; // myLastCreatedElems is already filled
10146 myLastCreatedElems.Append( elem );
10151 //================================================================================
10153 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10154 \param theElems - the list of elements (edges or faces) to be replicated
10155 The nodes for duplication could be found from these elements
10156 \param theNodesNot - list of nodes to NOT replicate
10157 \param theAffectedElems - the list of elements (cells and edges) to which the
10158 replicated nodes should be associated to.
10159 \return TRUE if operation has been completed successfully, FALSE otherwise
10161 //================================================================================
10163 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10164 const TIDSortedElemSet& theNodesNot,
10165 const TIDSortedElemSet& theAffectedElems )
10167 myLastCreatedElems.Clear();
10168 myLastCreatedNodes.Clear();
10170 if ( theElems.size() == 0 )
10173 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10178 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10179 // duplicate elements and nodes
10180 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10181 // replce nodes by duplications
10182 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10186 //================================================================================
10188 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10189 \param theMeshDS - mesh instance
10190 \param theElems - the elements replicated or modified (nodes should be changed)
10191 \param theNodesNot - nodes to NOT replicate
10192 \param theNodeNodeMap - relation of old node to new created node
10193 \param theIsDoubleElem - flag os to replicate element or modify
10194 \return TRUE if operation has been completed successfully, FALSE otherwise
10196 //================================================================================
10198 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10199 const TIDSortedElemSet& theElems,
10200 const TIDSortedElemSet& theNodesNot,
10201 std::map< const SMDS_MeshNode*,
10202 const SMDS_MeshNode* >& theNodeNodeMap,
10203 const bool theIsDoubleElem )
10205 MESSAGE("doubleNodes");
10206 // iterate on through element and duplicate them (by nodes duplication)
10208 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10209 for ( ; elemItr != theElems.end(); ++elemItr )
10211 const SMDS_MeshElement* anElem = *elemItr;
10215 bool isDuplicate = false;
10216 // duplicate nodes to duplicate element
10217 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10218 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10220 while ( anIter->more() )
10223 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10224 SMDS_MeshNode* aNewNode = aCurrNode;
10225 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10226 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10227 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10230 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10231 theNodeNodeMap[ aCurrNode ] = aNewNode;
10232 myLastCreatedNodes.Append( aNewNode );
10234 isDuplicate |= (aCurrNode != aNewNode);
10235 newNodes[ ind++ ] = aNewNode;
10237 if ( !isDuplicate )
10240 if ( theIsDoubleElem )
10241 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10244 MESSAGE("ChangeElementNodes");
10245 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10252 //================================================================================
10254 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10255 \param theNodes - identifiers of nodes to be doubled
10256 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10257 nodes. If list of element identifiers is empty then nodes are doubled but
10258 they not assigned to elements
10259 \return TRUE if operation has been completed successfully, FALSE otherwise
10261 //================================================================================
10263 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10264 const std::list< int >& theListOfModifiedElems )
10266 MESSAGE("DoubleNodes");
10267 myLastCreatedElems.Clear();
10268 myLastCreatedNodes.Clear();
10270 if ( theListOfNodes.size() == 0 )
10273 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10277 // iterate through nodes and duplicate them
10279 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10281 std::list< int >::const_iterator aNodeIter;
10282 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10284 int aCurr = *aNodeIter;
10285 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10291 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10294 anOldNodeToNewNode[ aNode ] = aNewNode;
10295 myLastCreatedNodes.Append( aNewNode );
10299 // Create map of new nodes for modified elements
10301 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10303 std::list< int >::const_iterator anElemIter;
10304 for ( anElemIter = theListOfModifiedElems.begin();
10305 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10307 int aCurr = *anElemIter;
10308 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10312 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10314 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10316 while ( anIter->more() )
10318 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10319 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10321 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10322 aNodeArr[ ind++ ] = aNewNode;
10325 aNodeArr[ ind++ ] = aCurrNode;
10327 anElemToNodes[ anElem ] = aNodeArr;
10330 // Change nodes of elements
10332 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10333 anElemToNodesIter = anElemToNodes.begin();
10334 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10336 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10337 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10340 MESSAGE("ChangeElementNodes");
10341 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10350 //================================================================================
10352 \brief Check if element located inside shape
10353 \return TRUE if IN or ON shape, FALSE otherwise
10355 //================================================================================
10357 template<class Classifier>
10358 bool isInside(const SMDS_MeshElement* theElem,
10359 Classifier& theClassifier,
10360 const double theTol)
10362 gp_XYZ centerXYZ (0, 0, 0);
10363 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10364 while (aNodeItr->more())
10365 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10367 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10368 theClassifier.Perform(aPnt, theTol);
10369 TopAbs_State aState = theClassifier.State();
10370 return (aState == TopAbs_IN || aState == TopAbs_ON );
10373 //================================================================================
10375 * \brief Classifier of the 3D point on the TopoDS_Face
10376 * with interaface suitable for isInside()
10378 //================================================================================
10380 struct _FaceClassifier
10382 Extrema_ExtPS _extremum;
10383 BRepAdaptor_Surface _surface;
10384 TopAbs_State _state;
10386 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10388 _extremum.Initialize( _surface,
10389 _surface.FirstUParameter(), _surface.LastUParameter(),
10390 _surface.FirstVParameter(), _surface.LastVParameter(),
10391 _surface.Tolerance(), _surface.Tolerance() );
10393 void Perform(const gp_Pnt& aPnt, double theTol)
10396 _state = TopAbs_OUT;
10397 _extremum.Perform(aPnt);
10398 if ( _extremum.IsDone() )
10399 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10400 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10402 TopAbs_State State() const
10409 //================================================================================
10411 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10412 This method is the first step of DoubleNodeElemGroupsInRegion.
10413 \param theElems - list of groups of elements (edges or faces) to be replicated
10414 \param theNodesNot - list of groups of nodes not to replicated
10415 \param theShape - shape to detect affected elements (element which geometric center
10416 located on or inside shape). If the shape is null, detection is done on faces orientations
10417 (select elements with a gravity center on the side given by faces normals).
10418 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10419 The replicated nodes should be associated to affected elements.
10420 \return groups of affected elements
10421 \sa DoubleNodeElemGroupsInRegion()
10423 //================================================================================
10425 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10426 const TIDSortedElemSet& theNodesNot,
10427 const TopoDS_Shape& theShape,
10428 TIDSortedElemSet& theAffectedElems)
10430 if ( theShape.IsNull() )
10432 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10433 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10434 std::set<const SMDS_MeshElement*> edgesToCheck;
10435 alreadyCheckedNodes.clear();
10436 alreadyCheckedElems.clear();
10437 edgesToCheck.clear();
10439 // --- iterates on elements to be replicated and get elements by back references from their nodes
10441 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10443 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10445 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10446 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10449 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10450 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10451 std::set<const SMDS_MeshNode*> nodesElem;
10453 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10454 while ( nodeItr->more() )
10456 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10457 nodesElem.insert(aNode);
10459 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10460 for (; nodit != nodesElem.end(); nodit++)
10462 MESSAGE(" noeud ");
10463 const SMDS_MeshNode* aNode = *nodit;
10464 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10466 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10468 alreadyCheckedNodes.insert(aNode);
10469 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10470 while ( backElemItr->more() )
10472 MESSAGE(" backelem ");
10473 const SMDS_MeshElement* curElem = backElemItr->next();
10474 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10476 if (theElems.find(curElem) != theElems.end())
10478 alreadyCheckedElems.insert(curElem);
10479 double x=0, y=0, z=0;
10481 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10482 while ( nodeItr2->more() )
10484 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10485 x += anotherNode->X();
10486 y += anotherNode->Y();
10487 z += anotherNode->Z();
10491 p.SetCoord( x/nb -aNode->X(),
10493 z/nb -aNode->Z() );
10494 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10497 MESSAGE(" --- inserted")
10498 theAffectedElems.insert( curElem );
10500 else if (curElem->GetType() == SMDSAbs_Edge)
10501 edgesToCheck.insert(curElem);
10505 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10506 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10507 for( ; eit != edgesToCheck.end(); eit++)
10509 bool onside = true;
10510 const SMDS_MeshElement* anEdge = *eit;
10511 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10512 while ( nodeItr->more() )
10514 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10515 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10523 MESSAGE(" --- edge onside inserted")
10524 theAffectedElems.insert(anEdge);
10530 const double aTol = Precision::Confusion();
10531 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10532 auto_ptr<_FaceClassifier> aFaceClassifier;
10533 if ( theShape.ShapeType() == TopAbs_SOLID )
10535 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10536 bsc3d->PerformInfinitePoint(aTol);
10538 else if (theShape.ShapeType() == TopAbs_FACE )
10540 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10543 // iterates on indicated elements and get elements by back references from their nodes
10544 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10546 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10548 MESSAGE("element " << ielem++);
10549 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10552 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10553 while ( nodeItr->more() )
10555 MESSAGE(" noeud ");
10556 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10557 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10559 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10560 while ( backElemItr->more() )
10562 MESSAGE(" backelem ");
10563 const SMDS_MeshElement* curElem = backElemItr->next();
10564 if ( curElem && theElems.find(curElem) == theElems.end() &&
10566 isInside( curElem, *bsc3d, aTol ) :
10567 isInside( curElem, *aFaceClassifier, aTol )))
10568 theAffectedElems.insert( curElem );
10576 //================================================================================
10578 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10579 \param theElems - group of of elements (edges or faces) to be replicated
10580 \param theNodesNot - group of nodes not to replicate
10581 \param theShape - shape to detect affected elements (element which geometric center
10582 located on or inside shape).
10583 The replicated nodes should be associated to affected elements.
10584 \return TRUE if operation has been completed successfully, FALSE otherwise
10586 //================================================================================
10588 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10589 const TIDSortedElemSet& theNodesNot,
10590 const TopoDS_Shape& theShape )
10592 if ( theShape.IsNull() )
10595 const double aTol = Precision::Confusion();
10596 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10597 auto_ptr<_FaceClassifier> aFaceClassifier;
10598 if ( theShape.ShapeType() == TopAbs_SOLID )
10600 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10601 bsc3d->PerformInfinitePoint(aTol);
10603 else if (theShape.ShapeType() == TopAbs_FACE )
10605 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10608 // iterates on indicated elements and get elements by back references from their nodes
10609 TIDSortedElemSet anAffected;
10610 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10611 for ( ; elemItr != theElems.end(); ++elemItr )
10613 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10617 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10618 while ( nodeItr->more() )
10620 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10621 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10623 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10624 while ( backElemItr->more() )
10626 const SMDS_MeshElement* curElem = backElemItr->next();
10627 if ( curElem && theElems.find(curElem) == theElems.end() &&
10629 isInside( curElem, *bsc3d, aTol ) :
10630 isInside( curElem, *aFaceClassifier, aTol )))
10631 anAffected.insert( curElem );
10635 return DoubleNodes( theElems, theNodesNot, anAffected );
10639 * \brief compute an oriented angle between two planes defined by four points.
10640 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10641 * @param p0 base of the rotation axe
10642 * @param p1 extremity of the rotation axe
10643 * @param g1 belongs to the first plane
10644 * @param g2 belongs to the second plane
10646 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10648 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10649 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10650 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10651 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10652 gp_Vec vref(p0, p1);
10655 gp_Vec n1 = vref.Crossed(v1);
10656 gp_Vec n2 = vref.Crossed(v2);
10658 return n2.AngleWithRef(n1, vref);
10660 catch ( Standard_Failure ) {
10662 return Max( v1.Magnitude(), v2.Magnitude() );
10666 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10667 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10668 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10669 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10670 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10671 * 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.
10672 * 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.
10673 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10674 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10675 * \param theElems - list of groups of volumes, where a group of volume is a set of
10676 * SMDS_MeshElements sorted by Id.
10677 * \param createJointElems - if TRUE, create the elements
10678 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10679 * the boundary between \a theDomains and the rest mesh
10680 * \return TRUE if operation has been completed successfully, FALSE otherwise
10682 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10683 bool createJointElems,
10684 bool onAllBoundaries)
10686 MESSAGE("----------------------------------------------");
10687 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10688 MESSAGE("----------------------------------------------");
10690 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10691 meshDS->BuildDownWardConnectivity(true);
10693 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10695 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10696 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10697 // build the list of nodes shared by 2 or more domains, with their domain indexes
10699 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10700 std::map<int,int>celldom; // cell vtkId --> domain
10701 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10702 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10703 faceDomains.clear();
10705 cellDomains.clear();
10706 nodeDomains.clear();
10707 std::map<int,int> emptyMap;
10708 std::set<int> emptySet;
10711 MESSAGE(".. Number of domains :"<<theElems.size());
10713 TIDSortedElemSet theRestDomElems;
10714 const int iRestDom = -1;
10715 const int idom0 = onAllBoundaries ? iRestDom : 0;
10716 const int nbDomains = theElems.size();
10718 // Check if the domains do not share an element
10719 for (int idom = 0; idom < nbDomains-1; idom++)
10721 // MESSAGE("... Check of domain #" << idom);
10722 const TIDSortedElemSet& domain = theElems[idom];
10723 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10724 for (; elemItr != domain.end(); ++elemItr)
10726 const SMDS_MeshElement* anElem = *elemItr;
10727 int idombisdeb = idom + 1 ;
10728 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10730 const TIDSortedElemSet& domainbis = theElems[idombis];
10731 if ( domainbis.count(anElem) )
10733 MESSAGE(".... Domain #" << idom);
10734 MESSAGE(".... Domain #" << idombis);
10735 throw SALOME_Exception("The domains are not disjoint.");
10742 for (int idom = 0; idom < nbDomains; idom++)
10745 // --- build a map (face to duplicate --> volume to modify)
10746 // with all the faces shared by 2 domains (group of elements)
10747 // and corresponding volume of this domain, for each shared face.
10748 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10750 MESSAGE("... Neighbors of domain #" << idom);
10751 const TIDSortedElemSet& domain = theElems[idom];
10752 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10753 for (; elemItr != domain.end(); ++elemItr)
10755 const SMDS_MeshElement* anElem = *elemItr;
10758 int vtkId = anElem->getVtkId();
10759 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10760 int neighborsVtkIds[NBMAXNEIGHBORS];
10761 int downIds[NBMAXNEIGHBORS];
10762 unsigned char downTypes[NBMAXNEIGHBORS];
10763 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10764 for (int n = 0; n < nbNeighbors; n++)
10766 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10767 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10768 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10771 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10773 // MESSAGE("Domain " << idombis);
10774 const TIDSortedElemSet& domainbis = theElems[idombis];
10775 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10777 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10779 DownIdType face(downIds[n], downTypes[n]);
10780 if (!faceDomains[face].count(idom))
10782 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10783 celldom[vtkId] = idom;
10784 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10788 theRestDomElems.insert( elem );
10789 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10790 celldom[neighborsVtkIds[n]] = iRestDom;
10798 //MESSAGE("Number of shared faces " << faceDomains.size());
10799 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10801 // --- explore the shared faces domain by domain,
10802 // explore the nodes of the face and see if they belong to a cell in the domain,
10803 // which has only a node or an edge on the border (not a shared face)
10805 for (int idomain = idom0; idomain < nbDomains; idomain++)
10807 //MESSAGE("Domain " << idomain);
10808 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10809 itface = faceDomains.begin();
10810 for (; itface != faceDomains.end(); ++itface)
10812 const std::map<int, int>& domvol = itface->second;
10813 if (!domvol.count(idomain))
10815 DownIdType face = itface->first;
10816 //MESSAGE(" --- face " << face.cellId);
10817 std::set<int> oldNodes;
10819 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10820 std::set<int>::iterator itn = oldNodes.begin();
10821 for (; itn != oldNodes.end(); ++itn)
10824 //MESSAGE(" node " << oldId);
10825 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10826 for (int i=0; i<l.ncells; i++)
10828 int vtkId = l.cells[i];
10829 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10830 if (!domain.count(anElem))
10832 int vtkType = grid->GetCellType(vtkId);
10833 int downId = grid->CellIdToDownId(vtkId);
10836 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10837 continue; // not OK at this stage of the algorithm:
10838 //no cells created after BuildDownWardConnectivity
10840 DownIdType aCell(downId, vtkType);
10841 cellDomains[aCell][idomain] = vtkId;
10842 celldom[vtkId] = idomain;
10843 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10849 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10850 // for each shared face, get the nodes
10851 // for each node, for each domain of the face, create a clone of the node
10853 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10854 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10855 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10857 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10858 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10859 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10861 MESSAGE(".. Duplication of the nodes");
10862 for (int idomain = idom0; idomain < nbDomains; idomain++)
10864 itface = faceDomains.begin();
10865 for (; itface != faceDomains.end(); ++itface)
10867 const std::map<int, int>& domvol = itface->second;
10868 if (!domvol.count(idomain))
10870 DownIdType face = itface->first;
10871 //MESSAGE(" --- face " << face.cellId);
10872 std::set<int> oldNodes;
10874 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10875 std::set<int>::iterator itn = oldNodes.begin();
10876 for (; itn != oldNodes.end(); ++itn)
10879 if (nodeDomains[oldId].empty())
10881 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10882 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10884 std::map<int, int>::const_iterator itdom = domvol.begin();
10885 for (; itdom != domvol.end(); ++itdom)
10887 int idom = itdom->first;
10888 //MESSAGE(" domain " << idom);
10889 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10891 if (nodeDomains[oldId].size() >= 2) // a multiple node
10893 vector<int> orderedDoms;
10894 //MESSAGE("multiple node " << oldId);
10895 if (mutipleNodes.count(oldId))
10896 orderedDoms = mutipleNodes[oldId];
10899 map<int,int>::iterator it = nodeDomains[oldId].begin();
10900 for (; it != nodeDomains[oldId].end(); ++it)
10901 orderedDoms.push_back(it->first);
10903 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10904 //stringstream txt;
10905 //for (int i=0; i<orderedDoms.size(); i++)
10906 // txt << orderedDoms[i] << " ";
10907 //MESSAGE("orderedDoms " << txt.str());
10908 mutipleNodes[oldId] = orderedDoms;
10910 double *coords = grid->GetPoint(oldId);
10911 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10912 int newId = newNode->getVtkId();
10913 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10914 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10921 MESSAGE(".. Creation of elements");
10922 for (int idomain = idom0; idomain < nbDomains; idomain++)
10924 itface = faceDomains.begin();
10925 for (; itface != faceDomains.end(); ++itface)
10927 std::map<int, int> domvol = itface->second;
10928 if (!domvol.count(idomain))
10930 DownIdType face = itface->first;
10931 //MESSAGE(" --- face " << face.cellId);
10932 std::set<int> oldNodes;
10934 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10935 int nbMultipleNodes = 0;
10936 std::set<int>::iterator itn = oldNodes.begin();
10937 for (; itn != oldNodes.end(); ++itn)
10940 if (mutipleNodes.count(oldId))
10943 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10945 //MESSAGE("multiple Nodes detected on a shared face");
10946 int downId = itface->first.cellId;
10947 unsigned char cellType = itface->first.cellType;
10948 // --- shared edge or shared face ?
10949 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10952 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10953 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10954 if (mutipleNodes.count(nodes[i]))
10955 if (!mutipleNodesToFace.count(nodes[i]))
10956 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10958 else // shared face (between two volumes)
10960 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10961 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10962 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10963 for (int ie =0; ie < nbEdges; ie++)
10966 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10967 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10969 vector<int> vn0 = mutipleNodes[nodes[0]];
10970 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10972 for (int i0 = 0; i0 < vn0.size(); i0++)
10973 for (int i1 = 0; i1 < vn1.size(); i1++)
10974 if (vn0[i0] == vn1[i1])
10975 doms.push_back(vn0[i0]);
10976 if (doms.size() >2)
10978 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10979 double *coords = grid->GetPoint(nodes[0]);
10980 gp_Pnt p0(coords[0], coords[1], coords[2]);
10981 coords = grid->GetPoint(nodes[nbNodes - 1]);
10982 gp_Pnt p1(coords[0], coords[1], coords[2]);
10984 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10985 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10986 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10987 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10988 for (int id=0; id < doms.size(); id++)
10990 int idom = doms[id];
10991 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10992 for (int ivol=0; ivol<nbvol; ivol++)
10994 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10995 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10996 if (domain.count(elem))
10998 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10999 domvol[idom] = svol;
11000 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11002 vtkIdType npts = 0;
11003 vtkIdType* pts = 0;
11004 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11005 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11008 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11009 angleDom[idom] = 0;
11013 gp_Pnt g(values[0], values[1], values[2]);
11014 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11015 //MESSAGE(" angle=" << angleDom[idom]);
11021 map<double, int> sortedDom; // sort domains by angle
11022 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11023 sortedDom[ia->second] = ia->first;
11024 vector<int> vnodes;
11026 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11028 vdom.push_back(ib->second);
11029 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11031 for (int ino = 0; ino < nbNodes; ino++)
11032 vnodes.push_back(nodes[ino]);
11033 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11042 // --- iterate on shared faces (volumes to modify, face to extrude)
11043 // get node id's of the face (id SMDS = id VTK)
11044 // create flat element with old and new nodes if requested
11046 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11047 // (domain1 X domain2) = domain1 + MAXINT*domain2
11049 std::map<int, std::map<long,int> > nodeQuadDomains;
11050 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11052 MESSAGE(".. Creation of elements: simple junction");
11053 if (createJointElems)
11056 string joints2DName = "joints2D";
11057 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11058 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11059 string joints3DName = "joints3D";
11060 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11061 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11063 itface = faceDomains.begin();
11064 for (; itface != faceDomains.end(); ++itface)
11066 DownIdType face = itface->first;
11067 std::set<int> oldNodes;
11068 std::set<int>::iterator itn;
11070 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11072 std::map<int, int> domvol = itface->second;
11073 std::map<int, int>::iterator itdom = domvol.begin();
11074 int dom1 = itdom->first;
11075 int vtkVolId = itdom->second;
11077 int dom2 = itdom->first;
11078 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11080 stringstream grpname;
11083 grpname << dom1 << "_" << dom2;
11085 grpname << dom2 << "_" << dom1;
11086 string namegrp = grpname.str();
11087 if (!mapOfJunctionGroups.count(namegrp))
11088 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11089 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11091 sgrp->Add(vol->GetID());
11092 if (vol->GetType() == SMDSAbs_Volume)
11093 joints3DGrp->Add(vol->GetID());
11094 else if (vol->GetType() == SMDSAbs_Face)
11095 joints2DGrp->Add(vol->GetID());
11099 // --- create volumes on multiple domain intersection if requested
11100 // iterate on mutipleNodesToFace
11101 // iterate on edgesMultiDomains
11103 MESSAGE(".. Creation of elements: multiple junction");
11104 if (createJointElems)
11106 // --- iterate on mutipleNodesToFace
11108 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11109 for (; itn != mutipleNodesToFace.end(); ++itn)
11111 int node = itn->first;
11112 vector<int> orderDom = itn->second;
11113 vector<vtkIdType> orderedNodes;
11114 for (int idom = 0; idom <orderDom.size(); idom++)
11115 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11116 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11118 stringstream grpname;
11120 grpname << 0 << "_" << 0;
11122 string namegrp = grpname.str();
11123 if (!mapOfJunctionGroups.count(namegrp))
11124 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11125 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11127 sgrp->Add(face->GetID());
11130 // --- iterate on edgesMultiDomains
11132 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11133 for (; ite != edgesMultiDomains.end(); ++ite)
11135 vector<int> nodes = ite->first;
11136 vector<int> orderDom = ite->second;
11137 vector<vtkIdType> orderedNodes;
11138 if (nodes.size() == 2)
11140 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11141 for (int ino=0; ino < nodes.size(); ino++)
11142 if (orderDom.size() == 3)
11143 for (int idom = 0; idom <orderDom.size(); idom++)
11144 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11146 for (int idom = orderDom.size()-1; idom >=0; idom--)
11147 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11148 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11151 string namegrp = "jointsMultiples";
11152 if (!mapOfJunctionGroups.count(namegrp))
11153 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11154 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11156 sgrp->Add(vol->GetID());
11160 INFOS("Quadratic multiple joints not implemented");
11161 // TODO quadratic nodes
11166 // --- list the explicit faces and edges of the mesh that need to be modified,
11167 // i.e. faces and edges built with one or more duplicated nodes.
11168 // associate these faces or edges to their corresponding domain.
11169 // only the first domain found is kept when a face or edge is shared
11171 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11172 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11173 faceOrEdgeDom.clear();
11176 MESSAGE(".. Modification of elements");
11177 for (int idomain = idom0; idomain < nbDomains; idomain++)
11179 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11180 for (; itnod != nodeDomains.end(); ++itnod)
11182 int oldId = itnod->first;
11183 //MESSAGE(" node " << oldId);
11184 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11185 for (int i = 0; i < l.ncells; i++)
11187 int vtkId = l.cells[i];
11188 int vtkType = grid->GetCellType(vtkId);
11189 int downId = grid->CellIdToDownId(vtkId);
11191 continue; // new cells: not to be modified
11192 DownIdType aCell(downId, vtkType);
11193 int volParents[1000];
11194 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11195 for (int j = 0; j < nbvol; j++)
11196 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11197 if (!feDom.count(vtkId))
11199 feDom[vtkId] = idomain;
11200 faceOrEdgeDom[aCell] = emptyMap;
11201 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11202 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11203 // << " type " << vtkType << " downId " << downId);
11209 // --- iterate on shared faces (volumes to modify, face to extrude)
11210 // get node id's of the face
11211 // replace old nodes by new nodes in volumes, and update inverse connectivity
11213 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11214 for (int m=0; m<3; m++)
11216 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11217 itface = (*amap).begin();
11218 for (; itface != (*amap).end(); ++itface)
11220 DownIdType face = itface->first;
11221 std::set<int> oldNodes;
11222 std::set<int>::iterator itn;
11224 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11225 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11226 std::map<int, int> localClonedNodeIds;
11228 std::map<int, int> domvol = itface->second;
11229 std::map<int, int>::iterator itdom = domvol.begin();
11230 for (; itdom != domvol.end(); ++itdom)
11232 int idom = itdom->first;
11233 int vtkVolId = itdom->second;
11234 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11235 localClonedNodeIds.clear();
11236 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11239 if (nodeDomains[oldId].count(idom))
11241 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11242 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11245 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11250 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11251 grid->BuildLinks();
11259 * \brief Double nodes on some external faces and create flat elements.
11260 * Flat elements are mainly used by some types of mechanic calculations.
11262 * Each group of the list must be constituted of faces.
11263 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11264 * @param theElems - list of groups of faces, where a group of faces is a set of
11265 * SMDS_MeshElements sorted by Id.
11266 * @return TRUE if operation has been completed successfully, FALSE otherwise
11268 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11270 MESSAGE("-------------------------------------------------");
11271 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11272 MESSAGE("-------------------------------------------------");
11274 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11276 // --- For each group of faces
11277 // duplicate the nodes, create a flat element based on the face
11278 // replace the nodes of the faces by their clones
11280 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11281 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11282 clonedNodes.clear();
11283 intermediateNodes.clear();
11284 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11285 mapOfJunctionGroups.clear();
11287 for (int idom = 0; idom < theElems.size(); idom++)
11289 const TIDSortedElemSet& domain = theElems[idom];
11290 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11291 for (; elemItr != domain.end(); ++elemItr)
11293 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11294 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11297 // MESSAGE("aFace=" << aFace->GetID());
11298 bool isQuad = aFace->IsQuadratic();
11299 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11301 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11303 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11304 while (nodeIt->more())
11306 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11307 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11309 ln2.push_back(node);
11311 ln0.push_back(node);
11313 const SMDS_MeshNode* clone = 0;
11314 if (!clonedNodes.count(node))
11316 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11317 clonedNodes[node] = clone;
11320 clone = clonedNodes[node];
11323 ln3.push_back(clone);
11325 ln1.push_back(clone);
11327 const SMDS_MeshNode* inter = 0;
11328 if (isQuad && (!isMedium))
11330 if (!intermediateNodes.count(node))
11332 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11333 intermediateNodes[node] = inter;
11336 inter = intermediateNodes[node];
11337 ln4.push_back(inter);
11341 // --- extrude the face
11343 vector<const SMDS_MeshNode*> ln;
11344 SMDS_MeshVolume* vol = 0;
11345 vtkIdType aType = aFace->GetVtkType();
11349 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11350 // MESSAGE("vol prism " << vol->GetID());
11351 ln.push_back(ln1[0]);
11352 ln.push_back(ln1[1]);
11353 ln.push_back(ln1[2]);
11356 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11357 // MESSAGE("vol hexa " << vol->GetID());
11358 ln.push_back(ln1[0]);
11359 ln.push_back(ln1[1]);
11360 ln.push_back(ln1[2]);
11361 ln.push_back(ln1[3]);
11363 case VTK_QUADRATIC_TRIANGLE:
11364 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11365 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11366 // MESSAGE("vol quad prism " << vol->GetID());
11367 ln.push_back(ln1[0]);
11368 ln.push_back(ln1[1]);
11369 ln.push_back(ln1[2]);
11370 ln.push_back(ln3[0]);
11371 ln.push_back(ln3[1]);
11372 ln.push_back(ln3[2]);
11374 case VTK_QUADRATIC_QUAD:
11375 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11376 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11377 // ln4[0], ln4[1], ln4[2], ln4[3]);
11378 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11379 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11380 ln4[0], ln4[1], ln4[2], ln4[3]);
11381 // MESSAGE("vol quad hexa " << vol->GetID());
11382 ln.push_back(ln1[0]);
11383 ln.push_back(ln1[1]);
11384 ln.push_back(ln1[2]);
11385 ln.push_back(ln1[3]);
11386 ln.push_back(ln3[0]);
11387 ln.push_back(ln3[1]);
11388 ln.push_back(ln3[2]);
11389 ln.push_back(ln3[3]);
11399 stringstream grpname;
11403 string namegrp = grpname.str();
11404 if (!mapOfJunctionGroups.count(namegrp))
11405 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11406 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11408 sgrp->Add(vol->GetID());
11411 // --- modify the face
11413 aFace->ChangeNodes(&ln[0], ln.size());
11420 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11421 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11422 * groups of faces to remove inside the object, (idem edges).
11423 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11425 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11426 const TopoDS_Shape& theShape,
11427 SMESH_NodeSearcher* theNodeSearcher,
11428 const char* groupName,
11429 std::vector<double>& nodesCoords,
11430 std::vector<std::vector<int> >& listOfListOfNodes)
11432 MESSAGE("--------------------------------");
11433 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11434 MESSAGE("--------------------------------");
11436 // --- zone of volumes to remove is given :
11437 // 1 either by a geom shape (one or more vertices) and a radius,
11438 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11439 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11440 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11441 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11442 // defined by it's name.
11444 SMESHDS_GroupBase* groupDS = 0;
11445 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11446 while ( groupIt->more() )
11449 SMESH_Group * group = groupIt->next();
11450 if ( !group ) continue;
11451 groupDS = group->GetGroupDS();
11452 if ( !groupDS || groupDS->IsEmpty() ) continue;
11453 std::string grpName = group->GetName();
11454 //MESSAGE("grpName=" << grpName);
11455 if (grpName == groupName)
11461 bool isNodeGroup = false;
11462 bool isNodeCoords = false;
11465 if (groupDS->GetType() != SMDSAbs_Node)
11467 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11470 if (nodesCoords.size() > 0)
11471 isNodeCoords = true; // a list o nodes given by their coordinates
11472 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11474 // --- define groups to build
11476 int idg; // --- group of SMDS volumes
11477 string grpvName = groupName;
11478 grpvName += "_vol";
11479 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11482 MESSAGE("group not created " << grpvName);
11485 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11487 int idgs; // --- group of SMDS faces on the skin
11488 string grpsName = groupName;
11489 grpsName += "_skin";
11490 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11493 MESSAGE("group not created " << grpsName);
11496 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11498 int idgi; // --- group of SMDS faces internal (several shapes)
11499 string grpiName = groupName;
11500 grpiName += "_internalFaces";
11501 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11504 MESSAGE("group not created " << grpiName);
11507 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11509 int idgei; // --- group of SMDS faces internal (several shapes)
11510 string grpeiName = groupName;
11511 grpeiName += "_internalEdges";
11512 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11515 MESSAGE("group not created " << grpeiName);
11518 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11520 // --- build downward connectivity
11522 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11523 meshDS->BuildDownWardConnectivity(true);
11524 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11526 // --- set of volumes detected inside
11528 std::set<int> setOfInsideVol;
11529 std::set<int> setOfVolToCheck;
11531 std::vector<gp_Pnt> gpnts;
11534 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11536 MESSAGE("group of nodes provided");
11537 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11538 while ( elemIt->more() )
11540 const SMDS_MeshElement* elem = elemIt->next();
11543 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11546 SMDS_MeshElement* vol = 0;
11547 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11548 while (volItr->more())
11550 vol = (SMDS_MeshElement*)volItr->next();
11551 setOfInsideVol.insert(vol->getVtkId());
11552 sgrp->Add(vol->GetID());
11556 else if (isNodeCoords)
11558 MESSAGE("list of nodes coordinates provided");
11561 while (i < nodesCoords.size()-2)
11563 double x = nodesCoords[i++];
11564 double y = nodesCoords[i++];
11565 double z = nodesCoords[i++];
11566 gp_Pnt p = gp_Pnt(x, y ,z);
11567 gpnts.push_back(p);
11568 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11572 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11574 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11575 TopTools_IndexedMapOfShape vertexMap;
11576 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11577 gp_Pnt p = gp_Pnt(0,0,0);
11578 if (vertexMap.Extent() < 1)
11581 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11583 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11584 p = BRep_Tool::Pnt(vertex);
11585 gpnts.push_back(p);
11586 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11590 if (gpnts.size() > 0)
11593 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11595 nodeId = startNode->GetID();
11596 MESSAGE("nodeId " << nodeId);
11598 double radius2 = radius*radius;
11599 MESSAGE("radius2 " << radius2);
11601 // --- volumes on start node
11603 setOfVolToCheck.clear();
11604 SMDS_MeshElement* startVol = 0;
11605 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11606 while (volItr->more())
11608 startVol = (SMDS_MeshElement*)volItr->next();
11609 setOfVolToCheck.insert(startVol->getVtkId());
11611 if (setOfVolToCheck.empty())
11613 MESSAGE("No volumes found");
11617 // --- starting with central volumes then their neighbors, check if they are inside
11618 // or outside the domain, until no more new neighbor volume is inside.
11619 // Fill the group of inside volumes
11621 std::map<int, double> mapOfNodeDistance2;
11622 mapOfNodeDistance2.clear();
11623 std::set<int> setOfOutsideVol;
11624 while (!setOfVolToCheck.empty())
11626 std::set<int>::iterator it = setOfVolToCheck.begin();
11628 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11629 bool volInside = false;
11630 vtkIdType npts = 0;
11631 vtkIdType* pts = 0;
11632 grid->GetCellPoints(vtkId, npts, pts);
11633 for (int i=0; i<npts; i++)
11635 double distance2 = 0;
11636 if (mapOfNodeDistance2.count(pts[i]))
11638 distance2 = mapOfNodeDistance2[pts[i]];
11639 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11643 double *coords = grid->GetPoint(pts[i]);
11644 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11646 for (int j=0; j<gpnts.size(); j++)
11648 double d2 = aPoint.SquareDistance(gpnts[j]);
11649 if (d2 < distance2)
11652 if (distance2 < radius2)
11656 mapOfNodeDistance2[pts[i]] = distance2;
11657 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11659 if (distance2 < radius2)
11661 volInside = true; // one or more nodes inside the domain
11662 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11668 setOfInsideVol.insert(vtkId);
11669 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11670 int neighborsVtkIds[NBMAXNEIGHBORS];
11671 int downIds[NBMAXNEIGHBORS];
11672 unsigned char downTypes[NBMAXNEIGHBORS];
11673 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11674 for (int n = 0; n < nbNeighbors; n++)
11675 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11676 setOfVolToCheck.insert(neighborsVtkIds[n]);
11680 setOfOutsideVol.insert(vtkId);
11681 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11683 setOfVolToCheck.erase(vtkId);
11687 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11688 // If yes, add the volume to the inside set
11690 bool addedInside = true;
11691 std::set<int> setOfVolToReCheck;
11692 while (addedInside)
11694 MESSAGE(" --------------------------- re check");
11695 addedInside = false;
11696 std::set<int>::iterator itv = setOfInsideVol.begin();
11697 for (; itv != setOfInsideVol.end(); ++itv)
11700 int neighborsVtkIds[NBMAXNEIGHBORS];
11701 int downIds[NBMAXNEIGHBORS];
11702 unsigned char downTypes[NBMAXNEIGHBORS];
11703 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11704 for (int n = 0; n < nbNeighbors; n++)
11705 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11706 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11708 setOfVolToCheck = setOfVolToReCheck;
11709 setOfVolToReCheck.clear();
11710 while (!setOfVolToCheck.empty())
11712 std::set<int>::iterator it = setOfVolToCheck.begin();
11714 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11716 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11717 int countInside = 0;
11718 int neighborsVtkIds[NBMAXNEIGHBORS];
11719 int downIds[NBMAXNEIGHBORS];
11720 unsigned char downTypes[NBMAXNEIGHBORS];
11721 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11722 for (int n = 0; n < nbNeighbors; n++)
11723 if (setOfInsideVol.count(neighborsVtkIds[n]))
11725 MESSAGE("countInside " << countInside);
11726 if (countInside > 1)
11728 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11729 setOfInsideVol.insert(vtkId);
11730 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11731 addedInside = true;
11734 setOfVolToReCheck.insert(vtkId);
11736 setOfVolToCheck.erase(vtkId);
11740 // --- map of Downward faces at the boundary, inside the global volume
11741 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11742 // fill group of SMDS faces inside the volume (when several volume shapes)
11743 // fill group of SMDS faces on the skin of the global volume (if skin)
11745 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11746 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11747 std::set<int>::iterator it = setOfInsideVol.begin();
11748 for (; it != setOfInsideVol.end(); ++it)
11751 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11752 int neighborsVtkIds[NBMAXNEIGHBORS];
11753 int downIds[NBMAXNEIGHBORS];
11754 unsigned char downTypes[NBMAXNEIGHBORS];
11755 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11756 for (int n = 0; n < nbNeighbors; n++)
11758 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11759 if (neighborDim == 3)
11761 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11763 DownIdType face(downIds[n], downTypes[n]);
11764 boundaryFaces[face] = vtkId;
11766 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11767 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11768 if (vtkFaceId >= 0)
11770 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11771 // find also the smds edges on this face
11772 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11773 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11774 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11775 for (int i = 0; i < nbEdges; i++)
11777 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11778 if (vtkEdgeId >= 0)
11779 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11783 else if (neighborDim == 2) // skin of the volume
11785 DownIdType face(downIds[n], downTypes[n]);
11786 skinFaces[face] = vtkId;
11787 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11788 if (vtkFaceId >= 0)
11789 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11794 // --- identify the edges constituting the wire of each subshape on the skin
11795 // define polylines with the nodes of edges, equivalent to wires
11796 // project polylines on subshapes, and partition, to get geom faces
11798 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11799 std::set<int> emptySet;
11801 std::set<int> shapeIds;
11803 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11804 while (itelem->more())
11806 const SMDS_MeshElement *elem = itelem->next();
11807 int shapeId = elem->getshapeId();
11808 int vtkId = elem->getVtkId();
11809 if (!shapeIdToVtkIdSet.count(shapeId))
11811 shapeIdToVtkIdSet[shapeId] = emptySet;
11812 shapeIds.insert(shapeId);
11814 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11817 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11818 std::set<DownIdType, DownIdCompare> emptyEdges;
11819 emptyEdges.clear();
11821 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11822 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11824 int shapeId = itShape->first;
11825 MESSAGE(" --- Shape ID --- "<< shapeId);
11826 shapeIdToEdges[shapeId] = emptyEdges;
11828 std::vector<int> nodesEdges;
11830 std::set<int>::iterator its = itShape->second.begin();
11831 for (; its != itShape->second.end(); ++its)
11834 MESSAGE(" " << vtkId);
11835 int neighborsVtkIds[NBMAXNEIGHBORS];
11836 int downIds[NBMAXNEIGHBORS];
11837 unsigned char downTypes[NBMAXNEIGHBORS];
11838 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11839 for (int n = 0; n < nbNeighbors; n++)
11841 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11843 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11844 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11845 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11847 DownIdType edge(downIds[n], downTypes[n]);
11848 if (!shapeIdToEdges[shapeId].count(edge))
11850 shapeIdToEdges[shapeId].insert(edge);
11852 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11853 nodesEdges.push_back(vtkNodeId[0]);
11854 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11855 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11861 std::list<int> order;
11863 if (nodesEdges.size() > 0)
11865 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11866 nodesEdges[0] = -1;
11867 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11868 nodesEdges[1] = -1; // do not reuse this edge
11872 int nodeTofind = order.back(); // try first to push back
11874 for (i = 0; i<nodesEdges.size(); i++)
11875 if (nodesEdges[i] == nodeTofind)
11877 if (i == nodesEdges.size())
11878 found = false; // no follower found on back
11881 if (i%2) // odd ==> use the previous one
11882 if (nodesEdges[i-1] < 0)
11886 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11887 nodesEdges[i-1] = -1;
11889 else // even ==> use the next one
11890 if (nodesEdges[i+1] < 0)
11894 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11895 nodesEdges[i+1] = -1;
11900 // try to push front
11902 nodeTofind = order.front(); // try to push front
11903 for (i = 0; i<nodesEdges.size(); i++)
11904 if (nodesEdges[i] == nodeTofind)
11906 if (i == nodesEdges.size())
11908 found = false; // no predecessor found on front
11911 if (i%2) // odd ==> use the previous one
11912 if (nodesEdges[i-1] < 0)
11916 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11917 nodesEdges[i-1] = -1;
11919 else // even ==> use the next one
11920 if (nodesEdges[i+1] < 0)
11924 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11925 nodesEdges[i+1] = -1;
11931 std::vector<int> nodes;
11932 nodes.push_back(shapeId);
11933 std::list<int>::iterator itl = order.begin();
11934 for (; itl != order.end(); itl++)
11936 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11937 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11939 listOfListOfNodes.push_back(nodes);
11942 // partition geom faces with blocFissure
11943 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11944 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11950 //================================================================================
11952 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11953 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11954 * \return TRUE if operation has been completed successfully, FALSE otherwise
11956 //================================================================================
11958 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11960 // iterates on volume elements and detect all free faces on them
11961 SMESHDS_Mesh* aMesh = GetMeshDS();
11964 //bool res = false;
11965 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11966 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11969 const SMDS_MeshVolume* volume = vIt->next();
11970 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11971 vTool.SetExternalNormal();
11972 //const bool isPoly = volume->IsPoly();
11973 const int iQuad = volume->IsQuadratic();
11974 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11976 if (!vTool.IsFreeFace(iface))
11979 vector<const SMDS_MeshNode *> nodes;
11980 int nbFaceNodes = vTool.NbFaceNodes(iface);
11981 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11983 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11984 nodes.push_back(faceNodes[inode]);
11985 if (iQuad) { // add medium nodes
11986 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11987 nodes.push_back(faceNodes[inode]);
11988 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11989 nodes.push_back(faceNodes[8]);
11991 // add new face based on volume nodes
11992 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11994 continue; // face already exsist
11996 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12000 return ( nbFree==(nbExisted+nbCreated) );
12005 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12007 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12009 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12012 //================================================================================
12014 * \brief Creates missing boundary elements
12015 * \param elements - elements whose boundary is to be checked
12016 * \param dimension - defines type of boundary elements to create
12017 * \param group - a group to store created boundary elements in
12018 * \param targetMesh - a mesh to store created boundary elements in
12019 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12020 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12021 * boundary elements will be copied into the targetMesh
12022 * \param toAddExistingBondary - if true, not only new but also pre-existing
12023 * boundary elements will be added into the new group
12024 * \param aroundElements - if true, elements will be created on boundary of given
12025 * elements else, on boundary of the whole mesh.
12026 * \return nb of added boundary elements
12028 //================================================================================
12030 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12031 Bnd_Dimension dimension,
12032 SMESH_Group* group/*=0*/,
12033 SMESH_Mesh* targetMesh/*=0*/,
12034 bool toCopyElements/*=false*/,
12035 bool toCopyExistingBoundary/*=false*/,
12036 bool toAddExistingBondary/*= false*/,
12037 bool aroundElements/*= false*/)
12039 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12040 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12041 // hope that all elements are of the same type, do not check them all
12042 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12043 throw SALOME_Exception(LOCALIZED("wrong element type"));
12046 toCopyElements = toCopyExistingBoundary = false;
12048 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12049 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12050 int nbAddedBnd = 0;
12052 // editor adding present bnd elements and optionally holding elements to add to the group
12053 SMESH_MeshEditor* presentEditor;
12054 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12055 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12057 SMESH_MesherHelper helper( *myMesh );
12058 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12059 SMDS_VolumeTool vTool;
12060 TIDSortedElemSet avoidSet;
12061 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12064 typedef vector<const SMDS_MeshNode*> TConnectivity;
12066 SMDS_ElemIteratorPtr eIt;
12067 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12068 else eIt = elemSetIterator( elements );
12070 while (eIt->more())
12072 const SMDS_MeshElement* elem = eIt->next();
12073 const int iQuad = elem->IsQuadratic();
12075 // ------------------------------------------------------------------------------------
12076 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12077 // ------------------------------------------------------------------------------------
12078 vector<const SMDS_MeshElement*> presentBndElems;
12079 vector<TConnectivity> missingBndElems;
12080 TConnectivity nodes, elemNodes;
12081 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12083 vTool.SetExternalNormal();
12084 const SMDS_MeshElement* otherVol = 0;
12085 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12087 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12088 ( !aroundElements || elements.count( otherVol )))
12090 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12091 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12092 if ( missType == SMDSAbs_Edge ) // boundary edges
12094 nodes.resize( 2+iQuad );
12095 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12097 for ( int j = 0; j < nodes.size(); ++j )
12099 if ( const SMDS_MeshElement* edge =
12100 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12101 presentBndElems.push_back( edge );
12103 missingBndElems.push_back( nodes );
12106 else // boundary face
12109 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12110 nodes.push_back( nn[inode] ); // add corner nodes
12112 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12113 nodes.push_back( nn[inode] ); // add medium nodes
12114 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12116 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12118 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12119 SMDSAbs_Face, /*noMedium=*/false ))
12120 presentBndElems.push_back( f );
12122 missingBndElems.push_back( nodes );
12124 if ( targetMesh != myMesh )
12126 // add 1D elements on face boundary to be added to a new mesh
12127 const SMDS_MeshElement* edge;
12128 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12131 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12133 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12134 if ( edge && avoidSet.insert( edge ).second )
12135 presentBndElems.push_back( edge );
12141 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12143 avoidSet.clear(), avoidSet.insert( elem );
12144 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12145 SMDS_MeshElement::iterator() );
12146 elemNodes.push_back( elemNodes[0] );
12147 nodes.resize( 2 + iQuad );
12148 const int nbLinks = elem->NbCornerNodes();
12149 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12151 nodes[0] = elemNodes[iN];
12152 nodes[1] = elemNodes[iN+1+iQuad];
12153 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12154 continue; // not free link
12156 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12157 if ( const SMDS_MeshElement* edge =
12158 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12159 presentBndElems.push_back( edge );
12161 missingBndElems.push_back( nodes );
12165 // ---------------------------------
12166 // 2. Add missing boundary elements
12167 // ---------------------------------
12168 if ( targetMesh != myMesh )
12169 // instead of making a map of nodes in this mesh and targetMesh,
12170 // we create nodes with same IDs.
12171 for ( int i = 0; i < missingBndElems.size(); ++i )
12173 TConnectivity& srcNodes = missingBndElems[i];
12174 TConnectivity nodes( srcNodes.size() );
12175 for ( inode = 0; inode < nodes.size(); ++inode )
12176 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12177 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12179 /*noMedium=*/false))
12181 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12185 for ( int i = 0; i < missingBndElems.size(); ++i )
12187 TConnectivity& nodes = missingBndElems[i];
12188 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12190 /*noMedium=*/false))
12192 SMDS_MeshElement* elem =
12193 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12196 // try to set a new element to a shape
12197 if ( myMesh->HasShapeToMesh() )
12200 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12201 const int nbN = nodes.size() / (iQuad+1 );
12202 for ( inode = 0; inode < nbN && ok; ++inode )
12204 pair<int, TopAbs_ShapeEnum> i_stype =
12205 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12206 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12207 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12209 if ( ok && mediumShapes.size() > 1 )
12211 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12212 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12213 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12215 if (( ok = ( stype_i->first != stype_i_0.first )))
12216 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12217 aMesh->IndexToShape( stype_i_0.second ));
12220 if ( ok && mediumShapes.begin()->first == missShapeType )
12221 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12225 // ----------------------------------
12226 // 3. Copy present boundary elements
12227 // ----------------------------------
12228 if ( toCopyExistingBoundary )
12229 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12231 const SMDS_MeshElement* e = presentBndElems[i];
12232 TConnectivity nodes( e->NbNodes() );
12233 for ( inode = 0; inode < nodes.size(); ++inode )
12234 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12235 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12237 else // store present elements to add them to a group
12238 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12240 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12243 } // loop on given elements
12245 // ---------------------------------------------
12246 // 4. Fill group with boundary elements
12247 // ---------------------------------------------
12250 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12251 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12252 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12254 tgtEditor.myLastCreatedElems.Clear();
12255 tgtEditor2.myLastCreatedElems.Clear();
12257 // -----------------------
12258 // 5. Copy given elements
12259 // -----------------------
12260 if ( toCopyElements && targetMesh != myMesh )
12262 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12263 else eIt = elemSetIterator( elements );
12264 while (eIt->more())
12266 const SMDS_MeshElement* elem = eIt->next();
12267 TConnectivity nodes( elem->NbNodes() );
12268 for ( inode = 0; inode < nodes.size(); ++inode )
12269 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12270 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12272 tgtEditor.myLastCreatedElems.Clear();