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 OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3705 if ( projector.SquareDistance( i ) < minVal ) {
3706 minVal = projector.SquareDistance( i );
3708 if ( projector.Value( i ) < minVal ) {
3709 minVal = projector.Value( i );
3711 projector.Point( i ).Parameter( u, v );
3713 result.SetCoord( u, v );
3719 //=======================================================================
3721 //purpose : Smooth theElements during theNbIterations or until a worst
3722 // element has aspect ratio <= theTgtAspectRatio.
3723 // Aspect Ratio varies in range [1.0, inf].
3724 // If theElements is empty, the whole mesh is smoothed.
3725 // theFixedNodes contains additionally fixed nodes. Nodes built
3726 // on edges and boundary nodes are always fixed.
3727 //=======================================================================
3729 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3730 set<const SMDS_MeshNode*> & theFixedNodes,
3731 const SmoothMethod theSmoothMethod,
3732 const int theNbIterations,
3733 double theTgtAspectRatio,
3736 myLastCreatedElems.Clear();
3737 myLastCreatedNodes.Clear();
3739 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3741 if ( theTgtAspectRatio < 1.0 )
3742 theTgtAspectRatio = 1.0;
3744 const double disttol = 1.e-16;
3746 SMESH::Controls::AspectRatio aQualityFunc;
3748 SMESHDS_Mesh* aMesh = GetMeshDS();
3750 if ( theElems.empty() ) {
3751 // add all faces to theElems
3752 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3753 while ( fIt->more() ) {
3754 const SMDS_MeshElement* face = fIt->next();
3755 theElems.insert( theElems.end(), face );
3758 // get all face ids theElems are on
3759 set< int > faceIdSet;
3760 TIDSortedElemSet::iterator itElem;
3762 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3763 int fId = FindShape( *itElem );
3764 // check that corresponding submesh exists and a shape is face
3766 faceIdSet.find( fId ) == faceIdSet.end() &&
3767 aMesh->MeshElements( fId )) {
3768 TopoDS_Shape F = aMesh->IndexToShape( fId );
3769 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3770 faceIdSet.insert( fId );
3773 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3775 // ===============================================
3776 // smooth elements on each TopoDS_Face separately
3777 // ===============================================
3779 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3780 for ( ; fId != faceIdSet.rend(); ++fId ) {
3781 // get face surface and submesh
3782 Handle(Geom_Surface) surface;
3783 SMESHDS_SubMesh* faceSubMesh = 0;
3785 double fToler2 = 0, f,l;
3786 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3787 bool isUPeriodic = false, isVPeriodic = false;
3789 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3790 surface = BRep_Tool::Surface( face );
3791 faceSubMesh = aMesh->MeshElements( *fId );
3792 fToler2 = BRep_Tool::Tolerance( face );
3793 fToler2 *= fToler2 * 10.;
3794 isUPeriodic = surface->IsUPeriodic();
3797 isVPeriodic = surface->IsVPeriodic();
3800 surface->Bounds( u1, u2, v1, v2 );
3802 // ---------------------------------------------------------
3803 // for elements on a face, find movable and fixed nodes and
3804 // compute UV for them
3805 // ---------------------------------------------------------
3806 bool checkBoundaryNodes = false;
3807 bool isQuadratic = false;
3808 set<const SMDS_MeshNode*> setMovableNodes;
3809 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3810 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3811 list< const SMDS_MeshElement* > elemsOnFace;
3813 Extrema_GenExtPS projector;
3814 GeomAdaptor_Surface surfAdaptor;
3815 if ( !surface.IsNull() ) {
3816 surfAdaptor.Load( surface );
3817 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3819 int nbElemOnFace = 0;
3820 itElem = theElems.begin();
3821 // loop on not yet smoothed elements: look for elems on a face
3822 while ( itElem != theElems.end() ) {
3823 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3824 break; // all elements found
3826 const SMDS_MeshElement* elem = *itElem;
3827 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3828 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3832 elemsOnFace.push_back( elem );
3833 theElems.erase( itElem++ );
3837 isQuadratic = elem->IsQuadratic();
3839 // get movable nodes of elem
3840 const SMDS_MeshNode* node;
3841 SMDS_TypeOfPosition posType;
3842 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3843 int nn = 0, nbn = elem->NbNodes();
3844 if(elem->IsQuadratic())
3846 while ( nn++ < nbn ) {
3847 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3848 const SMDS_PositionPtr& pos = node->GetPosition();
3849 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3850 if (posType != SMDS_TOP_EDGE &&
3851 posType != SMDS_TOP_VERTEX &&
3852 theFixedNodes.find( node ) == theFixedNodes.end())
3854 // check if all faces around the node are on faceSubMesh
3855 // because a node on edge may be bound to face
3856 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3858 if ( faceSubMesh ) {
3859 while ( eIt->more() && all ) {
3860 const SMDS_MeshElement* e = eIt->next();
3861 all = faceSubMesh->Contains( e );
3865 setMovableNodes.insert( node );
3867 checkBoundaryNodes = true;
3869 if ( posType == SMDS_TOP_3DSPACE )
3870 checkBoundaryNodes = true;
3873 if ( surface.IsNull() )
3876 // get nodes to check UV
3877 list< const SMDS_MeshNode* > uvCheckNodes;
3878 itN = elem->nodesIterator();
3879 nn = 0; nbn = elem->NbNodes();
3880 if(elem->IsQuadratic())
3882 while ( nn++ < nbn ) {
3883 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3884 if ( uvMap.find( node ) == uvMap.end() )
3885 uvCheckNodes.push_back( node );
3886 // add nodes of elems sharing node
3887 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3888 // while ( eIt->more() ) {
3889 // const SMDS_MeshElement* e = eIt->next();
3890 // if ( e != elem ) {
3891 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3892 // while ( nIt->more() ) {
3893 // const SMDS_MeshNode* n =
3894 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3895 // if ( uvMap.find( n ) == uvMap.end() )
3896 // uvCheckNodes.push_back( n );
3902 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3903 for ( ; n != uvCheckNodes.end(); ++n ) {
3906 const SMDS_PositionPtr& pos = node->GetPosition();
3907 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3909 switch ( posType ) {
3910 case SMDS_TOP_FACE: {
3911 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3912 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3915 case SMDS_TOP_EDGE: {
3916 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3917 Handle(Geom2d_Curve) pcurve;
3918 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3919 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3920 if ( !pcurve.IsNull() ) {
3921 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3922 uv = pcurve->Value( u ).XY();
3926 case SMDS_TOP_VERTEX: {
3927 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3928 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3929 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3934 // check existing UV
3935 bool project = true;
3936 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3937 double dist1 = DBL_MAX, dist2 = 0;
3938 if ( posType != SMDS_TOP_3DSPACE ) {
3939 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3940 project = dist1 > fToler2;
3942 if ( project ) { // compute new UV
3944 if ( !getClosestUV( projector, pNode, newUV )) {
3945 MESSAGE("Node Projection Failed " << node);
3949 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3951 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3953 if ( posType != SMDS_TOP_3DSPACE )
3954 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3955 if ( dist2 < dist1 )
3959 // store UV in the map
3960 listUV.push_back( uv );
3961 uvMap.insert( make_pair( node, &listUV.back() ));
3963 } // loop on not yet smoothed elements
3965 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3966 checkBoundaryNodes = true;
3968 // fix nodes on mesh boundary
3970 if ( checkBoundaryNodes ) {
3971 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3972 map< SMESH_TLink, int >::iterator link_nb;
3973 // put all elements links to linkNbMap
3974 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3975 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3976 const SMDS_MeshElement* elem = (*elemIt);
3977 int nbn = elem->NbCornerNodes();
3978 // loop on elem links: insert them in linkNbMap
3979 for ( int iN = 0; iN < nbn; ++iN ) {
3980 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3981 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3982 SMESH_TLink link( n1, n2 );
3983 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3987 // remove nodes that are in links encountered only once from setMovableNodes
3988 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3989 if ( link_nb->second == 1 ) {
3990 setMovableNodes.erase( link_nb->first.node1() );
3991 setMovableNodes.erase( link_nb->first.node2() );
3996 // -----------------------------------------------------
3997 // for nodes on seam edge, compute one more UV ( uvMap2 );
3998 // find movable nodes linked to nodes on seam and which
3999 // are to be smoothed using the second UV ( uvMap2 )
4000 // -----------------------------------------------------
4002 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4003 if ( !surface.IsNull() ) {
4004 TopExp_Explorer eExp( face, TopAbs_EDGE );
4005 for ( ; eExp.More(); eExp.Next() ) {
4006 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4007 if ( !BRep_Tool::IsClosed( edge, face ))
4009 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4010 if ( !sm ) continue;
4011 // find out which parameter varies for a node on seam
4014 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4015 if ( pcurve.IsNull() ) continue;
4016 uv1 = pcurve->Value( f );
4018 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4019 if ( pcurve.IsNull() ) continue;
4020 uv2 = pcurve->Value( f );
4021 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4023 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4024 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4026 // get nodes on seam and its vertices
4027 list< const SMDS_MeshNode* > seamNodes;
4028 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4029 while ( nSeamIt->more() ) {
4030 const SMDS_MeshNode* node = nSeamIt->next();
4031 if ( !isQuadratic || !IsMedium( node ))
4032 seamNodes.push_back( node );
4034 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4035 for ( ; vExp.More(); vExp.Next() ) {
4036 sm = aMesh->MeshElements( vExp.Current() );
4038 nSeamIt = sm->GetNodes();
4039 while ( nSeamIt->more() )
4040 seamNodes.push_back( nSeamIt->next() );
4043 // loop on nodes on seam
4044 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4045 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4046 const SMDS_MeshNode* nSeam = *noSeIt;
4047 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4048 if ( n_uv == uvMap.end() )
4051 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4052 // set the second UV
4053 listUV.push_back( *n_uv->second );
4054 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4055 if ( uvMap2.empty() )
4056 uvMap2 = uvMap; // copy the uvMap contents
4057 uvMap2[ nSeam ] = &listUV.back();
4059 // collect movable nodes linked to ones on seam in nodesNearSeam
4060 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4061 while ( eIt->more() ) {
4062 const SMDS_MeshElement* e = eIt->next();
4063 int nbUseMap1 = 0, nbUseMap2 = 0;
4064 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4065 int nn = 0, nbn = e->NbNodes();
4066 if(e->IsQuadratic()) nbn = nbn/2;
4067 while ( nn++ < nbn )
4069 const SMDS_MeshNode* n =
4070 static_cast<const SMDS_MeshNode*>( nIt->next() );
4072 setMovableNodes.find( n ) == setMovableNodes.end() )
4074 // add only nodes being closer to uv2 than to uv1
4075 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4076 0.5 * ( n->Y() + nSeam->Y() ),
4077 0.5 * ( n->Z() + nSeam->Z() ));
4079 getClosestUV( projector, pMid, uv );
4080 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4081 nodesNearSeam.insert( n );
4087 // for centroidalSmooth all element nodes must
4088 // be on one side of a seam
4089 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4090 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4092 while ( nn++ < nbn ) {
4093 const SMDS_MeshNode* n =
4094 static_cast<const SMDS_MeshNode*>( nIt->next() );
4095 setMovableNodes.erase( n );
4099 } // loop on nodes on seam
4100 } // loop on edge of a face
4101 } // if ( !face.IsNull() )
4103 if ( setMovableNodes.empty() ) {
4104 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4105 continue; // goto next face
4113 double maxRatio = -1., maxDisplacement = -1.;
4114 set<const SMDS_MeshNode*>::iterator nodeToMove;
4115 for ( it = 0; it < theNbIterations; it++ ) {
4116 maxDisplacement = 0.;
4117 nodeToMove = setMovableNodes.begin();
4118 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4119 const SMDS_MeshNode* node = (*nodeToMove);
4120 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4123 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4124 if ( theSmoothMethod == LAPLACIAN )
4125 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4127 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4129 // node displacement
4130 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4131 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4132 if ( aDispl > maxDisplacement )
4133 maxDisplacement = aDispl;
4135 // no node movement => exit
4136 //if ( maxDisplacement < 1.e-16 ) {
4137 if ( maxDisplacement < disttol ) {
4138 MESSAGE("-- no node movement --");
4142 // check elements quality
4144 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4145 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4146 const SMDS_MeshElement* elem = (*elemIt);
4147 if ( !elem || elem->GetType() != SMDSAbs_Face )
4149 SMESH::Controls::TSequenceOfXYZ aPoints;
4150 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4151 double aValue = aQualityFunc.GetValue( aPoints );
4152 if ( aValue > maxRatio )
4156 if ( maxRatio <= theTgtAspectRatio ) {
4157 MESSAGE("-- quality achived --");
4160 if (it+1 == theNbIterations) {
4161 MESSAGE("-- Iteration limit exceeded --");
4163 } // smoothing iterations
4165 MESSAGE(" Face id: " << *fId <<
4166 " Nb iterstions: " << it <<
4167 " Displacement: " << maxDisplacement <<
4168 " Aspect Ratio " << maxRatio);
4170 // ---------------------------------------
4171 // new nodes positions are computed,
4172 // record movement in DS and set new UV
4173 // ---------------------------------------
4174 nodeToMove = setMovableNodes.begin();
4175 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4176 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4177 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4178 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4179 if ( node_uv != uvMap.end() ) {
4180 gp_XY* uv = node_uv->second;
4182 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4186 // move medium nodes of quadratic elements
4189 SMESH_MesherHelper helper( *GetMesh() );
4190 helper.SetSubShape( face );
4191 vector<const SMDS_MeshNode*> nodes;
4193 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4194 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4196 const SMDS_MeshElement* QF = *elemIt;
4197 if ( QF->IsQuadratic() )
4199 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4200 SMDS_MeshElement::iterator() );
4201 nodes.push_back( nodes[0] );
4203 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4205 if ( !surface.IsNull() )
4207 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4208 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4209 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4210 xyz = surface->Value( uv.X(), uv.Y() );
4213 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4215 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4216 // we have to move a medium node
4217 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4223 } // loop on face ids
4227 //=======================================================================
4228 //function : isReverse
4229 //purpose : Return true if normal of prevNodes is not co-directied with
4230 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4231 // iNotSame is where prevNodes and nextNodes are different.
4232 // If result is true then future volume orientation is OK
4233 //=======================================================================
4235 static bool isReverse(const SMDS_MeshElement* face,
4236 const vector<const SMDS_MeshNode*>& prevNodes,
4237 const vector<const SMDS_MeshNode*>& nextNodes,
4241 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4242 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4243 gp_XYZ extrDir( pN - pP ), faceNorm;
4244 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4246 return faceNorm * extrDir < 0.0;
4249 //=======================================================================
4251 * \brief Create elements by sweeping an element
4252 * \param elem - element to sweep
4253 * \param newNodesItVec - nodes generated from each node of the element
4254 * \param newElems - generated elements
4255 * \param nbSteps - number of sweeping steps
4256 * \param srcElements - to append elem for each generated element
4258 //=======================================================================
4260 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4261 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4262 list<const SMDS_MeshElement*>& newElems,
4264 SMESH_SequenceOfElemPtr& srcElements)
4266 //MESSAGE("sweepElement " << nbSteps);
4267 SMESHDS_Mesh* aMesh = GetMeshDS();
4269 const int nbNodes = elem->NbNodes();
4270 const int nbCorners = elem->NbCornerNodes();
4271 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4272 polyhedron creation !!! */
4273 // Loop on elem nodes:
4274 // find new nodes and detect same nodes indices
4275 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4276 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4277 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4278 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4280 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4281 vector<int> sames(nbNodes);
4282 vector<bool> isSingleNode(nbNodes);
4284 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4285 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4286 const SMDS_MeshNode* node = nnIt->first;
4287 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4288 if ( listNewNodes.empty() )
4291 itNN [ iNode ] = listNewNodes.begin();
4292 prevNod[ iNode ] = node;
4293 nextNod[ iNode ] = listNewNodes.front();
4295 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4296 corner node of linear */
4297 if ( prevNod[ iNode ] != nextNod [ iNode ])
4298 nbDouble += !isSingleNode[iNode];
4300 if( iNode < nbCorners ) { // check corners only
4301 if ( prevNod[ iNode ] == nextNod [ iNode ])
4302 sames[nbSame++] = iNode;
4304 iNotSameNode = iNode;
4308 if ( nbSame == nbNodes || nbSame > 2) {
4309 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4313 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4315 // fix nodes order to have bottom normal external
4316 if ( baseType == SMDSEntity_Polygon )
4318 std::reverse( itNN.begin(), itNN.end() );
4319 std::reverse( prevNod.begin(), prevNod.end() );
4320 std::reverse( midlNod.begin(), midlNod.end() );
4321 std::reverse( nextNod.begin(), nextNod.end() );
4322 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4326 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4327 SMDS_MeshCell::applyInterlace( ind, itNN );
4328 SMDS_MeshCell::applyInterlace( ind, prevNod );
4329 SMDS_MeshCell::applyInterlace( ind, nextNod );
4330 SMDS_MeshCell::applyInterlace( ind, midlNod );
4331 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4334 sames[nbSame] = iNotSameNode;
4335 for ( int j = 0; j <= nbSame; ++j )
4336 for ( size_t i = 0; i < ind.size(); ++i )
4337 if ( ind[i] == sames[j] )
4342 iNotSameNode = sames[nbSame];
4347 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4349 iSameNode = sames[ nbSame-1 ];
4350 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4351 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4352 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4355 // make new elements
4356 for (int iStep = 0; iStep < nbSteps; iStep++ )
4359 for ( iNode = 0; iNode < nbNodes; iNode++ )
4361 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4362 nextNod[ iNode ] = *itNN[ iNode ]++;
4365 SMDS_MeshElement* aNewElem = 0;
4366 /*if(!elem->IsPoly())*/ {
4367 switch ( baseType ) {
4369 case SMDSEntity_Node: { // sweep NODE
4370 if ( nbSame == 0 ) {
4371 if ( isSingleNode[0] )
4372 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4374 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4380 case SMDSEntity_Edge: { // sweep EDGE
4381 if ( nbDouble == 0 )
4383 if ( nbSame == 0 ) // ---> quadrangle
4384 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4385 nextNod[ 1 ], nextNod[ 0 ] );
4386 else // ---> triangle
4387 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4388 nextNod[ iNotSameNode ] );
4390 else // ---> polygon
4392 vector<const SMDS_MeshNode*> poly_nodes;
4393 poly_nodes.push_back( prevNod[0] );
4394 poly_nodes.push_back( prevNod[1] );
4395 if ( prevNod[1] != nextNod[1] )
4397 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4398 poly_nodes.push_back( nextNod[1] );
4400 if ( prevNod[0] != nextNod[0] )
4402 poly_nodes.push_back( nextNod[0] );
4403 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4405 switch ( poly_nodes.size() ) {
4407 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4410 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4411 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4414 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4419 case SMDSEntity_Triangle: // TRIANGLE --->
4421 if ( nbDouble > 0 ) break;
4422 if ( nbSame == 0 ) // ---> pentahedron
4423 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4424 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4426 else if ( nbSame == 1 ) // ---> pyramid
4427 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4428 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4429 nextNod[ iSameNode ]);
4431 else // 2 same nodes: ---> tetrahedron
4432 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4433 nextNod[ iNotSameNode ]);
4436 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4440 if ( nbDouble+nbSame == 2 )
4442 if(nbSame==0) { // ---> quadratic quadrangle
4443 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4444 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4446 else { //(nbSame==1) // ---> quadratic triangle
4448 return; // medium node on axis
4450 else if(sames[0]==0)
4451 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4452 nextNod[2], midlNod[1], prevNod[2]);
4454 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4455 midlNod[0], nextNod[2], prevNod[2]);
4458 else if ( nbDouble == 3 )
4460 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4461 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4462 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4469 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4470 if ( nbDouble > 0 ) break;
4472 if ( nbSame == 0 ) // ---> hexahedron
4473 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4474 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4476 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4477 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4478 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4479 nextNod[ iSameNode ]);
4480 newElems.push_back( aNewElem );
4481 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4482 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4483 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4485 else if ( nbSame == 2 ) { // ---> pentahedron
4486 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4487 // iBeforeSame is same too
4488 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4489 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4490 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4492 // iAfterSame is same too
4493 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4494 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4495 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4499 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4500 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4501 if ( nbDouble+nbSame != 3 ) break;
4503 // ---> pentahedron with 15 nodes
4504 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4505 nextNod[0], nextNod[1], nextNod[2],
4506 prevNod[3], prevNod[4], prevNod[5],
4507 nextNod[3], nextNod[4], nextNod[5],
4508 midlNod[0], midlNod[1], midlNod[2]);
4510 else if(nbSame==1) {
4511 // ---> 2d order pyramid of 13 nodes
4512 int apex = iSameNode;
4513 int i0 = ( apex + 1 ) % nbCorners;
4514 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4518 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4519 nextNod[i0], nextNod[i1], prevNod[apex],
4520 prevNod[i01], midlNod[i0],
4521 nextNod[i01], midlNod[i1],
4522 prevNod[i1a], prevNod[i0a],
4523 nextNod[i0a], nextNod[i1a]);
4525 else if(nbSame==2) {
4526 // ---> 2d order tetrahedron of 10 nodes
4527 int n1 = iNotSameNode;
4528 int n2 = ( n1 + 1 ) % nbCorners;
4529 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4533 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4534 prevNod[n12], prevNod[n23], prevNod[n31],
4535 midlNod[n1], nextNod[n12], nextNod[n31]);
4539 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4541 if ( nbDouble != 4 ) break;
4542 // ---> hexahedron with 20 nodes
4543 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4544 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4545 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4546 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4547 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4549 else if(nbSame==1) {
4550 // ---> pyramid + pentahedron - can not be created since it is needed
4551 // additional middle node at the center of face
4552 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4555 else if( nbSame == 2 ) {
4556 if ( nbDouble != 2 ) break;
4557 // ---> 2d order Pentahedron with 15 nodes
4559 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4560 // iBeforeSame is same too
4567 // iAfterSame is same too
4577 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4578 prevNod[n4], prevNod[n5], nextNod[n5],
4579 prevNod[n12], midlNod[n2], nextNod[n12],
4580 prevNod[n45], midlNod[n5], nextNod[n45],
4581 prevNod[n14], prevNod[n25], nextNod[n25]);
4585 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4587 if( nbSame == 0 && nbDouble == 9 ) {
4588 // ---> tri-quadratic hexahedron with 27 nodes
4589 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4590 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4591 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4592 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4593 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4594 prevNod[8], // bottom center
4595 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4596 nextNod[8], // top center
4597 midlNod[8]);// elem center
4605 case SMDSEntity_Polygon: { // sweep POLYGON
4607 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4608 // ---> hexagonal prism
4609 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4610 prevNod[3], prevNod[4], prevNod[5],
4611 nextNod[0], nextNod[1], nextNod[2],
4612 nextNod[3], nextNod[4], nextNod[5]);
4616 case SMDSEntity_Ball:
4624 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4626 if ( baseType != SMDSEntity_Polygon )
4628 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4629 SMDS_MeshCell::applyInterlace( ind, prevNod );
4630 SMDS_MeshCell::applyInterlace( ind, nextNod );
4631 SMDS_MeshCell::applyInterlace( ind, midlNod );
4632 SMDS_MeshCell::applyInterlace( ind, itNN );
4633 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4634 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4636 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4637 vector<int> quantities (nbNodes + 2);
4638 polyedre_nodes.clear();
4642 for (int inode = 0; inode < nbNodes; inode++)
4643 polyedre_nodes.push_back( prevNod[inode] );
4644 quantities.push_back( nbNodes );
4647 polyedre_nodes.push_back( nextNod[0] );
4648 for (int inode = nbNodes; inode-1; --inode )
4649 polyedre_nodes.push_back( nextNod[inode-1] );
4650 quantities.push_back( nbNodes );
4653 for (int iface = 0; iface < nbNodes; iface++)
4655 const int prevNbNodes = polyedre_nodes.size();
4656 int inextface = (iface+1) % nbNodes;
4657 polyedre_nodes.push_back( prevNod[inextface] );
4658 polyedre_nodes.push_back( prevNod[iface] );
4659 if ( prevNod[iface] != nextNod[iface] )
4661 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4662 polyedre_nodes.push_back( nextNod[iface] );
4664 if ( prevNod[inextface] != nextNod[inextface] )
4666 polyedre_nodes.push_back( nextNod[inextface] );
4667 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4669 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4670 if ( nbFaceNodes > 2 )
4671 quantities.push_back( nbFaceNodes );
4672 else // degenerated face
4673 polyedre_nodes.resize( prevNbNodes );
4675 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4679 newElems.push_back( aNewElem );
4680 myLastCreatedElems.Append(aNewElem);
4681 srcElements.Append( elem );
4684 // set new prev nodes
4685 for ( iNode = 0; iNode < nbNodes; iNode++ )
4686 prevNod[ iNode ] = nextNod[ iNode ];
4691 //=======================================================================
4693 * \brief Create 1D and 2D elements around swept elements
4694 * \param mapNewNodes - source nodes and ones generated from them
4695 * \param newElemsMap - source elements and ones generated from them
4696 * \param elemNewNodesMap - nodes generated from each node of each element
4697 * \param elemSet - all swept elements
4698 * \param nbSteps - number of sweeping steps
4699 * \param srcElements - to append elem for each generated element
4701 //=======================================================================
4703 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4704 TTElemOfElemListMap & newElemsMap,
4705 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4706 TIDSortedElemSet& elemSet,
4708 SMESH_SequenceOfElemPtr& srcElements)
4710 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4711 SMESHDS_Mesh* aMesh = GetMeshDS();
4713 // Find nodes belonging to only one initial element - sweep them into edges.
4715 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4716 for ( ; nList != mapNewNodes.end(); nList++ )
4718 const SMDS_MeshNode* node =
4719 static_cast<const SMDS_MeshNode*>( nList->first );
4720 if ( newElemsMap.count( node ))
4721 continue; // node was extruded into edge
4722 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4723 int nbInitElems = 0;
4724 const SMDS_MeshElement* el = 0;
4725 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4726 while ( eIt->more() && nbInitElems < 2 ) {
4728 SMDSAbs_ElementType type = el->GetType();
4729 if ( type == SMDSAbs_Volume || type < highType ) continue;
4730 if ( type > highType ) {
4734 nbInitElems += elemSet.count(el);
4736 if ( nbInitElems < 2 ) {
4737 bool NotCreateEdge = el && el->IsMediumNode(node);
4738 if(!NotCreateEdge) {
4739 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4740 list<const SMDS_MeshElement*> newEdges;
4741 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4746 // Make a ceiling for each element ie an equal element of last new nodes.
4747 // Find free links of faces - make edges and sweep them into faces.
4749 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4750 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4751 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4753 const SMDS_MeshElement* elem = itElem->first;
4754 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4756 if(itElem->second.size()==0) continue;
4758 const bool isQuadratic = elem->IsQuadratic();
4760 if ( elem->GetType() == SMDSAbs_Edge ) {
4761 // create a ceiling edge
4762 if ( !isQuadratic ) {
4763 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4764 vecNewNodes[ 1 ]->second.back())) {
4765 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4766 vecNewNodes[ 1 ]->second.back()));
4767 srcElements.Append( elem );
4771 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4772 vecNewNodes[ 1 ]->second.back(),
4773 vecNewNodes[ 2 ]->second.back())) {
4774 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4775 vecNewNodes[ 1 ]->second.back(),
4776 vecNewNodes[ 2 ]->second.back()));
4777 srcElements.Append( elem );
4781 if ( elem->GetType() != SMDSAbs_Face )
4784 bool hasFreeLinks = false;
4786 TIDSortedElemSet avoidSet;
4787 avoidSet.insert( elem );
4789 set<const SMDS_MeshNode*> aFaceLastNodes;
4790 int iNode, nbNodes = vecNewNodes.size();
4791 if ( !isQuadratic ) {
4792 // loop on the face nodes
4793 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4794 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4795 // look for free links of the face
4796 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4797 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4798 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4799 // check if a link n1-n2 is free
4800 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4801 hasFreeLinks = true;
4802 // make a new edge and a ceiling for a new edge
4803 const SMDS_MeshElement* edge;
4804 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4805 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4806 srcElements.Append( myLastCreatedElems.Last() );
4808 n1 = vecNewNodes[ iNode ]->second.back();
4809 n2 = vecNewNodes[ iNext ]->second.back();
4810 if ( !aMesh->FindEdge( n1, n2 )) {
4811 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4812 srcElements.Append( edge );
4817 else { // elem is quadratic face
4818 int nbn = nbNodes/2;
4819 for ( iNode = 0; iNode < nbn; iNode++ ) {
4820 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4821 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4822 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4823 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4824 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4825 // check if a link is free
4826 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4827 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4828 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4829 hasFreeLinks = true;
4830 // make an edge and a ceiling for a new edge
4832 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4833 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4834 srcElements.Append( elem );
4836 n1 = vecNewNodes[ iNode ]->second.back();
4837 n2 = vecNewNodes[ iNext ]->second.back();
4838 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4839 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4840 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4841 srcElements.Append( elem );
4845 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4846 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4850 // sweep free links into faces
4852 if ( hasFreeLinks ) {
4853 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4854 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4856 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4857 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4858 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4859 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4860 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4862 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4863 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4864 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4866 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4867 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4868 std::advance( v, volNb );
4869 // find indices of free faces of a volume and their source edges
4870 list< int > freeInd;
4871 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4872 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4873 int iF, nbF = vTool.NbFaces();
4874 for ( iF = 0; iF < nbF; iF ++ ) {
4875 if (vTool.IsFreeFace( iF ) &&
4876 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4877 initNodeSet != faceNodeSet) // except an initial face
4879 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4881 if ( faceNodeSet == initNodeSetNoCenter )
4883 freeInd.push_back( iF );
4884 // find source edge of a free face iF
4885 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4886 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4887 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4888 initNodeSet.begin(), initNodeSet.end(),
4889 commonNodes.begin());
4890 if ( (*v)->IsQuadratic() )
4891 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4893 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4895 if ( !srcEdges.back() )
4897 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4898 << iF << " of volume #" << vTool.ID() << endl;
4903 if ( freeInd.empty() )
4906 // create faces for all steps;
4907 // if such a face has been already created by sweep of edge,
4908 // assure that its orientation is OK
4909 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4910 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4911 vTool.SetExternalNormal();
4912 const int nextShift = vTool.IsForward() ? +1 : -1;
4913 list< int >::iterator ind = freeInd.begin();
4914 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4915 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4917 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4918 int nbn = vTool.NbFaceNodes( *ind );
4919 const SMDS_MeshElement * f = 0;
4920 if ( nbn == 3 ) ///// triangle
4922 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4924 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4926 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4928 nodes[ 1 + nextShift ] };
4930 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4936 else if ( nbn == 4 ) ///// quadrangle
4938 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4940 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4942 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4943 nodes[ 2 ], nodes[ 2+nextShift ] };
4945 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4947 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4948 newOrder[ 2 ], newOrder[ 3 ]));
4951 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4953 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4955 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4957 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4959 nodes[2 + 2*nextShift],
4960 nodes[3 - 2*nextShift],
4962 nodes[3 + 2*nextShift]};
4964 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4966 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4974 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4976 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4977 nodes[1], nodes[3], nodes[5], nodes[7] );
4979 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4981 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4982 nodes[4 - 2*nextShift],
4984 nodes[4 + 2*nextShift],
4986 nodes[5 - 2*nextShift],
4988 nodes[5 + 2*nextShift] };
4990 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4992 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4993 newOrder[ 2 ], newOrder[ 3 ],
4994 newOrder[ 4 ], newOrder[ 5 ],
4995 newOrder[ 6 ], newOrder[ 7 ]));
4998 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5000 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5001 SMDSAbs_Face, /*noMedium=*/false);
5003 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5005 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5006 nodes[4 - 2*nextShift],
5008 nodes[4 + 2*nextShift],
5010 nodes[5 - 2*nextShift],
5012 nodes[5 + 2*nextShift],
5015 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5017 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5018 newOrder[ 2 ], newOrder[ 3 ],
5019 newOrder[ 4 ], newOrder[ 5 ],
5020 newOrder[ 6 ], newOrder[ 7 ],
5024 else //////// polygon
5026 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5027 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5029 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5031 if ( !vTool.IsForward() )
5032 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5034 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5036 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5040 while ( srcElements.Length() < myLastCreatedElems.Length() )
5041 srcElements.Append( *srcEdge );
5043 } // loop on free faces
5045 // go to the next volume
5047 while ( iVol++ < nbVolumesByStep ) v++;
5050 } // loop on volumes of one step
5051 } // sweep free links into faces
5053 // Make a ceiling face with a normal external to a volume
5055 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5056 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5057 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5059 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5060 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5061 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5064 lastVol.SetExternalNormal();
5065 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5066 int nbn = lastVol.NbFaceNodes( iF );
5067 // we do not use this->AddElement() because nodes are interlaced
5068 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5069 if ( !hasFreeLinks ||
5070 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5073 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5075 else if ( nbn == 4 )
5076 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5078 else if ( nbn == 6 && isQuadratic )
5079 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5080 nodes[1], nodes[3], nodes[5]));
5081 else if ( nbn == 7 && isQuadratic )
5082 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5083 nodes[1], nodes[3], nodes[5], nodes[6]));
5084 else if ( nbn == 8 && isQuadratic )
5085 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5086 nodes[1], nodes[3], nodes[5], nodes[7]));
5087 else if ( nbn == 9 && isQuadratic )
5088 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5089 nodes[1], nodes[3], nodes[5], nodes[7],
5092 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5094 while ( srcElements.Length() < myLastCreatedElems.Length() )
5095 srcElements.Append( elem );
5098 } // loop on swept elements
5101 //=======================================================================
5102 //function : RotationSweep
5104 //=======================================================================
5106 SMESH_MeshEditor::PGroupIDs
5107 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5108 const gp_Ax1& theAxis,
5109 const double theAngle,
5110 const int theNbSteps,
5111 const double theTol,
5112 const bool theMakeGroups,
5113 const bool theMakeWalls)
5115 myLastCreatedElems.Clear();
5116 myLastCreatedNodes.Clear();
5118 // source elements for each generated one
5119 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5121 MESSAGE( "RotationSweep()");
5123 aTrsf.SetRotation( theAxis, theAngle );
5125 aTrsf2.SetRotation( theAxis, theAngle/2. );
5127 gp_Lin aLine( theAxis );
5128 double aSqTol = theTol * theTol;
5130 SMESHDS_Mesh* aMesh = GetMeshDS();
5132 TNodeOfNodeListMap mapNewNodes;
5133 TElemOfVecOfNnlmiMap mapElemNewNodes;
5134 TTElemOfElemListMap newElemsMap;
5136 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5137 myMesh->NbFaces(ORDER_QUADRATIC) +
5138 myMesh->NbVolumes(ORDER_QUADRATIC) );
5140 TIDSortedElemSet::iterator itElem;
5141 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5142 const SMDS_MeshElement* elem = *itElem;
5143 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5145 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5146 newNodesItVec.reserve( elem->NbNodes() );
5148 // loop on elem nodes
5149 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5150 while ( itN->more() )
5152 // check if a node has been already sweeped
5153 const SMDS_MeshNode* node = cast2Node( itN->next() );
5155 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5157 aXYZ.Coord( coord[0], coord[1], coord[2] );
5158 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5160 TNodeOfNodeListMapItr nIt =
5161 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5162 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5163 if ( listNewNodes.empty() )
5165 // check if we are to create medium nodes between corner ones
5166 bool needMediumNodes = false;
5167 if ( isQuadraticMesh )
5169 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5170 while (it->more() && !needMediumNodes )
5172 const SMDS_MeshElement* invElem = it->next();
5173 if ( invElem != elem && !theElems.count( invElem )) continue;
5174 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5175 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5176 needMediumNodes = true;
5181 const SMDS_MeshNode * newNode = node;
5182 for ( int i = 0; i < theNbSteps; i++ ) {
5184 if ( needMediumNodes ) // create a medium node
5186 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5187 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5188 myLastCreatedNodes.Append(newNode);
5189 srcNodes.Append( node );
5190 listNewNodes.push_back( newNode );
5191 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5194 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5196 // create a corner node
5197 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5198 myLastCreatedNodes.Append(newNode);
5199 srcNodes.Append( node );
5200 listNewNodes.push_back( newNode );
5203 listNewNodes.push_back( newNode );
5204 // if ( needMediumNodes )
5205 // listNewNodes.push_back( newNode );
5209 newNodesItVec.push_back( nIt );
5211 // make new elements
5212 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5216 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5218 PGroupIDs newGroupIDs;
5219 if ( theMakeGroups )
5220 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5226 //=======================================================================
5227 //function : CreateNode
5229 //=======================================================================
5230 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5233 const double tolnode,
5234 SMESH_SequenceOfNode& aNodes)
5236 // myLastCreatedElems.Clear();
5237 // myLastCreatedNodes.Clear();
5240 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5242 // try to search in sequence of existing nodes
5243 // if aNodes.Length()>0 we 'nave to use given sequence
5244 // else - use all nodes of mesh
5245 if(aNodes.Length()>0) {
5247 for(i=1; i<=aNodes.Length(); i++) {
5248 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5249 if(P1.Distance(P2)<tolnode)
5250 return aNodes.Value(i);
5254 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5255 while(itn->more()) {
5256 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5257 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5258 if(P1.Distance(P2)<tolnode)
5263 // create new node and return it
5264 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5265 //myLastCreatedNodes.Append(NewNode);
5270 //=======================================================================
5271 //function : ExtrusionSweep
5273 //=======================================================================
5275 SMESH_MeshEditor::PGroupIDs
5276 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5277 const gp_Vec& theStep,
5278 const int theNbSteps,
5279 TTElemOfElemListMap& newElemsMap,
5280 const bool theMakeGroups,
5282 const double theTolerance)
5284 ExtrusParam aParams;
5285 aParams.myDir = gp_Dir(theStep);
5286 aParams.myNodes.Clear();
5287 aParams.mySteps = new TColStd_HSequenceOfReal;
5289 for(i=1; i<=theNbSteps; i++)
5290 aParams.mySteps->Append(theStep.Magnitude());
5293 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5297 //=======================================================================
5298 //function : ExtrusionSweep
5300 //=======================================================================
5302 SMESH_MeshEditor::PGroupIDs
5303 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5304 ExtrusParam& theParams,
5305 TTElemOfElemListMap& newElemsMap,
5306 const bool theMakeGroups,
5308 const double theTolerance)
5310 myLastCreatedElems.Clear();
5311 myLastCreatedNodes.Clear();
5313 // source elements for each generated one
5314 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5316 SMESHDS_Mesh* aMesh = GetMeshDS();
5318 int nbsteps = theParams.mySteps->Length();
5320 TNodeOfNodeListMap mapNewNodes;
5321 //TNodeOfNodeVecMap mapNewNodes;
5322 TElemOfVecOfNnlmiMap mapElemNewNodes;
5323 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5325 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5326 myMesh->NbFaces(ORDER_QUADRATIC) +
5327 myMesh->NbVolumes(ORDER_QUADRATIC) );
5329 TIDSortedElemSet::iterator itElem;
5330 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5331 // check element type
5332 const SMDS_MeshElement* elem = *itElem;
5333 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5336 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5337 newNodesItVec.reserve( elem->NbNodes() );
5339 // loop on elem nodes
5340 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5341 while ( itN->more() )
5343 // check if a node has been already sweeped
5344 const SMDS_MeshNode* node = cast2Node( itN->next() );
5345 TNodeOfNodeListMap::iterator nIt =
5346 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5347 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5348 if ( listNewNodes.empty() )
5352 // check if we are to create medium nodes between corner ones
5353 bool needMediumNodes = false;
5354 if ( isQuadraticMesh )
5356 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5357 while (it->more() && !needMediumNodes )
5359 const SMDS_MeshElement* invElem = it->next();
5360 if ( invElem != elem && !theElems.count( invElem )) continue;
5361 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5362 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5363 needMediumNodes = true;
5367 double coord[] = { node->X(), node->Y(), node->Z() };
5368 for ( int i = 0; i < nbsteps; i++ )
5370 if ( needMediumNodes ) // create a medium node
5372 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5373 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5374 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5375 if( theFlags & EXTRUSION_FLAG_SEW ) {
5376 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5377 theTolerance, theParams.myNodes);
5378 listNewNodes.push_back( newNode );
5381 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5382 myLastCreatedNodes.Append(newNode);
5383 srcNodes.Append( node );
5384 listNewNodes.push_back( newNode );
5387 // create a corner node
5388 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5389 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5390 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5391 if( theFlags & EXTRUSION_FLAG_SEW ) {
5392 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5393 theTolerance, theParams.myNodes);
5394 listNewNodes.push_back( newNode );
5397 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5398 myLastCreatedNodes.Append(newNode);
5399 srcNodes.Append( node );
5400 listNewNodes.push_back( newNode );
5404 newNodesItVec.push_back( nIt );
5406 // make new elements
5407 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5410 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5411 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5413 PGroupIDs newGroupIDs;
5414 if ( theMakeGroups )
5415 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5420 //=======================================================================
5421 //function : ExtrusionAlongTrack
5423 //=======================================================================
5424 SMESH_MeshEditor::Extrusion_Error
5425 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5426 SMESH_subMesh* theTrack,
5427 const SMDS_MeshNode* theN1,
5428 const bool theHasAngles,
5429 list<double>& theAngles,
5430 const bool theLinearVariation,
5431 const bool theHasRefPoint,
5432 const gp_Pnt& theRefPoint,
5433 const bool theMakeGroups)
5435 MESSAGE("ExtrusionAlongTrack");
5436 myLastCreatedElems.Clear();
5437 myLastCreatedNodes.Clear();
5440 std::list<double> aPrms;
5441 TIDSortedElemSet::iterator itElem;
5444 TopoDS_Edge aTrackEdge;
5445 TopoDS_Vertex aV1, aV2;
5447 SMDS_ElemIteratorPtr aItE;
5448 SMDS_NodeIteratorPtr aItN;
5449 SMDSAbs_ElementType aTypeE;
5451 TNodeOfNodeListMap mapNewNodes;
5454 aNbE = theElements.size();
5457 return EXTR_NO_ELEMENTS;
5459 // 1.1 Track Pattern
5462 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5464 aItE = pSubMeshDS->GetElements();
5465 while ( aItE->more() ) {
5466 const SMDS_MeshElement* pE = aItE->next();
5467 aTypeE = pE->GetType();
5468 // Pattern must contain links only
5469 if ( aTypeE != SMDSAbs_Edge )
5470 return EXTR_PATH_NOT_EDGE;
5473 list<SMESH_MeshEditor_PathPoint> fullList;
5475 const TopoDS_Shape& aS = theTrack->GetSubShape();
5476 // Sub-shape for the Pattern must be an Edge or Wire
5477 if( aS.ShapeType() == TopAbs_EDGE ) {
5478 aTrackEdge = TopoDS::Edge( aS );
5479 // the Edge must not be degenerated
5480 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5481 return EXTR_BAD_PATH_SHAPE;
5482 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5483 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5484 const SMDS_MeshNode* aN1 = aItN->next();
5485 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5486 const SMDS_MeshNode* aN2 = aItN->next();
5487 // starting node must be aN1 or aN2
5488 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5489 return EXTR_BAD_STARTING_NODE;
5490 aItN = pSubMeshDS->GetNodes();
5491 while ( aItN->more() ) {
5492 const SMDS_MeshNode* pNode = aItN->next();
5493 const SMDS_EdgePosition* pEPos =
5494 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5495 double aT = pEPos->GetUParameter();
5496 aPrms.push_back( aT );
5498 //Extrusion_Error err =
5499 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5500 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5501 list< SMESH_subMesh* > LSM;
5502 TopTools_SequenceOfShape Edges;
5503 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5504 while(itSM->more()) {
5505 SMESH_subMesh* SM = itSM->next();
5507 const TopoDS_Shape& aS = SM->GetSubShape();
5510 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5511 int startNid = theN1->GetID();
5512 TColStd_MapOfInteger UsedNums;
5514 int NbEdges = Edges.Length();
5516 for(; i<=NbEdges; i++) {
5518 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5519 for(; itLSM!=LSM.end(); itLSM++) {
5521 if(UsedNums.Contains(k)) continue;
5522 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5523 SMESH_subMesh* locTrack = *itLSM;
5524 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5525 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5526 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5527 const SMDS_MeshNode* aN1 = aItN->next();
5528 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5529 const SMDS_MeshNode* aN2 = aItN->next();
5530 // starting node must be aN1 or aN2
5531 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5532 // 2. Collect parameters on the track edge
5534 aItN = locMeshDS->GetNodes();
5535 while ( aItN->more() ) {
5536 const SMDS_MeshNode* pNode = aItN->next();
5537 const SMDS_EdgePosition* pEPos =
5538 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5539 double aT = pEPos->GetUParameter();
5540 aPrms.push_back( aT );
5542 list<SMESH_MeshEditor_PathPoint> LPP;
5543 //Extrusion_Error err =
5544 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5545 LLPPs.push_back(LPP);
5547 // update startN for search following egde
5548 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5549 else startNid = aN1->GetID();
5553 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5554 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5555 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5556 for(; itPP!=firstList.end(); itPP++) {
5557 fullList.push_back( *itPP );
5559 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5560 fullList.pop_back();
5562 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5563 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5564 itPP = currList.begin();
5565 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5566 gp_Dir D1 = PP1.Tangent();
5567 gp_Dir D2 = PP2.Tangent();
5568 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5569 (D1.Z()+D2.Z())/2 ) );
5570 PP1.SetTangent(Dnew);
5571 fullList.push_back(PP1);
5573 for(; itPP!=firstList.end(); itPP++) {
5574 fullList.push_back( *itPP );
5576 PP1 = fullList.back();
5577 fullList.pop_back();
5579 // if wire not closed
5580 fullList.push_back(PP1);
5584 return EXTR_BAD_PATH_SHAPE;
5587 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5588 theHasRefPoint, theRefPoint, theMakeGroups);
5592 //=======================================================================
5593 //function : ExtrusionAlongTrack
5595 //=======================================================================
5596 SMESH_MeshEditor::Extrusion_Error
5597 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5598 SMESH_Mesh* theTrack,
5599 const SMDS_MeshNode* theN1,
5600 const bool theHasAngles,
5601 list<double>& theAngles,
5602 const bool theLinearVariation,
5603 const bool theHasRefPoint,
5604 const gp_Pnt& theRefPoint,
5605 const bool theMakeGroups)
5607 myLastCreatedElems.Clear();
5608 myLastCreatedNodes.Clear();
5611 std::list<double> aPrms;
5612 TIDSortedElemSet::iterator itElem;
5615 TopoDS_Edge aTrackEdge;
5616 TopoDS_Vertex aV1, aV2;
5618 SMDS_ElemIteratorPtr aItE;
5619 SMDS_NodeIteratorPtr aItN;
5620 SMDSAbs_ElementType aTypeE;
5622 TNodeOfNodeListMap mapNewNodes;
5625 aNbE = theElements.size();
5628 return EXTR_NO_ELEMENTS;
5630 // 1.1 Track Pattern
5633 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5635 aItE = pMeshDS->elementsIterator();
5636 while ( aItE->more() ) {
5637 const SMDS_MeshElement* pE = aItE->next();
5638 aTypeE = pE->GetType();
5639 // Pattern must contain links only
5640 if ( aTypeE != SMDSAbs_Edge )
5641 return EXTR_PATH_NOT_EDGE;
5644 list<SMESH_MeshEditor_PathPoint> fullList;
5646 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5648 if ( !theTrack->HasShapeToMesh() ) {
5649 //Mesh without shape
5650 const SMDS_MeshNode* currentNode = NULL;
5651 const SMDS_MeshNode* prevNode = theN1;
5652 std::vector<const SMDS_MeshNode*> aNodesList;
5653 aNodesList.push_back(theN1);
5654 int nbEdges = 0, conn=0;
5655 const SMDS_MeshElement* prevElem = NULL;
5656 const SMDS_MeshElement* currentElem = NULL;
5657 int totalNbEdges = theTrack->NbEdges();
5658 SMDS_ElemIteratorPtr nIt;
5661 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5662 return EXTR_BAD_STARTING_NODE;
5665 conn = nbEdgeConnectivity(theN1);
5667 return EXTR_PATH_NOT_EDGE;
5669 aItE = theN1->GetInverseElementIterator();
5670 prevElem = aItE->next();
5671 currentElem = prevElem;
5673 if(totalNbEdges == 1 ) {
5674 nIt = currentElem->nodesIterator();
5675 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5676 if(currentNode == prevNode)
5677 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5678 aNodesList.push_back(currentNode);
5680 nIt = currentElem->nodesIterator();
5681 while( nIt->more() ) {
5682 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5683 if(currentNode == prevNode)
5684 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5685 aNodesList.push_back(currentNode);
5687 //case of the closed mesh
5688 if(currentNode == theN1) {
5693 conn = nbEdgeConnectivity(currentNode);
5695 return EXTR_PATH_NOT_EDGE;
5696 }else if( conn == 1 && nbEdges > 0 ) {
5701 prevNode = currentNode;
5702 aItE = currentNode->GetInverseElementIterator();
5703 currentElem = aItE->next();
5704 if( currentElem == prevElem)
5705 currentElem = aItE->next();
5706 nIt = currentElem->nodesIterator();
5707 prevElem = currentElem;
5713 if(nbEdges != totalNbEdges)
5714 return EXTR_PATH_NOT_EDGE;
5716 TopTools_SequenceOfShape Edges;
5717 double x1,x2,y1,y2,z1,z2;
5718 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5719 int startNid = theN1->GetID();
5720 for(int i = 1; i < aNodesList.size(); i++) {
5721 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5722 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5723 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5724 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5725 list<SMESH_MeshEditor_PathPoint> LPP;
5727 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5728 LLPPs.push_back(LPP);
5729 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5730 else startNid = aNodesList[i-1]->GetID();
5734 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5735 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5736 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5737 for(; itPP!=firstList.end(); itPP++) {
5738 fullList.push_back( *itPP );
5741 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5742 SMESH_MeshEditor_PathPoint PP2;
5743 fullList.pop_back();
5745 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5746 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5747 itPP = currList.begin();
5748 PP2 = currList.front();
5749 gp_Dir D1 = PP1.Tangent();
5750 gp_Dir D2 = PP2.Tangent();
5751 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5752 (D1.Z()+D2.Z())/2 ) );
5753 PP1.SetTangent(Dnew);
5754 fullList.push_back(PP1);
5756 for(; itPP!=currList.end(); itPP++) {
5757 fullList.push_back( *itPP );
5759 PP1 = fullList.back();
5760 fullList.pop_back();
5762 fullList.push_back(PP1);
5764 } // Sub-shape for the Pattern must be an Edge or Wire
5765 else if( aS.ShapeType() == TopAbs_EDGE ) {
5766 aTrackEdge = TopoDS::Edge( aS );
5767 // the Edge must not be degenerated
5768 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5769 return EXTR_BAD_PATH_SHAPE;
5770 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5771 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5772 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5773 // starting node must be aN1 or aN2
5774 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5775 return EXTR_BAD_STARTING_NODE;
5776 aItN = pMeshDS->nodesIterator();
5777 while ( aItN->more() ) {
5778 const SMDS_MeshNode* pNode = aItN->next();
5779 if( pNode==aN1 || pNode==aN2 ) continue;
5780 const SMDS_EdgePosition* pEPos =
5781 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5782 double aT = pEPos->GetUParameter();
5783 aPrms.push_back( aT );
5785 //Extrusion_Error err =
5786 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5788 else if( aS.ShapeType() == TopAbs_WIRE ) {
5789 list< SMESH_subMesh* > LSM;
5790 TopTools_SequenceOfShape Edges;
5791 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5792 for(; eExp.More(); eExp.Next()) {
5793 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5794 if( SMESH_Algo::isDegenerated(E) ) continue;
5795 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5801 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5802 TopoDS_Vertex aVprev;
5803 TColStd_MapOfInteger UsedNums;
5804 int NbEdges = Edges.Length();
5806 for(; i<=NbEdges; i++) {
5808 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5809 for(; itLSM!=LSM.end(); itLSM++) {
5811 if(UsedNums.Contains(k)) continue;
5812 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5813 SMESH_subMesh* locTrack = *itLSM;
5814 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5815 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5816 bool aN1isOK = false, aN2isOK = false;
5817 if ( aVprev.IsNull() ) {
5818 // if previous vertex is not yet defined, it means that we in the beginning of wire
5819 // and we have to find initial vertex corresponding to starting node theN1
5820 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5821 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5822 // starting node must be aN1 or aN2
5823 aN1isOK = ( aN1 && aN1 == theN1 );
5824 aN2isOK = ( aN2 && aN2 == theN1 );
5827 // we have specified ending vertex of the previous edge on the previous iteration
5828 // and we have just to check that it corresponds to any vertex in current segment
5829 aN1isOK = aVprev.IsSame( aV1 );
5830 aN2isOK = aVprev.IsSame( aV2 );
5832 if ( !aN1isOK && !aN2isOK ) continue;
5833 // 2. Collect parameters on the track edge
5835 aItN = locMeshDS->GetNodes();
5836 while ( aItN->more() ) {
5837 const SMDS_MeshNode* pNode = aItN->next();
5838 const SMDS_EdgePosition* pEPos =
5839 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5840 double aT = pEPos->GetUParameter();
5841 aPrms.push_back( aT );
5843 list<SMESH_MeshEditor_PathPoint> LPP;
5844 //Extrusion_Error err =
5845 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5846 LLPPs.push_back(LPP);
5848 // update startN for search following egde
5849 if ( aN1isOK ) aVprev = aV2;
5854 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5855 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5856 fullList.splice( fullList.end(), firstList );
5858 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5859 fullList.pop_back();
5861 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5862 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5863 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5864 gp_Dir D1 = PP1.Tangent();
5865 gp_Dir D2 = PP2.Tangent();
5866 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5867 PP1.SetTangent(Dnew);
5868 fullList.push_back(PP1);
5869 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5870 PP1 = fullList.back();
5871 fullList.pop_back();
5873 // if wire not closed
5874 fullList.push_back(PP1);
5878 return EXTR_BAD_PATH_SHAPE;
5881 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5882 theHasRefPoint, theRefPoint, theMakeGroups);
5886 //=======================================================================
5887 //function : MakeEdgePathPoints
5888 //purpose : auxilary for ExtrusionAlongTrack
5889 //=======================================================================
5890 SMESH_MeshEditor::Extrusion_Error
5891 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5892 const TopoDS_Edge& aTrackEdge,
5894 list<SMESH_MeshEditor_PathPoint>& LPP)
5896 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5898 aTolVec2=aTolVec*aTolVec;
5900 TopoDS_Vertex aV1, aV2;
5901 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5902 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5903 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5904 // 2. Collect parameters on the track edge
5905 aPrms.push_front( aT1 );
5906 aPrms.push_back( aT2 );
5909 if( FirstIsStart ) {
5920 SMESH_MeshEditor_PathPoint aPP;
5921 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5922 std::list<double>::iterator aItD = aPrms.begin();
5923 for(; aItD != aPrms.end(); ++aItD) {
5927 aC3D->D1( aT, aP3D, aVec );
5928 aL2 = aVec.SquareMagnitude();
5929 if ( aL2 < aTolVec2 )
5930 return EXTR_CANT_GET_TANGENT;
5931 gp_Dir aTgt( aVec );
5933 aPP.SetTangent( aTgt );
5934 aPP.SetParameter( aT );
5941 //=======================================================================
5942 //function : MakeExtrElements
5943 //purpose : auxilary for ExtrusionAlongTrack
5944 //=======================================================================
5945 SMESH_MeshEditor::Extrusion_Error
5946 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5947 list<SMESH_MeshEditor_PathPoint>& fullList,
5948 const bool theHasAngles,
5949 list<double>& theAngles,
5950 const bool theLinearVariation,
5951 const bool theHasRefPoint,
5952 const gp_Pnt& theRefPoint,
5953 const bool theMakeGroups)
5955 const int aNbTP = fullList.size();
5957 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5958 LinearAngleVariation(aNbTP-1, theAngles);
5959 // fill vector of path points with angles
5960 vector<SMESH_MeshEditor_PathPoint> aPPs;
5961 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5962 list<double>::iterator itAngles = theAngles.begin();
5963 aPPs.push_back( *itPP++ );
5964 for( ; itPP != fullList.end(); itPP++) {
5965 aPPs.push_back( *itPP );
5966 if ( theHasAngles && itAngles != theAngles.end() )
5967 aPPs.back().SetAngle( *itAngles++ );
5970 TNodeOfNodeListMap mapNewNodes;
5971 TElemOfVecOfNnlmiMap mapElemNewNodes;
5972 TTElemOfElemListMap newElemsMap;
5973 TIDSortedElemSet::iterator itElem;
5974 // source elements for each generated one
5975 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5977 // 3. Center of rotation aV0
5978 gp_Pnt aV0 = theRefPoint;
5979 if ( !theHasRefPoint )
5981 gp_XYZ aGC( 0.,0.,0. );
5982 TIDSortedElemSet newNodes;
5984 itElem = theElements.begin();
5985 for ( ; itElem != theElements.end(); itElem++ ) {
5986 const SMDS_MeshElement* elem = *itElem;
5988 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5989 while ( itN->more() ) {
5990 const SMDS_MeshElement* node = itN->next();
5991 if ( newNodes.insert( node ).second )
5992 aGC += SMESH_TNodeXYZ( node );
5995 aGC /= newNodes.size();
5997 } // if (!theHasRefPoint) {
5999 // 4. Processing the elements
6000 SMESHDS_Mesh* aMesh = GetMeshDS();
6002 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6003 // check element type
6004 const SMDS_MeshElement* elem = *itElem;
6005 SMDSAbs_ElementType aTypeE = elem->GetType();
6006 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6009 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6010 newNodesItVec.reserve( elem->NbNodes() );
6012 // loop on elem nodes
6014 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6015 while ( itN->more() )
6018 // check if a node has been already processed
6019 const SMDS_MeshNode* node =
6020 static_cast<const SMDS_MeshNode*>( itN->next() );
6021 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6022 if ( nIt == mapNewNodes.end() ) {
6023 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6024 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6027 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6028 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6029 gp_Ax1 anAx1, anAxT1T0;
6030 gp_Dir aDT1x, aDT0x, aDT1T0;
6035 aPN0 = SMESH_TNodeXYZ( node );
6037 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6039 aDT0x= aPP0.Tangent();
6040 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6042 for ( int j = 1; j < aNbTP; ++j ) {
6043 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6045 aDT1x = aPP1.Tangent();
6046 aAngle1x = aPP1.Angle();
6048 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6050 gp_Vec aV01x( aP0x, aP1x );
6051 aTrsf.SetTranslation( aV01x );
6054 aV1x = aV0x.Transformed( aTrsf );
6055 aPN1 = aPN0.Transformed( aTrsf );
6057 // rotation 1 [ T1,T0 ]
6058 aAngleT1T0=-aDT1x.Angle( aDT0x );
6059 if (fabs(aAngleT1T0) > aTolAng) {
6061 anAxT1T0.SetLocation( aV1x );
6062 anAxT1T0.SetDirection( aDT1T0 );
6063 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6065 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6069 if ( theHasAngles ) {
6070 anAx1.SetLocation( aV1x );
6071 anAx1.SetDirection( aDT1x );
6072 aTrsfRot.SetRotation( anAx1, aAngle1x );
6074 aPN1 = aPN1.Transformed( aTrsfRot );
6078 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6079 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6080 // create additional node
6081 double x = ( aPN1.X() + aPN0.X() )/2.;
6082 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6083 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6084 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6085 myLastCreatedNodes.Append(newNode);
6086 srcNodes.Append( node );
6087 listNewNodes.push_back( newNode );
6089 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6090 myLastCreatedNodes.Append(newNode);
6091 srcNodes.Append( node );
6092 listNewNodes.push_back( newNode );
6102 // if current elem is quadratic and current node is not medium
6103 // we have to check - may be it is needed to insert additional nodes
6104 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6105 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6106 if(listNewNodes.size()==aNbTP-1) {
6107 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6108 gp_XYZ P(node->X(), node->Y(), node->Z());
6109 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6111 for(i=0; i<aNbTP-1; i++) {
6112 const SMDS_MeshNode* N = *it;
6113 double x = ( N->X() + P.X() )/2.;
6114 double y = ( N->Y() + P.Y() )/2.;
6115 double z = ( N->Z() + P.Z() )/2.;
6116 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6117 srcNodes.Append( node );
6118 myLastCreatedNodes.Append(newN);
6121 P = gp_XYZ(N->X(),N->Y(),N->Z());
6123 listNewNodes.clear();
6124 for(i=0; i<2*(aNbTP-1); i++) {
6125 listNewNodes.push_back(aNodes[i]);
6131 newNodesItVec.push_back( nIt );
6133 // make new elements
6134 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6135 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6136 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6139 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6141 if ( theMakeGroups )
6142 generateGroups( srcNodes, srcElems, "extruded");
6148 //=======================================================================
6149 //function : LinearAngleVariation
6150 //purpose : auxilary for ExtrusionAlongTrack
6151 //=======================================================================
6152 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6153 list<double>& Angles)
6155 int nbAngles = Angles.size();
6156 if( nbSteps > nbAngles ) {
6157 vector<double> theAngles(nbAngles);
6158 list<double>::iterator it = Angles.begin();
6160 for(; it!=Angles.end(); it++) {
6162 theAngles[i] = (*it);
6165 double rAn2St = double( nbAngles ) / double( nbSteps );
6166 double angPrev = 0, angle;
6167 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6168 double angCur = rAn2St * ( iSt+1 );
6169 double angCurFloor = floor( angCur );
6170 double angPrevFloor = floor( angPrev );
6171 if ( angPrevFloor == angCurFloor )
6172 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6174 int iP = int( angPrevFloor );
6175 double angPrevCeil = ceil(angPrev);
6176 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6178 int iC = int( angCurFloor );
6179 if ( iC < nbAngles )
6180 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6182 iP = int( angPrevCeil );
6184 angle += theAngles[ iC ];
6186 res.push_back(angle);
6191 for(; it!=res.end(); it++)
6192 Angles.push_back( *it );
6197 //================================================================================
6199 * \brief Move or copy theElements applying theTrsf to their nodes
6200 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6201 * \param theTrsf - transformation to apply
6202 * \param theCopy - if true, create translated copies of theElems
6203 * \param theMakeGroups - if true and theCopy, create translated groups
6204 * \param theTargetMesh - mesh to copy translated elements into
6205 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6207 //================================================================================
6209 SMESH_MeshEditor::PGroupIDs
6210 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6211 const gp_Trsf& theTrsf,
6213 const bool theMakeGroups,
6214 SMESH_Mesh* theTargetMesh)
6216 myLastCreatedElems.Clear();
6217 myLastCreatedNodes.Clear();
6219 bool needReverse = false;
6220 string groupPostfix;
6221 switch ( theTrsf.Form() ) {
6223 MESSAGE("gp_PntMirror");
6225 groupPostfix = "mirrored";
6228 MESSAGE("gp_Ax1Mirror");
6229 groupPostfix = "mirrored";
6232 MESSAGE("gp_Ax2Mirror");
6234 groupPostfix = "mirrored";
6237 MESSAGE("gp_Rotation");
6238 groupPostfix = "rotated";
6240 case gp_Translation:
6241 MESSAGE("gp_Translation");
6242 groupPostfix = "translated";
6245 MESSAGE("gp_Scale");
6246 groupPostfix = "scaled";
6248 case gp_CompoundTrsf: // different scale by axis
6249 MESSAGE("gp_CompoundTrsf");
6250 groupPostfix = "scaled";
6254 needReverse = false;
6255 groupPostfix = "transformed";
6258 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6259 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6260 SMESHDS_Mesh* aMesh = GetMeshDS();
6263 // map old node to new one
6264 TNodeNodeMap nodeMap;
6266 // elements sharing moved nodes; those of them which have all
6267 // nodes mirrored but are not in theElems are to be reversed
6268 TIDSortedElemSet inverseElemSet;
6270 // source elements for each generated one
6271 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6273 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6274 TIDSortedElemSet orphanNode;
6276 if ( theElems.empty() ) // transform the whole mesh
6279 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6280 while ( eIt->more() ) theElems.insert( eIt->next() );
6282 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6283 while ( nIt->more() )
6285 const SMDS_MeshNode* node = nIt->next();
6286 if ( node->NbInverseElements() == 0)
6287 orphanNode.insert( node );
6291 // loop on elements to transform nodes : first orphan nodes then elems
6292 TIDSortedElemSet::iterator itElem;
6293 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6294 for (int i=0; i<2; i++)
6295 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6296 const SMDS_MeshElement* elem = *itElem;
6300 // loop on elem nodes
6301 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6302 while ( itN->more() ) {
6304 const SMDS_MeshNode* node = cast2Node( itN->next() );
6305 // check if a node has been already transformed
6306 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6307 nodeMap.insert( make_pair ( node, node ));
6308 if ( !n2n_isnew.second )
6312 coord[0] = node->X();
6313 coord[1] = node->Y();
6314 coord[2] = node->Z();
6315 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6316 if ( theTargetMesh ) {
6317 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6318 n2n_isnew.first->second = newNode;
6319 myLastCreatedNodes.Append(newNode);
6320 srcNodes.Append( node );
6322 else if ( theCopy ) {
6323 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6324 n2n_isnew.first->second = newNode;
6325 myLastCreatedNodes.Append(newNode);
6326 srcNodes.Append( node );
6329 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6330 // node position on shape becomes invalid
6331 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6332 ( SMDS_SpacePosition::originSpacePosition() );
6335 // keep inverse elements
6336 if ( !theCopy && !theTargetMesh && needReverse ) {
6337 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6338 while ( invElemIt->more() ) {
6339 const SMDS_MeshElement* iel = invElemIt->next();
6340 inverseElemSet.insert( iel );
6346 // either create new elements or reverse mirrored ones
6347 if ( !theCopy && !needReverse && !theTargetMesh )
6350 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6351 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6352 theElems.insert( *invElemIt );
6354 // Replicate or reverse elements
6356 std::vector<int> iForw;
6357 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6359 const SMDS_MeshElement* elem = *itElem;
6360 if ( !elem ) continue;
6362 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6363 int nbNodes = elem->NbNodes();
6364 if ( geomType == SMDSGeom_NONE ) continue; // node
6366 switch ( geomType ) {
6368 case SMDSGeom_POLYGON: // ---------------------- polygon
6370 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6372 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6373 while (itN->more()) {
6374 const SMDS_MeshNode* node =
6375 static_cast<const SMDS_MeshNode*>(itN->next());
6376 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6377 if (nodeMapIt == nodeMap.end())
6378 break; // not all nodes transformed
6380 // reverse mirrored faces and volumes
6381 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6383 poly_nodes[iNode] = (*nodeMapIt).second;
6387 if ( iNode != nbNodes )
6388 continue; // not all nodes transformed
6390 if ( theTargetMesh ) {
6391 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6392 srcElems.Append( elem );
6394 else if ( theCopy ) {
6395 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6396 srcElems.Append( elem );
6399 aMesh->ChangePolygonNodes(elem, poly_nodes);
6404 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6406 const SMDS_VtkVolume* aPolyedre =
6407 dynamic_cast<const SMDS_VtkVolume*>( elem );
6409 MESSAGE("Warning: bad volumic element");
6413 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6414 vector<int> quantities; quantities.reserve( nbNodes );
6416 bool allTransformed = true;
6417 int nbFaces = aPolyedre->NbFaces();
6418 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6419 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6420 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6421 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6422 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6423 if (nodeMapIt == nodeMap.end()) {
6424 allTransformed = false; // not all nodes transformed
6426 poly_nodes.push_back((*nodeMapIt).second);
6428 if ( needReverse && allTransformed )
6429 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6431 quantities.push_back(nbFaceNodes);
6433 if ( !allTransformed )
6434 continue; // not all nodes transformed
6436 if ( theTargetMesh ) {
6437 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6438 srcElems.Append( elem );
6440 else if ( theCopy ) {
6441 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6442 srcElems.Append( elem );
6445 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6450 case SMDSGeom_BALL: // -------------------- Ball
6452 if ( !theCopy && !theTargetMesh ) continue;
6454 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6455 if (nodeMapIt == nodeMap.end())
6456 continue; // not all nodes transformed
6458 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6459 if ( theTargetMesh ) {
6460 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6461 srcElems.Append( elem );
6464 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6465 srcElems.Append( elem );
6470 default: // ----------------------- Regular elements
6472 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6473 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6474 const std::vector<int>& i = needReverse ? iRev : iForw;
6476 // find transformed nodes
6477 vector<const SMDS_MeshNode*> nodes(nbNodes);
6479 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6480 while ( itN->more() ) {
6481 const SMDS_MeshNode* node =
6482 static_cast<const SMDS_MeshNode*>( itN->next() );
6483 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6484 if ( nodeMapIt == nodeMap.end() )
6485 break; // not all nodes transformed
6486 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6488 if ( iNode != nbNodes )
6489 continue; // not all nodes transformed
6491 if ( theTargetMesh ) {
6492 if ( SMDS_MeshElement* copy =
6493 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6494 myLastCreatedElems.Append( copy );
6495 srcElems.Append( elem );
6498 else if ( theCopy ) {
6499 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6500 srcElems.Append( elem );
6503 // reverse element as it was reversed by transformation
6505 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6507 } // switch ( geomType )
6509 } // loop on elements
6511 PGroupIDs newGroupIDs;
6513 if ( ( theMakeGroups && theCopy ) ||
6514 ( theMakeGroups && theTargetMesh ) )
6515 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6520 //=======================================================================
6522 * \brief Create groups of elements made during transformation
6523 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6524 * \param elemGens - elements making corresponding myLastCreatedElems
6525 * \param postfix - to append to names of new groups
6526 * \param targetMesh - mesh to create groups in
6527 * \param topPresent - is there "top" elements that are created by sweeping
6529 //=======================================================================
6531 SMESH_MeshEditor::PGroupIDs
6532 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6533 const SMESH_SequenceOfElemPtr& elemGens,
6534 const std::string& postfix,
6535 SMESH_Mesh* targetMesh,
6536 const bool topPresent)
6538 PGroupIDs newGroupIDs( new list<int> );
6539 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6541 // Sort existing groups by types and collect their names
6543 // containers to store an old group and generated new ones;
6544 // 1st new group is for result elems of different type than a source one;
6545 // 2nd new group is for same type result elems ("top" group at extrusion)
6547 using boost::make_tuple;
6548 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6549 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6550 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6552 set< string > groupNames;
6554 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6555 if ( !groupIt->more() ) return newGroupIDs;
6557 int newGroupID = mesh->GetGroupIds().back()+1;
6558 while ( groupIt->more() )
6560 SMESH_Group * group = groupIt->next();
6561 if ( !group ) continue;
6562 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6563 if ( !groupDS || groupDS->IsEmpty() ) continue;
6564 groupNames.insert ( group->GetName() );
6565 groupDS->SetStoreName( group->GetName() );
6566 const SMDSAbs_ElementType type = groupDS->GetType();
6567 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6568 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6569 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6570 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6573 // Loop on nodes and elements to add them in new groups
6575 vector< const SMDS_MeshElement* > resultElems;
6576 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6578 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6579 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6580 if ( gens.Length() != elems.Length() )
6581 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6583 // loop on created elements
6584 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6586 const SMDS_MeshElement* sourceElem = gens( iElem );
6587 if ( !sourceElem ) {
6588 MESSAGE("generateGroups(): NULL source element");
6591 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6592 if ( groupsOldNew.empty() ) { // no groups of this type at all
6593 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6594 ++iElem; // skip all elements made by sourceElem
6597 // collect all elements made by the iElem-th sourceElem
6598 resultElems.clear();
6599 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6600 if ( resElem != sourceElem )
6601 resultElems.push_back( resElem );
6602 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6603 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6604 if ( resElem != sourceElem )
6605 resultElems.push_back( resElem );
6607 const SMDS_MeshElement* topElem = 0;
6608 if ( isNodes ) // there must be a top element
6610 topElem = resultElems.back();
6611 resultElems.pop_back();
6615 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6616 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6617 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6619 topElem = *resElemIt;
6620 *resElemIt = 0; // erase *resElemIt
6624 // add resultElems to groups originted from ones the sourceElem belongs to
6625 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6626 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6628 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6629 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6631 // fill in a new group
6632 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6633 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6634 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6636 newGroup.Add( *resElemIt );
6638 // fill a "top" group
6641 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6642 newTopGroup.Add( topElem );
6646 } // loop on created elements
6647 }// loop on nodes and elements
6649 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6651 list<int> topGrouIds;
6652 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6654 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6655 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6656 orderedOldNewGroups[i]->get<2>() };
6657 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6659 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6660 if ( newGroupDS->IsEmpty() )
6662 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6667 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6670 const bool isTop = ( topPresent &&
6671 newGroupDS->GetType() == oldGroupDS->GetType() &&
6674 string name = oldGroupDS->GetStoreName();
6675 { // remove trailing whitespaces (issue 22599)
6676 size_t size = name.size();
6677 while ( size > 1 && isspace( name[ size-1 ]))
6679 if ( size != name.size() )
6681 name.resize( size );
6682 oldGroupDS->SetStoreName( name.c_str() );
6685 if ( !targetMesh ) {
6686 string suffix = ( isTop ? "top": postfix.c_str() );
6690 while ( !groupNames.insert( name ).second ) // name exists
6691 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6696 newGroupDS->SetStoreName( name.c_str() );
6698 // make a SMESH_Groups
6699 mesh->AddGroup( newGroupDS );
6701 topGrouIds.push_back( newGroupDS->GetID() );
6703 newGroupIDs->push_back( newGroupDS->GetID() );
6707 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6712 //================================================================================
6714 * \brief Return list of group of nodes close to each other within theTolerance
6715 * Search among theNodes or in the whole mesh if theNodes is empty using
6716 * an Octree algorithm
6718 //================================================================================
6720 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6721 const double theTolerance,
6722 TListOfListOfNodes & theGroupsOfNodes)
6724 myLastCreatedElems.Clear();
6725 myLastCreatedNodes.Clear();
6727 if ( theNodes.empty() )
6728 { // get all nodes in the mesh
6729 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6730 while ( nIt->more() )
6731 theNodes.insert( theNodes.end(),nIt->next());
6734 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6737 //=======================================================================
6738 //function : SimplifyFace
6740 //=======================================================================
6742 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6743 vector<const SMDS_MeshNode *>& poly_nodes,
6744 vector<int>& quantities) const
6746 int nbNodes = faceNodes.size();
6751 set<const SMDS_MeshNode*> nodeSet;
6753 // get simple seq of nodes
6754 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6755 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6756 int iSimple = 0, nbUnique = 0;
6758 simpleNodes[iSimple++] = faceNodes[0];
6760 for (int iCur = 1; iCur < nbNodes; iCur++) {
6761 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6762 simpleNodes[iSimple++] = faceNodes[iCur];
6763 if (nodeSet.insert( faceNodes[iCur] ).second)
6767 int nbSimple = iSimple;
6768 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6778 bool foundLoop = (nbSimple > nbUnique);
6781 set<const SMDS_MeshNode*> loopSet;
6782 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6783 const SMDS_MeshNode* n = simpleNodes[iSimple];
6784 if (!loopSet.insert( n ).second) {
6788 int iC = 0, curLast = iSimple;
6789 for (; iC < curLast; iC++) {
6790 if (simpleNodes[iC] == n) break;
6792 int loopLen = curLast - iC;
6794 // create sub-element
6796 quantities.push_back(loopLen);
6797 for (; iC < curLast; iC++) {
6798 poly_nodes.push_back(simpleNodes[iC]);
6801 // shift the rest nodes (place from the first loop position)
6802 for (iC = curLast + 1; iC < nbSimple; iC++) {
6803 simpleNodes[iC - loopLen] = simpleNodes[iC];
6805 nbSimple -= loopLen;
6808 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6809 } // while (foundLoop)
6813 quantities.push_back(iSimple);
6814 for (int i = 0; i < iSimple; i++)
6815 poly_nodes.push_back(simpleNodes[i]);
6821 //=======================================================================
6822 //function : MergeNodes
6823 //purpose : In each group, the cdr of nodes are substituted by the first one
6825 //=======================================================================
6827 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6829 MESSAGE("MergeNodes");
6830 myLastCreatedElems.Clear();
6831 myLastCreatedNodes.Clear();
6833 SMESHDS_Mesh* aMesh = GetMeshDS();
6835 TNodeNodeMap nodeNodeMap; // node to replace - new node
6836 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6837 list< int > rmElemIds, rmNodeIds;
6839 // Fill nodeNodeMap and elems
6841 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6842 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6843 list<const SMDS_MeshNode*>& nodes = *grIt;
6844 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6845 const SMDS_MeshNode* nToKeep = *nIt;
6846 //MESSAGE("node to keep " << nToKeep->GetID());
6847 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6848 const SMDS_MeshNode* nToRemove = *nIt;
6849 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6850 if ( nToRemove != nToKeep ) {
6851 //MESSAGE(" node to remove " << nToRemove->GetID());
6852 rmNodeIds.push_back( nToRemove->GetID() );
6853 AddToSameGroups( nToKeep, nToRemove, aMesh );
6854 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6855 // after MergeNodes() w/o creating node in place of merged ones.
6856 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6857 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6858 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6859 sm->SetIsAlwaysComputed( true );
6862 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6863 while ( invElemIt->more() ) {
6864 const SMDS_MeshElement* elem = invElemIt->next();
6869 // Change element nodes or remove an element
6871 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6872 for ( ; eIt != elems.end(); eIt++ ) {
6873 const SMDS_MeshElement* elem = *eIt;
6874 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6875 int nbNodes = elem->NbNodes();
6876 int aShapeId = FindShape( elem );
6878 set<const SMDS_MeshNode*> nodeSet;
6879 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6880 int iUnique = 0, iCur = 0, nbRepl = 0;
6881 vector<int> iRepl( nbNodes );
6883 // get new seq of nodes
6884 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6885 while ( itN->more() ) {
6886 const SMDS_MeshNode* n =
6887 static_cast<const SMDS_MeshNode*>( itN->next() );
6889 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6890 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6892 // BUG 0020185: begin
6894 bool stopRecur = false;
6895 set<const SMDS_MeshNode*> nodesRecur;
6896 nodesRecur.insert(n);
6897 while (!stopRecur) {
6898 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6899 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6900 n = (*nnIt_i).second;
6901 if (!nodesRecur.insert(n).second) {
6902 // error: recursive dependancy
6912 curNodes[ iCur ] = n;
6913 bool isUnique = nodeSet.insert( n ).second;
6915 uniqueNodes[ iUnique++ ] = n;
6917 iRepl[ nbRepl++ ] = iCur;
6921 // Analyse element topology after replacement
6924 int nbUniqueNodes = nodeSet.size();
6925 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6926 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6927 // Polygons and Polyhedral volumes
6928 if (elem->IsPoly()) {
6930 if (elem->GetType() == SMDSAbs_Face) {
6932 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6934 for (; inode < nbNodes; inode++) {
6935 face_nodes[inode] = curNodes[inode];
6938 vector<const SMDS_MeshNode *> polygons_nodes;
6939 vector<int> quantities;
6940 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6943 for (int iface = 0; iface < nbNew; iface++) {
6944 int nbNodes = quantities[iface];
6945 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6946 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6947 poly_nodes[ii] = polygons_nodes[inode];
6949 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6950 myLastCreatedElems.Append(newElem);
6952 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6955 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6956 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6957 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6959 if (nbNew > 0) quid = nbNew - 1;
6960 vector<int> newquant(quantities.begin()+quid, quantities.end());
6961 const SMDS_MeshElement* newElem = 0;
6962 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6963 myLastCreatedElems.Append(newElem);
6964 if ( aShapeId && newElem )
6965 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6966 rmElemIds.push_back(elem->GetID());
6969 rmElemIds.push_back(elem->GetID());
6973 else if (elem->GetType() == SMDSAbs_Volume) {
6974 // Polyhedral volume
6975 if (nbUniqueNodes < 4) {
6976 rmElemIds.push_back(elem->GetID());
6979 // each face has to be analyzed in order to check volume validity
6980 const SMDS_VtkVolume* aPolyedre =
6981 dynamic_cast<const SMDS_VtkVolume*>( elem );
6983 int nbFaces = aPolyedre->NbFaces();
6985 vector<const SMDS_MeshNode *> poly_nodes;
6986 vector<int> quantities;
6988 for (int iface = 1; iface <= nbFaces; iface++) {
6989 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6990 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6992 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6993 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6994 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6995 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6996 faceNode = (*nnIt).second;
6998 faceNodes[inode - 1] = faceNode;
7001 SimplifyFace(faceNodes, poly_nodes, quantities);
7004 if (quantities.size() > 3) {
7005 // to be done: remove coincident faces
7008 if (quantities.size() > 3)
7010 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7011 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7012 const SMDS_MeshElement* newElem = 0;
7013 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7014 myLastCreatedElems.Append(newElem);
7015 if ( aShapeId && newElem )
7016 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7017 rmElemIds.push_back(elem->GetID());
7021 rmElemIds.push_back(elem->GetID());
7032 // TODO not all the possible cases are solved. Find something more generic?
7033 switch ( nbNodes ) {
7034 case 2: ///////////////////////////////////// EDGE
7035 isOk = false; break;
7036 case 3: ///////////////////////////////////// TRIANGLE
7037 isOk = false; break;
7039 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7041 else { //////////////////////////////////// QUADRANGLE
7042 if ( nbUniqueNodes < 3 )
7044 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7045 isOk = false; // opposite nodes stick
7046 //MESSAGE("isOk " << isOk);
7049 case 6: ///////////////////////////////////// PENTAHEDRON
7050 if ( nbUniqueNodes == 4 ) {
7051 // ---------------------------------> tetrahedron
7053 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7054 // all top nodes stick: reverse a bottom
7055 uniqueNodes[ 0 ] = curNodes [ 1 ];
7056 uniqueNodes[ 1 ] = curNodes [ 0 ];
7058 else if (nbRepl == 3 &&
7059 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7060 // all bottom nodes stick: set a top before
7061 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7062 uniqueNodes[ 0 ] = curNodes [ 3 ];
7063 uniqueNodes[ 1 ] = curNodes [ 4 ];
7064 uniqueNodes[ 2 ] = curNodes [ 5 ];
7066 else if (nbRepl == 4 &&
7067 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7068 // a lateral face turns into a line: reverse a bottom
7069 uniqueNodes[ 0 ] = curNodes [ 1 ];
7070 uniqueNodes[ 1 ] = curNodes [ 0 ];
7075 else if ( nbUniqueNodes == 5 ) {
7076 // PENTAHEDRON --------------------> 2 tetrahedrons
7077 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7078 // a bottom node sticks with a linked top one
7080 SMDS_MeshElement* newElem =
7081 aMesh->AddVolume(curNodes[ 3 ],
7084 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7085 myLastCreatedElems.Append(newElem);
7087 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7088 // 2. : reverse a bottom
7089 uniqueNodes[ 0 ] = curNodes [ 1 ];
7090 uniqueNodes[ 1 ] = curNodes [ 0 ];
7100 if(elem->IsQuadratic()) { // Quadratic quadrangle
7112 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7115 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7117 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7118 uniqueNodes[0] = curNodes[0];
7119 uniqueNodes[1] = curNodes[2];
7120 uniqueNodes[2] = curNodes[3];
7121 uniqueNodes[3] = curNodes[5];
7122 uniqueNodes[4] = curNodes[6];
7123 uniqueNodes[5] = curNodes[7];
7126 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7127 uniqueNodes[0] = curNodes[0];
7128 uniqueNodes[1] = curNodes[1];
7129 uniqueNodes[2] = curNodes[2];
7130 uniqueNodes[3] = curNodes[4];
7131 uniqueNodes[4] = curNodes[5];
7132 uniqueNodes[5] = curNodes[6];
7135 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7136 uniqueNodes[0] = curNodes[1];
7137 uniqueNodes[1] = curNodes[2];
7138 uniqueNodes[2] = curNodes[3];
7139 uniqueNodes[3] = curNodes[5];
7140 uniqueNodes[4] = curNodes[6];
7141 uniqueNodes[5] = curNodes[0];
7144 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7145 uniqueNodes[0] = curNodes[0];
7146 uniqueNodes[1] = curNodes[1];
7147 uniqueNodes[2] = curNodes[3];
7148 uniqueNodes[3] = curNodes[4];
7149 uniqueNodes[4] = curNodes[6];
7150 uniqueNodes[5] = curNodes[7];
7153 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7154 uniqueNodes[0] = curNodes[0];
7155 uniqueNodes[1] = curNodes[2];
7156 uniqueNodes[2] = curNodes[3];
7157 uniqueNodes[3] = curNodes[1];
7158 uniqueNodes[4] = curNodes[6];
7159 uniqueNodes[5] = curNodes[7];
7162 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7163 uniqueNodes[0] = curNodes[0];
7164 uniqueNodes[1] = curNodes[1];
7165 uniqueNodes[2] = curNodes[2];
7166 uniqueNodes[3] = curNodes[4];
7167 uniqueNodes[4] = curNodes[5];
7168 uniqueNodes[5] = curNodes[7];
7171 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7172 uniqueNodes[0] = curNodes[0];
7173 uniqueNodes[1] = curNodes[1];
7174 uniqueNodes[2] = curNodes[3];
7175 uniqueNodes[3] = curNodes[4];
7176 uniqueNodes[4] = curNodes[2];
7177 uniqueNodes[5] = curNodes[7];
7180 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7181 uniqueNodes[0] = curNodes[0];
7182 uniqueNodes[1] = curNodes[1];
7183 uniqueNodes[2] = curNodes[2];
7184 uniqueNodes[3] = curNodes[4];
7185 uniqueNodes[4] = curNodes[5];
7186 uniqueNodes[5] = curNodes[3];
7191 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7194 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7198 //////////////////////////////////// HEXAHEDRON
7200 SMDS_VolumeTool hexa (elem);
7201 hexa.SetExternalNormal();
7202 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7203 //////////////////////// HEX ---> 1 tetrahedron
7204 for ( int iFace = 0; iFace < 6; iFace++ ) {
7205 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7206 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7207 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7208 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7209 // one face turns into a point ...
7210 int iOppFace = hexa.GetOppFaceIndex( iFace );
7211 ind = hexa.GetFaceNodesIndices( iOppFace );
7213 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7214 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7217 if ( nbStick == 1 ) {
7218 // ... and the opposite one - into a triangle.
7220 ind = hexa.GetFaceNodesIndices( iFace );
7221 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7228 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7229 //////////////////////// HEX ---> 1 prism
7230 int nbTria = 0, iTria[3];
7231 const int *ind; // indices of face nodes
7232 // look for triangular faces
7233 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7234 ind = hexa.GetFaceNodesIndices( iFace );
7235 TIDSortedNodeSet faceNodes;
7236 for ( iCur = 0; iCur < 4; iCur++ )
7237 faceNodes.insert( curNodes[ind[iCur]] );
7238 if ( faceNodes.size() == 3 )
7239 iTria[ nbTria++ ] = iFace;
7241 // check if triangles are opposite
7242 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7245 // set nodes of the bottom triangle
7246 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7248 for ( iCur = 0; iCur < 4; iCur++ )
7249 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7250 indB.push_back( ind[iCur] );
7251 if ( !hexa.IsForward() )
7252 std::swap( indB[0], indB[2] );
7253 for ( iCur = 0; iCur < 3; iCur++ )
7254 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7255 // set nodes of the top triangle
7256 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7257 for ( iCur = 0; iCur < 3; ++iCur )
7258 for ( int j = 0; j < 4; ++j )
7259 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7261 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7267 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7268 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7269 for ( int iFace = 0; iFace < 6; iFace++ ) {
7270 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7271 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7272 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7273 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7274 // one face turns into a point ...
7275 int iOppFace = hexa.GetOppFaceIndex( iFace );
7276 ind = hexa.GetFaceNodesIndices( iOppFace );
7278 iUnique = 2; // reverse a tetrahedron 1 bottom
7279 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7280 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7282 else if ( iUnique >= 0 )
7283 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7285 if ( nbStick == 0 ) {
7286 // ... and the opposite one is a quadrangle
7288 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7289 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7292 SMDS_MeshElement* newElem =
7293 aMesh->AddVolume(curNodes[ind[ 0 ]],
7296 curNodes[indTop[ 0 ]]);
7297 myLastCreatedElems.Append(newElem);
7299 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7306 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7307 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7308 // find indices of quad and tri faces
7309 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7310 for ( iFace = 0; iFace < 6; iFace++ ) {
7311 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7313 for ( iCur = 0; iCur < 4; iCur++ )
7314 nodeSet.insert( curNodes[ind[ iCur ]] );
7315 nbUniqueNodes = nodeSet.size();
7316 if ( nbUniqueNodes == 3 )
7317 iTriFace[ nbTri++ ] = iFace;
7318 else if ( nbUniqueNodes == 4 )
7319 iQuadFace[ nbQuad++ ] = iFace;
7321 if (nbQuad == 2 && nbTri == 4 &&
7322 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7323 // 2 opposite quadrangles stuck with a diagonal;
7324 // sample groups of merged indices: (0-4)(2-6)
7325 // --------------------------------------------> 2 tetrahedrons
7326 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7327 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7328 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7329 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7330 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7331 // stuck with 0-2 diagonal
7339 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7340 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7341 // stuck with 1-3 diagonal
7353 uniqueNodes[ 0 ] = curNodes [ i0 ];
7354 uniqueNodes[ 1 ] = curNodes [ i1d ];
7355 uniqueNodes[ 2 ] = curNodes [ i3d ];
7356 uniqueNodes[ 3 ] = curNodes [ i0t ];
7359 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7363 myLastCreatedElems.Append(newElem);
7365 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7368 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7369 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7370 // --------------------------------------------> prism
7371 // find 2 opposite triangles
7373 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7374 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7375 // find indices of kept and replaced nodes
7376 // and fill unique nodes of 2 opposite triangles
7377 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7378 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7379 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7380 // fill unique nodes
7383 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7384 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7385 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7387 // iCur of a linked node of the opposite face (make normals co-directed):
7388 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7389 // check that correspondent corners of triangles are linked
7390 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7393 uniqueNodes[ iUnique ] = n;
7394 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7403 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7406 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7413 } // switch ( nbNodes )
7415 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7417 if ( isOk ) { // the elem remains valid after sticking nodes
7418 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7420 // Change nodes of polyedre
7421 const SMDS_VtkVolume* aPolyedre =
7422 dynamic_cast<const SMDS_VtkVolume*>( elem );
7424 int nbFaces = aPolyedre->NbFaces();
7426 vector<const SMDS_MeshNode *> poly_nodes;
7427 vector<int> quantities (nbFaces);
7429 for (int iface = 1; iface <= nbFaces; iface++) {
7430 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7431 quantities[iface - 1] = nbFaceNodes;
7433 for (inode = 1; inode <= nbFaceNodes; inode++) {
7434 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7436 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7437 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7438 curNode = (*nnIt).second;
7440 poly_nodes.push_back(curNode);
7443 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7446 else // replace non-polyhedron elements
7448 const SMDSAbs_ElementType etyp = elem->GetType();
7449 const int elemId = elem->GetID();
7450 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7451 uniqueNodes.resize(nbUniqueNodes);
7453 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7455 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7456 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7457 if ( sm && newElem )
7458 sm->AddElement( newElem );
7459 if ( elem != newElem )
7460 ReplaceElemInGroups( elem, newElem, aMesh );
7464 // Remove invalid regular element or invalid polygon
7465 rmElemIds.push_back( elem->GetID() );
7468 } // loop on elements
7470 // Remove bad elements, then equal nodes (order important)
7472 Remove( rmElemIds, false );
7473 Remove( rmNodeIds, true );
7478 // ========================================================
7479 // class : SortableElement
7480 // purpose : allow sorting elements basing on their nodes
7481 // ========================================================
7482 class SortableElement : public set <const SMDS_MeshElement*>
7486 SortableElement( const SMDS_MeshElement* theElem )
7489 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7490 while ( nodeIt->more() )
7491 this->insert( nodeIt->next() );
7494 const SMDS_MeshElement* Get() const
7497 void Set(const SMDS_MeshElement* e) const
7502 mutable const SMDS_MeshElement* myElem;
7505 //=======================================================================
7506 //function : FindEqualElements
7507 //purpose : Return list of group of elements built on the same nodes.
7508 // Search among theElements or in the whole mesh if theElements is empty
7509 //=======================================================================
7511 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7512 TListOfListOfElementsID & theGroupsOfElementsID)
7514 myLastCreatedElems.Clear();
7515 myLastCreatedNodes.Clear();
7517 typedef map< SortableElement, int > TMapOfNodeSet;
7518 typedef list<int> TGroupOfElems;
7520 if ( theElements.empty() )
7521 { // get all elements in the mesh
7522 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7523 while ( eIt->more() )
7524 theElements.insert( theElements.end(), eIt->next());
7527 vector< TGroupOfElems > arrayOfGroups;
7528 TGroupOfElems groupOfElems;
7529 TMapOfNodeSet mapOfNodeSet;
7531 TIDSortedElemSet::iterator elemIt = theElements.begin();
7532 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7533 const SMDS_MeshElement* curElem = *elemIt;
7534 SortableElement SE(curElem);
7537 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7538 if( !(pp.second) ) {
7539 TMapOfNodeSet::iterator& itSE = pp.first;
7540 ind = (*itSE).second;
7541 arrayOfGroups[ind].push_back(curElem->GetID());
7544 groupOfElems.clear();
7545 groupOfElems.push_back(curElem->GetID());
7546 arrayOfGroups.push_back(groupOfElems);
7551 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7552 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7553 groupOfElems = *groupIt;
7554 if ( groupOfElems.size() > 1 ) {
7555 groupOfElems.sort();
7556 theGroupsOfElementsID.push_back(groupOfElems);
7561 //=======================================================================
7562 //function : MergeElements
7563 //purpose : In each given group, substitute all elements by the first one.
7564 //=======================================================================
7566 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7568 myLastCreatedElems.Clear();
7569 myLastCreatedNodes.Clear();
7571 typedef list<int> TListOfIDs;
7572 TListOfIDs rmElemIds; // IDs of elems to remove
7574 SMESHDS_Mesh* aMesh = GetMeshDS();
7576 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7577 while ( groupsIt != theGroupsOfElementsID.end() ) {
7578 TListOfIDs& aGroupOfElemID = *groupsIt;
7579 aGroupOfElemID.sort();
7580 int elemIDToKeep = aGroupOfElemID.front();
7581 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7582 aGroupOfElemID.pop_front();
7583 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7584 while ( idIt != aGroupOfElemID.end() ) {
7585 int elemIDToRemove = *idIt;
7586 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7587 // add the kept element in groups of removed one (PAL15188)
7588 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7589 rmElemIds.push_back( elemIDToRemove );
7595 Remove( rmElemIds, false );
7598 //=======================================================================
7599 //function : MergeEqualElements
7600 //purpose : Remove all but one of elements built on the same nodes.
7601 //=======================================================================
7603 void SMESH_MeshEditor::MergeEqualElements()
7605 TIDSortedElemSet aMeshElements; /* empty input ==
7606 to merge equal elements in the whole mesh */
7607 TListOfListOfElementsID aGroupsOfElementsID;
7608 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7609 MergeElements(aGroupsOfElementsID);
7612 //=======================================================================
7613 //function : findAdjacentFace
7615 //=======================================================================
7617 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7618 const SMDS_MeshNode* n2,
7619 const SMDS_MeshElement* elem)
7621 TIDSortedElemSet elemSet, avoidSet;
7623 avoidSet.insert ( elem );
7624 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7627 //=======================================================================
7628 //function : FindFreeBorder
7630 //=======================================================================
7632 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7634 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7635 const SMDS_MeshNode* theSecondNode,
7636 const SMDS_MeshNode* theLastNode,
7637 list< const SMDS_MeshNode* > & theNodes,
7638 list< const SMDS_MeshElement* >& theFaces)
7640 if ( !theFirstNode || !theSecondNode )
7642 // find border face between theFirstNode and theSecondNode
7643 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7647 theFaces.push_back( curElem );
7648 theNodes.push_back( theFirstNode );
7649 theNodes.push_back( theSecondNode );
7651 //vector<const SMDS_MeshNode*> nodes;
7652 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7653 TIDSortedElemSet foundElems;
7654 bool needTheLast = ( theLastNode != 0 );
7656 while ( nStart != theLastNode ) {
7657 if ( nStart == theFirstNode )
7658 return !needTheLast;
7660 // find all free border faces sharing form nStart
7662 list< const SMDS_MeshElement* > curElemList;
7663 list< const SMDS_MeshNode* > nStartList;
7664 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7665 while ( invElemIt->more() ) {
7666 const SMDS_MeshElement* e = invElemIt->next();
7667 if ( e == curElem || foundElems.insert( e ).second ) {
7669 int iNode = 0, nbNodes = e->NbNodes();
7670 //const SMDS_MeshNode* nodes[nbNodes+1];
7671 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7673 if(e->IsQuadratic()) {
7674 const SMDS_VtkFace* F =
7675 dynamic_cast<const SMDS_VtkFace*>(e);
7676 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7677 // use special nodes iterator
7678 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7679 while( anIter->more() ) {
7680 nodes[ iNode++ ] = cast2Node(anIter->next());
7684 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7685 while ( nIt->more() )
7686 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7688 nodes[ iNode ] = nodes[ 0 ];
7690 for ( iNode = 0; iNode < nbNodes; iNode++ )
7691 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7692 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7693 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7695 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7696 curElemList.push_back( e );
7700 // analyse the found
7702 int nbNewBorders = curElemList.size();
7703 if ( nbNewBorders == 0 ) {
7704 // no free border furthermore
7705 return !needTheLast;
7707 else if ( nbNewBorders == 1 ) {
7708 // one more element found
7710 nStart = nStartList.front();
7711 curElem = curElemList.front();
7712 theFaces.push_back( curElem );
7713 theNodes.push_back( nStart );
7716 // several continuations found
7717 list< const SMDS_MeshElement* >::iterator curElemIt;
7718 list< const SMDS_MeshNode* >::iterator nStartIt;
7719 // check if one of them reached the last node
7720 if ( needTheLast ) {
7721 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7722 curElemIt!= curElemList.end();
7723 curElemIt++, nStartIt++ )
7724 if ( *nStartIt == theLastNode ) {
7725 theFaces.push_back( *curElemIt );
7726 theNodes.push_back( *nStartIt );
7730 // find the best free border by the continuations
7731 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7732 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7733 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7734 curElemIt!= curElemList.end();
7735 curElemIt++, nStartIt++ )
7737 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7738 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7739 // find one more free border
7740 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7744 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7745 // choice: clear a worse one
7746 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7747 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7748 contNodes[ iWorse ].clear();
7749 contFaces[ iWorse ].clear();
7752 if ( contNodes[0].empty() && contNodes[1].empty() )
7755 // append the best free border
7756 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7757 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7758 theNodes.pop_back(); // remove nIgnore
7759 theNodes.pop_back(); // remove nStart
7760 theFaces.pop_back(); // remove curElem
7761 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7762 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7763 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7764 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7767 } // several continuations found
7768 } // while ( nStart != theLastNode )
7773 //=======================================================================
7774 //function : CheckFreeBorderNodes
7775 //purpose : Return true if the tree nodes are on a free border
7776 //=======================================================================
7778 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7779 const SMDS_MeshNode* theNode2,
7780 const SMDS_MeshNode* theNode3)
7782 list< const SMDS_MeshNode* > nodes;
7783 list< const SMDS_MeshElement* > faces;
7784 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7787 //=======================================================================
7788 //function : SewFreeBorder
7790 //=======================================================================
7792 SMESH_MeshEditor::Sew_Error
7793 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7794 const SMDS_MeshNode* theBordSecondNode,
7795 const SMDS_MeshNode* theBordLastNode,
7796 const SMDS_MeshNode* theSideFirstNode,
7797 const SMDS_MeshNode* theSideSecondNode,
7798 const SMDS_MeshNode* theSideThirdNode,
7799 const bool theSideIsFreeBorder,
7800 const bool toCreatePolygons,
7801 const bool toCreatePolyedrs)
7803 myLastCreatedElems.Clear();
7804 myLastCreatedNodes.Clear();
7806 MESSAGE("::SewFreeBorder()");
7807 Sew_Error aResult = SEW_OK;
7809 // ====================================
7810 // find side nodes and elements
7811 // ====================================
7813 list< const SMDS_MeshNode* > nSide[ 2 ];
7814 list< const SMDS_MeshElement* > eSide[ 2 ];
7815 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7816 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7820 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7821 nSide[0], eSide[0])) {
7822 MESSAGE(" Free Border 1 not found " );
7823 aResult = SEW_BORDER1_NOT_FOUND;
7825 if (theSideIsFreeBorder) {
7828 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7829 nSide[1], eSide[1])) {
7830 MESSAGE(" Free Border 2 not found " );
7831 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7834 if ( aResult != SEW_OK )
7837 if (!theSideIsFreeBorder) {
7841 // -------------------------------------------------------------------------
7843 // 1. If nodes to merge are not coincident, move nodes of the free border
7844 // from the coord sys defined by the direction from the first to last
7845 // nodes of the border to the correspondent sys of the side 2
7846 // 2. On the side 2, find the links most co-directed with the correspondent
7847 // links of the free border
7848 // -------------------------------------------------------------------------
7850 // 1. Since sewing may break if there are volumes to split on the side 2,
7851 // we wont move nodes but just compute new coordinates for them
7852 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7853 TNodeXYZMap nBordXYZ;
7854 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7855 list< const SMDS_MeshNode* >::iterator nBordIt;
7857 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7858 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7859 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7860 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7861 double tol2 = 1.e-8;
7862 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7863 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7864 // Need node movement.
7866 // find X and Z axes to create trsf
7867 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7869 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7871 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7874 gp_Ax3 toBordAx( Pb1, Zb, X );
7875 gp_Ax3 fromSideAx( Ps1, Zs, X );
7876 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7878 gp_Trsf toBordSys, fromSide2Sys;
7879 toBordSys.SetTransformation( toBordAx );
7880 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7881 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7884 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7885 const SMDS_MeshNode* n = *nBordIt;
7886 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7887 toBordSys.Transforms( xyz );
7888 fromSide2Sys.Transforms( xyz );
7889 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7893 // just insert nodes XYZ in the nBordXYZ map
7894 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7895 const SMDS_MeshNode* n = *nBordIt;
7896 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7900 // 2. On the side 2, find the links most co-directed with the correspondent
7901 // links of the free border
7903 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7904 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7905 sideNodes.push_back( theSideFirstNode );
7907 bool hasVolumes = false;
7908 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7909 set<long> foundSideLinkIDs, checkedLinkIDs;
7910 SMDS_VolumeTool volume;
7911 //const SMDS_MeshNode* faceNodes[ 4 ];
7913 const SMDS_MeshNode* sideNode;
7914 const SMDS_MeshElement* sideElem;
7915 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7916 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7917 nBordIt = bordNodes.begin();
7919 // border node position and border link direction to compare with
7920 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7921 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7922 // choose next side node by link direction or by closeness to
7923 // the current border node:
7924 bool searchByDir = ( *nBordIt != theBordLastNode );
7926 // find the next node on the Side 2
7928 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7930 checkedLinkIDs.clear();
7931 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7933 // loop on inverse elements of current node (prevSideNode) on the Side 2
7934 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7935 while ( invElemIt->more() )
7937 const SMDS_MeshElement* elem = invElemIt->next();
7938 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7939 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7940 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7941 bool isVolume = volume.Set( elem );
7942 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7943 if ( isVolume ) // --volume
7945 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7946 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7947 if(elem->IsQuadratic()) {
7948 const SMDS_VtkFace* F =
7949 dynamic_cast<const SMDS_VtkFace*>(elem);
7950 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7951 // use special nodes iterator
7952 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7953 while( anIter->more() ) {
7954 nodes[ iNode ] = cast2Node(anIter->next());
7955 if ( nodes[ iNode++ ] == prevSideNode )
7956 iPrevNode = iNode - 1;
7960 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7961 while ( nIt->more() ) {
7962 nodes[ iNode ] = cast2Node( nIt->next() );
7963 if ( nodes[ iNode++ ] == prevSideNode )
7964 iPrevNode = iNode - 1;
7967 // there are 2 links to check
7972 // loop on links, to be precise, on the second node of links
7973 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7974 const SMDS_MeshNode* n = nodes[ iNode ];
7976 if ( !volume.IsLinked( n, prevSideNode ))
7980 if ( iNode ) // a node before prevSideNode
7981 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7982 else // a node after prevSideNode
7983 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7985 // check if this link was already used
7986 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7987 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7988 if (!isJustChecked &&
7989 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7991 // test a link geometrically
7992 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7993 bool linkIsBetter = false;
7994 double dot = 0.0, dist = 0.0;
7995 if ( searchByDir ) { // choose most co-directed link
7996 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7997 linkIsBetter = ( dot > maxDot );
7999 else { // choose link with the node closest to bordPos
8000 dist = ( nextXYZ - bordPos ).SquareModulus();
8001 linkIsBetter = ( dist < minDist );
8003 if ( linkIsBetter ) {
8012 } // loop on inverse elements of prevSideNode
8015 MESSAGE(" Cant find path by links of the Side 2 ");
8016 return SEW_BAD_SIDE_NODES;
8018 sideNodes.push_back( sideNode );
8019 sideElems.push_back( sideElem );
8020 foundSideLinkIDs.insert ( linkID );
8021 prevSideNode = sideNode;
8023 if ( *nBordIt == theBordLastNode )
8024 searchByDir = false;
8026 // find the next border link to compare with
8027 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8028 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8029 // move to next border node if sideNode is before forward border node (bordPos)
8030 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8031 prevBordNode = *nBordIt;
8033 bordPos = nBordXYZ[ *nBordIt ];
8034 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8035 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8039 while ( sideNode != theSideSecondNode );
8041 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8042 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8043 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8045 } // end nodes search on the side 2
8047 // ============================
8048 // sew the border to the side 2
8049 // ============================
8051 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8052 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8054 TListOfListOfNodes nodeGroupsToMerge;
8055 if ( nbNodes[0] == nbNodes[1] ||
8056 ( theSideIsFreeBorder && !theSideThirdNode)) {
8058 // all nodes are to be merged
8060 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8061 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8062 nIt[0]++, nIt[1]++ )
8064 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8065 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8066 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8071 // insert new nodes into the border and the side to get equal nb of segments
8073 // get normalized parameters of nodes on the borders
8074 //double param[ 2 ][ maxNbNodes ];
8076 param[0] = new double [ maxNbNodes ];
8077 param[1] = new double [ maxNbNodes ];
8079 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8080 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8081 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8082 const SMDS_MeshNode* nPrev = *nIt;
8083 double bordLength = 0;
8084 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8085 const SMDS_MeshNode* nCur = *nIt;
8086 gp_XYZ segment (nCur->X() - nPrev->X(),
8087 nCur->Y() - nPrev->Y(),
8088 nCur->Z() - nPrev->Z());
8089 double segmentLen = segment.Modulus();
8090 bordLength += segmentLen;
8091 param[ iBord ][ iNode ] = bordLength;
8094 // normalize within [0,1]
8095 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8096 param[ iBord ][ iNode ] /= bordLength;
8100 // loop on border segments
8101 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8102 int i[ 2 ] = { 0, 0 };
8103 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8104 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8106 TElemOfNodeListMap insertMap;
8107 TElemOfNodeListMap::iterator insertMapIt;
8109 // key: elem to insert nodes into
8110 // value: 2 nodes to insert between + nodes to be inserted
8112 bool next[ 2 ] = { false, false };
8114 // find min adjacent segment length after sewing
8115 double nextParam = 10., prevParam = 0;
8116 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8117 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8118 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8119 if ( i[ iBord ] > 0 )
8120 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8122 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8123 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8124 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8126 // choose to insert or to merge nodes
8127 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8128 if ( Abs( du ) <= minSegLen * 0.2 ) {
8131 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8132 const SMDS_MeshNode* n0 = *nIt[0];
8133 const SMDS_MeshNode* n1 = *nIt[1];
8134 nodeGroupsToMerge.back().push_back( n1 );
8135 nodeGroupsToMerge.back().push_back( n0 );
8136 // position of node of the border changes due to merge
8137 param[ 0 ][ i[0] ] += du;
8138 // move n1 for the sake of elem shape evaluation during insertion.
8139 // n1 will be removed by MergeNodes() anyway
8140 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8141 next[0] = next[1] = true;
8146 int intoBord = ( du < 0 ) ? 0 : 1;
8147 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8148 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8149 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8150 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8151 if ( intoBord == 1 ) {
8152 // move node of the border to be on a link of elem of the side
8153 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8154 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8155 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8156 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8157 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8159 insertMapIt = insertMap.find( elem );
8160 bool notFound = ( insertMapIt == insertMap.end() );
8161 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8163 // insert into another link of the same element:
8164 // 1. perform insertion into the other link of the elem
8165 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8166 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8167 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8168 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8169 // 2. perform insertion into the link of adjacent faces
8171 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8173 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8177 if (toCreatePolyedrs) {
8178 // perform insertion into the links of adjacent volumes
8179 UpdateVolumes(n12, n22, nodeList);
8181 // 3. find an element appeared on n1 and n2 after the insertion
8182 insertMap.erase( elem );
8183 elem = findAdjacentFace( n1, n2, 0 );
8185 if ( notFound || otherLink ) {
8186 // add element and nodes of the side into the insertMap
8187 insertMapIt = insertMap.insert
8188 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8189 (*insertMapIt).second.push_back( n1 );
8190 (*insertMapIt).second.push_back( n2 );
8192 // add node to be inserted into elem
8193 (*insertMapIt).second.push_back( nIns );
8194 next[ 1 - intoBord ] = true;
8197 // go to the next segment
8198 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8199 if ( next[ iBord ] ) {
8200 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8202 nPrev[ iBord ] = *nIt[ iBord ];
8203 nIt[ iBord ]++; i[ iBord ]++;
8207 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8209 // perform insertion of nodes into elements
8211 for (insertMapIt = insertMap.begin();
8212 insertMapIt != insertMap.end();
8215 const SMDS_MeshElement* elem = (*insertMapIt).first;
8216 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8217 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8218 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8220 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8222 if ( !theSideIsFreeBorder ) {
8223 // look for and insert nodes into the faces adjacent to elem
8225 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8227 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8232 if (toCreatePolyedrs) {
8233 // perform insertion into the links of adjacent volumes
8234 UpdateVolumes(n1, n2, nodeList);
8240 } // end: insert new nodes
8242 MergeNodes ( nodeGroupsToMerge );
8247 //=======================================================================
8248 //function : InsertNodesIntoLink
8249 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8250 // and theBetweenNode2 and split theElement
8251 //=======================================================================
8253 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8254 const SMDS_MeshNode* theBetweenNode1,
8255 const SMDS_MeshNode* theBetweenNode2,
8256 list<const SMDS_MeshNode*>& theNodesToInsert,
8257 const bool toCreatePoly)
8259 if ( theFace->GetType() != SMDSAbs_Face ) return;
8261 // find indices of 2 link nodes and of the rest nodes
8262 int iNode = 0, il1, il2, i3, i4;
8263 il1 = il2 = i3 = i4 = -1;
8264 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8265 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8267 if(theFace->IsQuadratic()) {
8268 const SMDS_VtkFace* F =
8269 dynamic_cast<const SMDS_VtkFace*>(theFace);
8270 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8271 // use special nodes iterator
8272 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8273 while( anIter->more() ) {
8274 const SMDS_MeshNode* n = cast2Node(anIter->next());
8275 if ( n == theBetweenNode1 )
8277 else if ( n == theBetweenNode2 )
8283 nodes[ iNode++ ] = n;
8287 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8288 while ( nodeIt->more() ) {
8289 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8290 if ( n == theBetweenNode1 )
8292 else if ( n == theBetweenNode2 )
8298 nodes[ iNode++ ] = n;
8301 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8304 // arrange link nodes to go one after another regarding the face orientation
8305 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8306 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8311 aNodesToInsert.reverse();
8313 // check that not link nodes of a quadrangles are in good order
8314 int nbFaceNodes = theFace->NbNodes();
8315 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8321 if (toCreatePoly || theFace->IsPoly()) {
8324 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8326 // add nodes of face up to first node of link
8329 if(theFace->IsQuadratic()) {
8330 const SMDS_VtkFace* F =
8331 dynamic_cast<const SMDS_VtkFace*>(theFace);
8332 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8333 // use special nodes iterator
8334 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8335 while( anIter->more() && !isFLN ) {
8336 const SMDS_MeshNode* n = cast2Node(anIter->next());
8337 poly_nodes[iNode++] = n;
8338 if (n == nodes[il1]) {
8342 // add nodes to insert
8343 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8344 for (; nIt != aNodesToInsert.end(); nIt++) {
8345 poly_nodes[iNode++] = *nIt;
8347 // add nodes of face starting from last node of link
8348 while ( anIter->more() ) {
8349 poly_nodes[iNode++] = cast2Node(anIter->next());
8353 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8354 while ( nodeIt->more() && !isFLN ) {
8355 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8356 poly_nodes[iNode++] = n;
8357 if (n == nodes[il1]) {
8361 // add nodes to insert
8362 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8363 for (; nIt != aNodesToInsert.end(); nIt++) {
8364 poly_nodes[iNode++] = *nIt;
8366 // add nodes of face starting from last node of link
8367 while ( nodeIt->more() ) {
8368 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8369 poly_nodes[iNode++] = n;
8373 // edit or replace the face
8374 SMESHDS_Mesh *aMesh = GetMeshDS();
8376 if (theFace->IsPoly()) {
8377 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8380 int aShapeId = FindShape( theFace );
8382 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8383 myLastCreatedElems.Append(newElem);
8384 if ( aShapeId && newElem )
8385 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8387 aMesh->RemoveElement(theFace);
8392 SMESHDS_Mesh *aMesh = GetMeshDS();
8393 if( !theFace->IsQuadratic() ) {
8395 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8396 int nbLinkNodes = 2 + aNodesToInsert.size();
8397 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8398 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8399 linkNodes[ 0 ] = nodes[ il1 ];
8400 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8401 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8402 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8403 linkNodes[ iNode++ ] = *nIt;
8405 // decide how to split a quadrangle: compare possible variants
8406 // and choose which of splits to be a quadrangle
8407 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8408 if ( nbFaceNodes == 3 ) {
8409 iBestQuad = nbSplits;
8412 else if ( nbFaceNodes == 4 ) {
8413 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8414 double aBestRate = DBL_MAX;
8415 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8417 double aBadRate = 0;
8418 // evaluate elements quality
8419 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8420 if ( iSplit == iQuad ) {
8421 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8425 aBadRate += getBadRate( &quad, aCrit );
8428 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8430 nodes[ iSplit < iQuad ? i4 : i3 ]);
8431 aBadRate += getBadRate( &tria, aCrit );
8435 if ( aBadRate < aBestRate ) {
8437 aBestRate = aBadRate;
8442 // create new elements
8443 int aShapeId = FindShape( theFace );
8446 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8447 SMDS_MeshElement* newElem = 0;
8448 if ( iSplit == iBestQuad )
8449 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8454 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8456 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8457 myLastCreatedElems.Append(newElem);
8458 if ( aShapeId && newElem )
8459 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8462 // change nodes of theFace
8463 const SMDS_MeshNode* newNodes[ 4 ];
8464 newNodes[ 0 ] = linkNodes[ i1 ];
8465 newNodes[ 1 ] = linkNodes[ i2 ];
8466 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8467 newNodes[ 3 ] = nodes[ i4 ];
8468 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8469 const SMDS_MeshElement* newElem = 0;
8470 if (iSplit == iBestQuad)
8471 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8473 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8474 myLastCreatedElems.Append(newElem);
8475 if ( aShapeId && newElem )
8476 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8477 } // end if(!theFace->IsQuadratic())
8478 else { // theFace is quadratic
8479 // we have to split theFace on simple triangles and one simple quadrangle
8481 int nbshift = tmp*2;
8482 // shift nodes in nodes[] by nbshift
8484 for(i=0; i<nbshift; i++) {
8485 const SMDS_MeshNode* n = nodes[0];
8486 for(j=0; j<nbFaceNodes-1; j++) {
8487 nodes[j] = nodes[j+1];
8489 nodes[nbFaceNodes-1] = n;
8491 il1 = il1 - nbshift;
8492 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8493 // n0 n1 n2 n0 n1 n2
8494 // +-----+-----+ +-----+-----+
8503 // create new elements
8504 int aShapeId = FindShape( theFace );
8507 if(nbFaceNodes==6) { // quadratic triangle
8508 SMDS_MeshElement* newElem =
8509 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8510 myLastCreatedElems.Append(newElem);
8511 if ( aShapeId && newElem )
8512 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8513 if(theFace->IsMediumNode(nodes[il1])) {
8514 // create quadrangle
8515 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8516 myLastCreatedElems.Append(newElem);
8517 if ( aShapeId && newElem )
8518 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8524 // create quadrangle
8525 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8526 myLastCreatedElems.Append(newElem);
8527 if ( aShapeId && newElem )
8528 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8534 else { // nbFaceNodes==8 - quadratic quadrangle
8535 SMDS_MeshElement* newElem =
8536 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8537 myLastCreatedElems.Append(newElem);
8538 if ( aShapeId && newElem )
8539 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8540 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8541 myLastCreatedElems.Append(newElem);
8542 if ( aShapeId && newElem )
8543 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8544 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8545 myLastCreatedElems.Append(newElem);
8546 if ( aShapeId && newElem )
8547 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8548 if(theFace->IsMediumNode(nodes[il1])) {
8549 // create quadrangle
8550 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8551 myLastCreatedElems.Append(newElem);
8552 if ( aShapeId && newElem )
8553 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8559 // create quadrangle
8560 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8561 myLastCreatedElems.Append(newElem);
8562 if ( aShapeId && newElem )
8563 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8569 // create needed triangles using n1,n2,n3 and inserted nodes
8570 int nbn = 2 + aNodesToInsert.size();
8571 //const SMDS_MeshNode* aNodes[nbn];
8572 vector<const SMDS_MeshNode*> aNodes(nbn);
8573 aNodes[0] = nodes[n1];
8574 aNodes[nbn-1] = nodes[n2];
8575 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8576 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8577 aNodes[iNode++] = *nIt;
8579 for(i=1; i<nbn; i++) {
8580 SMDS_MeshElement* newElem =
8581 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8582 myLastCreatedElems.Append(newElem);
8583 if ( aShapeId && newElem )
8584 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8588 aMesh->RemoveElement(theFace);
8591 //=======================================================================
8592 //function : UpdateVolumes
8594 //=======================================================================
8595 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8596 const SMDS_MeshNode* theBetweenNode2,
8597 list<const SMDS_MeshNode*>& theNodesToInsert)
8599 myLastCreatedElems.Clear();
8600 myLastCreatedNodes.Clear();
8602 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8603 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8604 const SMDS_MeshElement* elem = invElemIt->next();
8606 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8607 SMDS_VolumeTool aVolume (elem);
8608 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8611 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8612 int iface, nbFaces = aVolume.NbFaces();
8613 vector<const SMDS_MeshNode *> poly_nodes;
8614 vector<int> quantities (nbFaces);
8616 for (iface = 0; iface < nbFaces; iface++) {
8617 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8618 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8619 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8621 for (int inode = 0; inode < nbFaceNodes; inode++) {
8622 poly_nodes.push_back(faceNodes[inode]);
8624 if (nbInserted == 0) {
8625 if (faceNodes[inode] == theBetweenNode1) {
8626 if (faceNodes[inode + 1] == theBetweenNode2) {
8627 nbInserted = theNodesToInsert.size();
8629 // add nodes to insert
8630 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8631 for (; nIt != theNodesToInsert.end(); nIt++) {
8632 poly_nodes.push_back(*nIt);
8636 else if (faceNodes[inode] == theBetweenNode2) {
8637 if (faceNodes[inode + 1] == theBetweenNode1) {
8638 nbInserted = theNodesToInsert.size();
8640 // add nodes to insert in reversed order
8641 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8643 for (; nIt != theNodesToInsert.begin(); nIt--) {
8644 poly_nodes.push_back(*nIt);
8646 poly_nodes.push_back(*nIt);
8653 quantities[iface] = nbFaceNodes + nbInserted;
8656 // Replace or update the volume
8657 SMESHDS_Mesh *aMesh = GetMeshDS();
8659 if (elem->IsPoly()) {
8660 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8664 int aShapeId = FindShape( elem );
8666 SMDS_MeshElement* newElem =
8667 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8668 myLastCreatedElems.Append(newElem);
8669 if (aShapeId && newElem)
8670 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8672 aMesh->RemoveElement(elem);
8679 //================================================================================
8681 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8683 //================================================================================
8685 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8686 vector<const SMDS_MeshNode *> & nodes,
8687 vector<int> & nbNodeInFaces )
8690 nbNodeInFaces.clear();
8691 SMDS_VolumeTool vTool ( elem );
8692 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8694 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8695 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8696 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8701 //=======================================================================
8703 * \brief Convert elements contained in a submesh to quadratic
8704 * \return int - nb of checked elements
8706 //=======================================================================
8708 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8709 SMESH_MesherHelper& theHelper,
8710 const bool theForce3d)
8713 if( !theSm ) return nbElem;
8715 vector<int> nbNodeInFaces;
8716 vector<const SMDS_MeshNode *> nodes;
8717 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8718 while(ElemItr->more())
8721 const SMDS_MeshElement* elem = ElemItr->next();
8722 if( !elem ) continue;
8724 // analyse a necessity of conversion
8725 const SMDSAbs_ElementType aType = elem->GetType();
8726 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8728 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8729 bool hasCentralNodes = false;
8730 if ( elem->IsQuadratic() )
8733 switch ( aGeomType ) {
8734 case SMDSEntity_Quad_Triangle:
8735 case SMDSEntity_Quad_Quadrangle:
8736 case SMDSEntity_Quad_Hexa:
8737 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8739 case SMDSEntity_BiQuad_Triangle:
8740 case SMDSEntity_BiQuad_Quadrangle:
8741 case SMDSEntity_TriQuad_Hexa:
8742 alreadyOK = theHelper.GetIsBiQuadratic();
8743 hasCentralNodes = true;
8748 // take into account already present modium nodes
8750 case SMDSAbs_Volume:
8751 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8753 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8755 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8761 // get elem data needed to re-create it
8763 const int id = elem->GetID();
8764 const int nbNodes = elem->NbCornerNodes();
8765 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8766 if ( aGeomType == SMDSEntity_Polyhedra )
8767 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8768 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8769 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8771 // remove a linear element
8772 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8774 // remove central nodes of biquadratic elements (biquad->quad convertion)
8775 if ( hasCentralNodes )
8776 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8777 if ( nodes[i]->NbInverseElements() == 0 )
8778 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8780 const SMDS_MeshElement* NewElem = 0;
8786 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8794 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8797 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8800 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8804 case SMDSAbs_Volume :
8808 case SMDSEntity_Tetra:
8809 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8811 case SMDSEntity_Pyramid:
8812 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8814 case SMDSEntity_Penta:
8815 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8817 case SMDSEntity_Hexa:
8818 case SMDSEntity_Quad_Hexa:
8819 case SMDSEntity_TriQuad_Hexa:
8820 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8821 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8823 case SMDSEntity_Hexagonal_Prism:
8825 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8832 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8833 if( NewElem && NewElem->getshapeId() < 1 )
8834 theSm->AddElement( NewElem );
8838 //=======================================================================
8839 //function : ConvertToQuadratic
8841 //=======================================================================
8843 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8845 SMESHDS_Mesh* meshDS = GetMeshDS();
8847 SMESH_MesherHelper aHelper(*myMesh);
8849 aHelper.SetIsQuadratic( true );
8850 aHelper.SetIsBiQuadratic( theToBiQuad );
8851 aHelper.SetElementsOnShape(true);
8852 aHelper.ToFixNodeParameters( true );
8854 // convert elements assigned to sub-meshes
8855 int nbCheckedElems = 0;
8856 if ( myMesh->HasShapeToMesh() )
8858 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8860 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8861 while ( smIt->more() ) {
8862 SMESH_subMesh* sm = smIt->next();
8863 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8864 aHelper.SetSubShape( sm->GetSubShape() );
8865 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8871 // convert elements NOT assigned to sub-meshes
8872 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8873 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8875 aHelper.SetElementsOnShape(false);
8876 SMESHDS_SubMesh *smDS = 0;
8879 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8880 while( aEdgeItr->more() )
8882 const SMDS_MeshEdge* edge = aEdgeItr->next();
8883 if ( !edge->IsQuadratic() )
8885 int id = edge->GetID();
8886 const SMDS_MeshNode* n1 = edge->GetNode(0);
8887 const SMDS_MeshNode* n2 = edge->GetNode(1);
8889 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8891 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8892 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8896 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8901 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8902 while( aFaceItr->more() )
8904 const SMDS_MeshFace* face = aFaceItr->next();
8905 if ( !face ) continue;
8907 const SMDSAbs_EntityType type = face->GetEntityType();
8911 case SMDSEntity_Quad_Triangle:
8912 case SMDSEntity_Quad_Quadrangle:
8913 alreadyOK = !theToBiQuad;
8914 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8916 case SMDSEntity_BiQuad_Triangle:
8917 case SMDSEntity_BiQuad_Quadrangle:
8918 alreadyOK = theToBiQuad;
8919 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8921 default: alreadyOK = false;
8926 const int id = face->GetID();
8927 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8929 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8931 SMDS_MeshFace * NewFace = 0;
8934 case SMDSEntity_Triangle:
8935 case SMDSEntity_Quad_Triangle:
8936 case SMDSEntity_BiQuad_Triangle:
8937 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8938 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8939 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8942 case SMDSEntity_Quadrangle:
8943 case SMDSEntity_Quad_Quadrangle:
8944 case SMDSEntity_BiQuad_Quadrangle:
8945 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8946 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8947 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8951 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8953 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8957 vector<int> nbNodeInFaces;
8958 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8959 while(aVolumeItr->more())
8961 const SMDS_MeshVolume* volume = aVolumeItr->next();
8962 if ( !volume ) continue;
8964 const SMDSAbs_EntityType type = volume->GetEntityType();
8965 if ( volume->IsQuadratic() )
8970 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8971 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8972 default: alreadyOK = true;
8976 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8980 const int id = volume->GetID();
8981 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8982 if ( type == SMDSEntity_Polyhedra )
8983 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8984 else if ( type == SMDSEntity_Hexagonal_Prism )
8985 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8987 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8989 SMDS_MeshVolume * NewVolume = 0;
8992 case SMDSEntity_Tetra:
8993 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8995 case SMDSEntity_Hexa:
8996 case SMDSEntity_Quad_Hexa:
8997 case SMDSEntity_TriQuad_Hexa:
8998 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8999 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9000 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9001 if ( nodes[i]->NbInverseElements() == 0 )
9002 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9004 case SMDSEntity_Pyramid:
9005 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9006 nodes[3], nodes[4], id, theForce3d);
9008 case SMDSEntity_Penta:
9009 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9010 nodes[3], nodes[4], nodes[5], id, theForce3d);
9012 case SMDSEntity_Hexagonal_Prism:
9014 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9016 ReplaceElemInGroups(volume, NewVolume, meshDS);
9021 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9022 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9023 // aHelper.FixQuadraticElements(myError);
9024 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9028 //================================================================================
9030 * \brief Makes given elements quadratic
9031 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9032 * \param theElements - elements to make quadratic
9034 //================================================================================
9036 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9037 TIDSortedElemSet& theElements,
9038 const bool theToBiQuad)
9040 if ( theElements.empty() ) return;
9042 // we believe that all theElements are of the same type
9043 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9045 // get all nodes shared by theElements
9046 TIDSortedNodeSet allNodes;
9047 TIDSortedElemSet::iterator eIt = theElements.begin();
9048 for ( ; eIt != theElements.end(); ++eIt )
9049 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9051 // complete theElements with elements of lower dim whose all nodes are in allNodes
9053 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9054 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9055 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9056 for ( ; nIt != allNodes.end(); ++nIt )
9058 const SMDS_MeshNode* n = *nIt;
9059 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9060 while ( invIt->more() )
9062 const SMDS_MeshElement* e = invIt->next();
9063 const SMDSAbs_ElementType type = e->GetType();
9064 if ( e->IsQuadratic() )
9066 quadAdjacentElems[ type ].insert( e );
9069 switch ( e->GetEntityType() ) {
9070 case SMDSEntity_Quad_Triangle:
9071 case SMDSEntity_Quad_Quadrangle:
9072 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9073 case SMDSEntity_BiQuad_Triangle:
9074 case SMDSEntity_BiQuad_Quadrangle:
9075 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9076 default: alreadyOK = true;
9081 if ( type >= elemType )
9082 continue; // same type or more complex linear element
9084 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9085 continue; // e is already checked
9089 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9090 while ( nodeIt->more() && allIn )
9091 allIn = allNodes.count( nodeIt->next() );
9093 theElements.insert(e );
9097 SMESH_MesherHelper helper(*myMesh);
9098 helper.SetIsQuadratic( true );
9099 helper.SetIsBiQuadratic( theToBiQuad );
9101 // add links of quadratic adjacent elements to the helper
9103 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9104 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9105 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9107 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9109 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9110 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9111 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9113 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9115 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9116 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9117 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9119 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9122 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9124 SMESHDS_Mesh* meshDS = GetMeshDS();
9125 SMESHDS_SubMesh* smDS = 0;
9126 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9128 const SMDS_MeshElement* elem = *eIt;
9131 int nbCentralNodes = 0;
9132 switch ( elem->GetEntityType() ) {
9133 // linear convertible
9134 case SMDSEntity_Edge:
9135 case SMDSEntity_Triangle:
9136 case SMDSEntity_Quadrangle:
9137 case SMDSEntity_Tetra:
9138 case SMDSEntity_Pyramid:
9139 case SMDSEntity_Hexa:
9140 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9141 // quadratic that can become bi-quadratic
9142 case SMDSEntity_Quad_Triangle:
9143 case SMDSEntity_Quad_Quadrangle:
9144 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9146 case SMDSEntity_BiQuad_Triangle:
9147 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9148 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9150 default: alreadyOK = true;
9152 if ( alreadyOK ) continue;
9154 const SMDSAbs_ElementType type = elem->GetType();
9155 const int id = elem->GetID();
9156 const int nbNodes = elem->NbCornerNodes();
9157 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9159 helper.SetSubShape( elem->getshapeId() );
9161 if ( !smDS || !smDS->Contains( elem ))
9162 smDS = meshDS->MeshElements( elem->getshapeId() );
9163 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9165 SMDS_MeshElement * newElem = 0;
9168 case 4: // cases for most frequently used element types go first (for optimization)
9169 if ( type == SMDSAbs_Volume )
9170 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9172 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9175 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9176 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9179 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9182 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9185 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9186 nodes[4], id, theForce3d);
9189 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9190 nodes[4], nodes[5], id, theForce3d);
9194 ReplaceElemInGroups( elem, newElem, meshDS);
9195 if( newElem && smDS )
9196 smDS->AddElement( newElem );
9198 // remove central nodes
9199 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9200 if ( nodes[i]->NbInverseElements() == 0 )
9201 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9203 } // loop on theElements
9206 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9207 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9208 // helper.FixQuadraticElements( myError );
9209 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9213 //=======================================================================
9215 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9216 * \return int - nb of checked elements
9218 //=======================================================================
9220 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9221 SMDS_ElemIteratorPtr theItr,
9222 const int theShapeID)
9225 SMESHDS_Mesh* meshDS = GetMeshDS();
9227 while( theItr->more() )
9229 const SMDS_MeshElement* elem = theItr->next();
9231 if( elem && elem->IsQuadratic())
9233 int id = elem->GetID();
9234 int nbCornerNodes = elem->NbCornerNodes();
9235 SMDSAbs_ElementType aType = elem->GetType();
9237 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9239 //remove a quadratic element
9240 if ( !theSm || !theSm->Contains( elem ))
9241 theSm = meshDS->MeshElements( elem->getshapeId() );
9242 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9244 // remove medium nodes
9245 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9246 if ( nodes[i]->NbInverseElements() == 0 )
9247 meshDS->RemoveFreeNode( nodes[i], theSm );
9249 // add a linear element
9250 nodes.resize( nbCornerNodes );
9251 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9252 ReplaceElemInGroups(elem, newElem, meshDS);
9253 if( theSm && newElem )
9254 theSm->AddElement( newElem );
9260 //=======================================================================
9261 //function : ConvertFromQuadratic
9263 //=======================================================================
9265 bool SMESH_MeshEditor::ConvertFromQuadratic()
9267 int nbCheckedElems = 0;
9268 if ( myMesh->HasShapeToMesh() )
9270 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9272 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9273 while ( smIt->more() ) {
9274 SMESH_subMesh* sm = smIt->next();
9275 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9276 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9282 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9283 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9285 SMESHDS_SubMesh *aSM = 0;
9286 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9294 //================================================================================
9296 * \brief Return true if all medium nodes of the element are in the node set
9298 //================================================================================
9300 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9302 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9303 if ( !nodeSet.count( elem->GetNode(i) ))
9309 //================================================================================
9311 * \brief Makes given elements linear
9313 //================================================================================
9315 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9317 if ( theElements.empty() ) return;
9319 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9320 set<int> mediumNodeIDs;
9321 TIDSortedElemSet::iterator eIt = theElements.begin();
9322 for ( ; eIt != theElements.end(); ++eIt )
9324 const SMDS_MeshElement* e = *eIt;
9325 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9326 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9329 // replace given elements by linear ones
9330 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9331 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9333 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9334 // except those elements sharing medium nodes of quadratic element whose medium nodes
9335 // are not all in mediumNodeIDs
9337 // get remaining medium nodes
9338 TIDSortedNodeSet mediumNodes;
9339 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9340 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9341 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9342 mediumNodes.insert( mediumNodes.end(), n );
9344 // find more quadratic elements to convert
9345 TIDSortedElemSet moreElemsToConvert;
9346 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9347 for ( ; nIt != mediumNodes.end(); ++nIt )
9349 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9350 while ( invIt->more() )
9352 const SMDS_MeshElement* e = invIt->next();
9353 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9355 // find a more complex element including e and
9356 // whose medium nodes are not in mediumNodes
9357 bool complexFound = false;
9358 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9360 SMDS_ElemIteratorPtr invIt2 =
9361 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9362 while ( invIt2->more() )
9364 const SMDS_MeshElement* eComplex = invIt2->next();
9365 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9367 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9368 if ( nbCommonNodes == e->NbNodes())
9370 complexFound = true;
9371 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9377 if ( !complexFound )
9378 moreElemsToConvert.insert( e );
9382 elemIt = elemSetIterator( moreElemsToConvert );
9383 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9386 //=======================================================================
9387 //function : SewSideElements
9389 //=======================================================================
9391 SMESH_MeshEditor::Sew_Error
9392 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9393 TIDSortedElemSet& theSide2,
9394 const SMDS_MeshNode* theFirstNode1,
9395 const SMDS_MeshNode* theFirstNode2,
9396 const SMDS_MeshNode* theSecondNode1,
9397 const SMDS_MeshNode* theSecondNode2)
9399 myLastCreatedElems.Clear();
9400 myLastCreatedNodes.Clear();
9402 MESSAGE ("::::SewSideElements()");
9403 if ( theSide1.size() != theSide2.size() )
9404 return SEW_DIFF_NB_OF_ELEMENTS;
9406 Sew_Error aResult = SEW_OK;
9408 // 1. Build set of faces representing each side
9409 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9410 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9412 // =======================================================================
9413 // 1. Build set of faces representing each side:
9414 // =======================================================================
9415 // a. build set of nodes belonging to faces
9416 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9417 // c. create temporary faces representing side of volumes if correspondent
9418 // face does not exist
9420 SMESHDS_Mesh* aMesh = GetMeshDS();
9421 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9422 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9423 TIDSortedElemSet faceSet1, faceSet2;
9424 set<const SMDS_MeshElement*> volSet1, volSet2;
9425 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9426 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9427 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9428 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9429 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9430 int iSide, iFace, iNode;
9432 list<const SMDS_MeshElement* > tempFaceList;
9433 for ( iSide = 0; iSide < 2; iSide++ ) {
9434 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9435 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9436 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9437 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9438 set<const SMDS_MeshElement*>::iterator vIt;
9439 TIDSortedElemSet::iterator eIt;
9440 set<const SMDS_MeshNode*>::iterator nIt;
9442 // check that given nodes belong to given elements
9443 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9444 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9445 int firstIndex = -1, secondIndex = -1;
9446 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9447 const SMDS_MeshElement* elem = *eIt;
9448 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9449 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9450 if ( firstIndex > -1 && secondIndex > -1 ) break;
9452 if ( firstIndex < 0 || secondIndex < 0 ) {
9453 // we can simply return until temporary faces created
9454 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9457 // -----------------------------------------------------------
9458 // 1a. Collect nodes of existing faces
9459 // and build set of face nodes in order to detect missing
9460 // faces corresponding to sides of volumes
9461 // -----------------------------------------------------------
9463 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9465 // loop on the given element of a side
9466 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9467 //const SMDS_MeshElement* elem = *eIt;
9468 const SMDS_MeshElement* elem = *eIt;
9469 if ( elem->GetType() == SMDSAbs_Face ) {
9470 faceSet->insert( elem );
9471 set <const SMDS_MeshNode*> faceNodeSet;
9472 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9473 while ( nodeIt->more() ) {
9474 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9475 nodeSet->insert( n );
9476 faceNodeSet.insert( n );
9478 setOfFaceNodeSet.insert( faceNodeSet );
9480 else if ( elem->GetType() == SMDSAbs_Volume )
9481 volSet->insert( elem );
9483 // ------------------------------------------------------------------------------
9484 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9485 // ------------------------------------------------------------------------------
9487 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9488 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9489 while ( fIt->more() ) { // loop on faces sharing a node
9490 const SMDS_MeshElement* f = fIt->next();
9491 if ( faceSet->find( f ) == faceSet->end() ) {
9492 // check if all nodes are in nodeSet and
9493 // complete setOfFaceNodeSet if they are
9494 set <const SMDS_MeshNode*> faceNodeSet;
9495 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9496 bool allInSet = true;
9497 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9498 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9499 if ( nodeSet->find( n ) == nodeSet->end() )
9502 faceNodeSet.insert( n );
9505 faceSet->insert( f );
9506 setOfFaceNodeSet.insert( faceNodeSet );
9512 // -------------------------------------------------------------------------
9513 // 1c. Create temporary faces representing sides of volumes if correspondent
9514 // face does not exist
9515 // -------------------------------------------------------------------------
9517 if ( !volSet->empty() ) {
9518 //int nodeSetSize = nodeSet->size();
9520 // loop on given volumes
9521 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9522 SMDS_VolumeTool vol (*vIt);
9523 // loop on volume faces: find free faces
9524 // --------------------------------------
9525 list<const SMDS_MeshElement* > freeFaceList;
9526 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9527 if ( !vol.IsFreeFace( iFace ))
9529 // check if there is already a face with same nodes in a face set
9530 const SMDS_MeshElement* aFreeFace = 0;
9531 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9532 int nbNodes = vol.NbFaceNodes( iFace );
9533 set <const SMDS_MeshNode*> faceNodeSet;
9534 vol.GetFaceNodes( iFace, faceNodeSet );
9535 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9537 // no such a face is given but it still can exist, check it
9538 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9539 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9542 // create a temporary face
9543 if ( nbNodes == 3 ) {
9544 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9545 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9547 else if ( nbNodes == 4 ) {
9548 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9549 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9552 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9553 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9554 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9557 tempFaceList.push_back( aFreeFace );
9561 freeFaceList.push_back( aFreeFace );
9563 } // loop on faces of a volume
9565 // choose one of several free faces of a volume
9566 // --------------------------------------------
9567 if ( freeFaceList.size() > 1 ) {
9568 // choose a face having max nb of nodes shared by other elems of a side
9569 int maxNbNodes = -1;
9570 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9571 while ( fIt != freeFaceList.end() ) { // loop on free faces
9572 int nbSharedNodes = 0;
9573 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9574 while ( nodeIt->more() ) { // loop on free face nodes
9575 const SMDS_MeshNode* n =
9576 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9577 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9578 while ( invElemIt->more() ) {
9579 const SMDS_MeshElement* e = invElemIt->next();
9580 nbSharedNodes += faceSet->count( e );
9581 nbSharedNodes += elemSet->count( e );
9584 if ( nbSharedNodes > maxNbNodes ) {
9585 maxNbNodes = nbSharedNodes;
9586 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9588 else if ( nbSharedNodes == maxNbNodes ) {
9592 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9595 if ( freeFaceList.size() > 1 )
9597 // could not choose one face, use another way
9598 // choose a face most close to the bary center of the opposite side
9599 gp_XYZ aBC( 0., 0., 0. );
9600 set <const SMDS_MeshNode*> addedNodes;
9601 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9602 eIt = elemSet2->begin();
9603 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9604 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9605 while ( nodeIt->more() ) { // loop on free face nodes
9606 const SMDS_MeshNode* n =
9607 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9608 if ( addedNodes.insert( n ).second )
9609 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9612 aBC /= addedNodes.size();
9613 double minDist = DBL_MAX;
9614 fIt = freeFaceList.begin();
9615 while ( fIt != freeFaceList.end() ) { // loop on free faces
9617 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9618 while ( nodeIt->more() ) { // loop on free face nodes
9619 const SMDS_MeshNode* n =
9620 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9621 gp_XYZ p( n->X(),n->Y(),n->Z() );
9622 dist += ( aBC - p ).SquareModulus();
9624 if ( dist < minDist ) {
9626 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9629 fIt = freeFaceList.erase( fIt++ );
9632 } // choose one of several free faces of a volume
9634 if ( freeFaceList.size() == 1 ) {
9635 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9636 faceSet->insert( aFreeFace );
9637 // complete a node set with nodes of a found free face
9638 // for ( iNode = 0; iNode < ; iNode++ )
9639 // nodeSet->insert( fNodes[ iNode ] );
9642 } // loop on volumes of a side
9644 // // complete a set of faces if new nodes in a nodeSet appeared
9645 // // ----------------------------------------------------------
9646 // if ( nodeSetSize != nodeSet->size() ) {
9647 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9648 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9649 // while ( fIt->more() ) { // loop on faces sharing a node
9650 // const SMDS_MeshElement* f = fIt->next();
9651 // if ( faceSet->find( f ) == faceSet->end() ) {
9652 // // check if all nodes are in nodeSet and
9653 // // complete setOfFaceNodeSet if they are
9654 // set <const SMDS_MeshNode*> faceNodeSet;
9655 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9656 // bool allInSet = true;
9657 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9658 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9659 // if ( nodeSet->find( n ) == nodeSet->end() )
9660 // allInSet = false;
9662 // faceNodeSet.insert( n );
9664 // if ( allInSet ) {
9665 // faceSet->insert( f );
9666 // setOfFaceNodeSet.insert( faceNodeSet );
9672 } // Create temporary faces, if there are volumes given
9675 if ( faceSet1.size() != faceSet2.size() ) {
9676 // delete temporary faces: they are in reverseElements of actual nodes
9677 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9678 // while ( tmpFaceIt->more() )
9679 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9680 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9681 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9682 // aMesh->RemoveElement(*tmpFaceIt);
9683 MESSAGE("Diff nb of faces");
9684 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9687 // ============================================================
9688 // 2. Find nodes to merge:
9689 // bind a node to remove to a node to put instead
9690 // ============================================================
9692 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9693 if ( theFirstNode1 != theFirstNode2 )
9694 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9695 if ( theSecondNode1 != theSecondNode2 )
9696 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9698 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9699 set< long > linkIdSet; // links to process
9700 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9702 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9703 list< NLink > linkList[2];
9704 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9705 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9706 // loop on links in linkList; find faces by links and append links
9707 // of the found faces to linkList
9708 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9709 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9711 NLink link[] = { *linkIt[0], *linkIt[1] };
9712 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9713 if ( !linkIdSet.count( linkID ) )
9716 // by links, find faces in the face sets,
9717 // and find indices of link nodes in the found faces;
9718 // in a face set, there is only one or no face sharing a link
9719 // ---------------------------------------------------------------
9721 const SMDS_MeshElement* face[] = { 0, 0 };
9722 vector<const SMDS_MeshNode*> fnodes[2];
9723 int iLinkNode[2][2];
9724 TIDSortedElemSet avoidSet;
9725 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9726 const SMDS_MeshNode* n1 = link[iSide].first;
9727 const SMDS_MeshNode* n2 = link[iSide].second;
9728 //cout << "Side " << iSide << " ";
9729 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9730 // find a face by two link nodes
9731 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9732 *faceSetPtr[ iSide ], avoidSet,
9733 &iLinkNode[iSide][0],
9734 &iLinkNode[iSide][1] );
9737 //cout << " F " << face[ iSide]->GetID() <<endl;
9738 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9739 // put face nodes to fnodes
9740 if ( face[ iSide ]->IsQuadratic() )
9742 // use interlaced nodes iterator
9743 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9744 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9745 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9746 while ( nIter->more() )
9747 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9751 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9752 face[ iSide ]->end_nodes() );
9754 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9758 // check similarity of elements of the sides
9759 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9760 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9761 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9762 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9765 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9767 break; // do not return because it's necessary to remove tmp faces
9770 // set nodes to merge
9771 // -------------------
9773 if ( face[0] && face[1] ) {
9774 const int nbNodes = face[0]->NbNodes();
9775 if ( nbNodes != face[1]->NbNodes() ) {
9776 MESSAGE("Diff nb of face nodes");
9777 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9778 break; // do not return because it s necessary to remove tmp faces
9780 bool reverse[] = { false, false }; // order of nodes in the link
9781 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9782 // analyse link orientation in faces
9783 int i1 = iLinkNode[ iSide ][ 0 ];
9784 int i2 = iLinkNode[ iSide ][ 1 ];
9785 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9787 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9788 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9789 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9791 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9792 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9795 // add other links of the faces to linkList
9796 // -----------------------------------------
9798 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9799 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9800 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9801 if ( !iter_isnew.second ) { // already in a set: no need to process
9802 linkIdSet.erase( iter_isnew.first );
9804 else // new in set == encountered for the first time: add
9806 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9807 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9808 linkList[0].push_back ( NLink( n1, n2 ));
9809 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9814 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9817 } // loop on link lists
9819 if ( aResult == SEW_OK &&
9820 ( //linkIt[0] != linkList[0].end() ||
9821 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9822 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9823 " " << (faceSetPtr[1]->empty()));
9824 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9827 // ====================================================================
9828 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9829 // ====================================================================
9831 // delete temporary faces
9832 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9833 // while ( tmpFaceIt->more() )
9834 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9835 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9836 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9837 aMesh->RemoveElement(*tmpFaceIt);
9839 if ( aResult != SEW_OK)
9842 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9843 // loop on nodes replacement map
9844 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9845 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9846 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9847 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9848 nodeIDsToRemove.push_back( nToRemove->GetID() );
9849 // loop on elements sharing nToRemove
9850 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9851 while ( invElemIt->more() ) {
9852 const SMDS_MeshElement* e = invElemIt->next();
9853 // get a new suite of nodes: make replacement
9854 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9855 vector< const SMDS_MeshNode*> nodes( nbNodes );
9856 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9857 while ( nIt->more() ) {
9858 const SMDS_MeshNode* n =
9859 static_cast<const SMDS_MeshNode*>( nIt->next() );
9860 nnIt = nReplaceMap.find( n );
9861 if ( nnIt != nReplaceMap.end() ) {
9867 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9868 // elemIDsToRemove.push_back( e->GetID() );
9872 SMDSAbs_ElementType etyp = e->GetType();
9873 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9876 myLastCreatedElems.Append(newElem);
9877 AddToSameGroups(newElem, e, aMesh);
9878 int aShapeId = e->getshapeId();
9881 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9884 aMesh->RemoveElement(e);
9889 Remove( nodeIDsToRemove, true );
9894 //================================================================================
9896 * \brief Find corresponding nodes in two sets of faces
9897 * \param theSide1 - first face set
9898 * \param theSide2 - second first face
9899 * \param theFirstNode1 - a boundary node of set 1
9900 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9901 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9902 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9903 * \param nReplaceMap - output map of corresponding nodes
9904 * \return bool - is a success or not
9906 //================================================================================
9909 //#define DEBUG_MATCHING_NODES
9912 SMESH_MeshEditor::Sew_Error
9913 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9914 set<const SMDS_MeshElement*>& theSide2,
9915 const SMDS_MeshNode* theFirstNode1,
9916 const SMDS_MeshNode* theFirstNode2,
9917 const SMDS_MeshNode* theSecondNode1,
9918 const SMDS_MeshNode* theSecondNode2,
9919 TNodeNodeMap & nReplaceMap)
9921 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9923 nReplaceMap.clear();
9924 if ( theFirstNode1 != theFirstNode2 )
9925 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9926 if ( theSecondNode1 != theSecondNode2 )
9927 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9929 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9930 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9932 list< NLink > linkList[2];
9933 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9934 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9936 // loop on links in linkList; find faces by links and append links
9937 // of the found faces to linkList
9938 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9939 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9940 NLink link[] = { *linkIt[0], *linkIt[1] };
9941 if ( linkSet.find( link[0] ) == linkSet.end() )
9944 // by links, find faces in the face sets,
9945 // and find indices of link nodes in the found faces;
9946 // in a face set, there is only one or no face sharing a link
9947 // ---------------------------------------------------------------
9949 const SMDS_MeshElement* face[] = { 0, 0 };
9950 list<const SMDS_MeshNode*> notLinkNodes[2];
9951 //bool reverse[] = { false, false }; // order of notLinkNodes
9953 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9955 const SMDS_MeshNode* n1 = link[iSide].first;
9956 const SMDS_MeshNode* n2 = link[iSide].second;
9957 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9958 set< const SMDS_MeshElement* > facesOfNode1;
9959 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9961 // during a loop of the first node, we find all faces around n1,
9962 // during a loop of the second node, we find one face sharing both n1 and n2
9963 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9964 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9965 while ( fIt->more() ) { // loop on faces sharing a node
9966 const SMDS_MeshElement* f = fIt->next();
9967 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9968 ! facesOfNode1.insert( f ).second ) // f encounters twice
9970 if ( face[ iSide ] ) {
9971 MESSAGE( "2 faces per link " );
9972 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9975 faceSet->erase( f );
9977 // get not link nodes
9978 int nbN = f->NbNodes();
9979 if ( f->IsQuadratic() )
9981 nbNodes[ iSide ] = nbN;
9982 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9983 int i1 = f->GetNodeIndex( n1 );
9984 int i2 = f->GetNodeIndex( n2 );
9985 int iEnd = nbN, iBeg = -1, iDelta = 1;
9986 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9988 std::swap( iEnd, iBeg ); iDelta = -1;
9993 if ( i == iEnd ) i = iBeg + iDelta;
9994 if ( i == i1 ) break;
9995 nodes.push_back ( f->GetNode( i ) );
10001 // check similarity of elements of the sides
10002 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10003 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10004 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10005 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10008 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10012 // set nodes to merge
10013 // -------------------
10015 if ( face[0] && face[1] ) {
10016 if ( nbNodes[0] != nbNodes[1] ) {
10017 MESSAGE("Diff nb of face nodes");
10018 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10020 #ifdef DEBUG_MATCHING_NODES
10021 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10022 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10023 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10025 int nbN = nbNodes[0];
10027 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10028 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10029 for ( int i = 0 ; i < nbN - 2; ++i ) {
10030 #ifdef DEBUG_MATCHING_NODES
10031 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10033 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10037 // add other links of the face 1 to linkList
10038 // -----------------------------------------
10040 const SMDS_MeshElement* f0 = face[0];
10041 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10042 for ( int i = 0; i < nbN; i++ )
10044 const SMDS_MeshNode* n2 = f0->GetNode( i );
10045 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10046 linkSet.insert( SMESH_TLink( n1, n2 ));
10047 if ( !iter_isnew.second ) { // already in a set: no need to process
10048 linkSet.erase( iter_isnew.first );
10050 else // new in set == encountered for the first time: add
10052 #ifdef DEBUG_MATCHING_NODES
10053 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10054 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10056 linkList[0].push_back ( NLink( n1, n2 ));
10057 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10062 } // loop on link lists
10067 //================================================================================
10069 * \brief Create elements equal (on same nodes) to given ones
10070 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10071 * elements of the uppest dimension are duplicated.
10073 //================================================================================
10075 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10077 CrearLastCreated();
10078 SMESHDS_Mesh* mesh = GetMeshDS();
10080 // get an element type and an iterator over elements
10082 SMDSAbs_ElementType type;
10083 SMDS_ElemIteratorPtr elemIt;
10084 vector< const SMDS_MeshElement* > allElems;
10085 if ( theElements.empty() )
10087 if ( mesh->NbNodes() == 0 )
10089 // get most complex type
10090 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10091 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10092 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10094 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10095 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10100 // put all elements in the vector <allElems>
10101 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10102 elemIt = mesh->elementsIterator( type );
10103 while ( elemIt->more() )
10104 allElems.push_back( elemIt->next());
10105 elemIt = elemSetIterator( allElems );
10109 type = (*theElements.begin())->GetType();
10110 elemIt = elemSetIterator( theElements );
10113 // duplicate elements
10115 if ( type == SMDSAbs_Ball )
10117 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10118 while ( elemIt->more() )
10120 const SMDS_MeshElement* elem = elemIt->next();
10121 if ( elem->GetType() != SMDSAbs_Ball )
10123 if (( elem = mesh->AddBall( elem->GetNode(0),
10124 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10125 myLastCreatedElems.Append( elem );
10130 vector< const SMDS_MeshNode* > nodes;
10131 while ( elemIt->more() )
10133 const SMDS_MeshElement* elem = elemIt->next();
10134 if ( elem->GetType() != type )
10137 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10139 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10141 std::vector<int> quantities =
10142 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10143 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10147 AddElement( nodes, type, elem->IsPoly() );
10148 elem = 0; // myLastCreatedElems is already filled
10151 myLastCreatedElems.Append( elem );
10156 //================================================================================
10158 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10159 \param theElems - the list of elements (edges or faces) to be replicated
10160 The nodes for duplication could be found from these elements
10161 \param theNodesNot - list of nodes to NOT replicate
10162 \param theAffectedElems - the list of elements (cells and edges) to which the
10163 replicated nodes should be associated to.
10164 \return TRUE if operation has been completed successfully, FALSE otherwise
10166 //================================================================================
10168 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10169 const TIDSortedElemSet& theNodesNot,
10170 const TIDSortedElemSet& theAffectedElems )
10172 myLastCreatedElems.Clear();
10173 myLastCreatedNodes.Clear();
10175 if ( theElems.size() == 0 )
10178 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10183 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10184 // duplicate elements and nodes
10185 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10186 // replce nodes by duplications
10187 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10191 //================================================================================
10193 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10194 \param theMeshDS - mesh instance
10195 \param theElems - the elements replicated or modified (nodes should be changed)
10196 \param theNodesNot - nodes to NOT replicate
10197 \param theNodeNodeMap - relation of old node to new created node
10198 \param theIsDoubleElem - flag os to replicate element or modify
10199 \return TRUE if operation has been completed successfully, FALSE otherwise
10201 //================================================================================
10203 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10204 const TIDSortedElemSet& theElems,
10205 const TIDSortedElemSet& theNodesNot,
10206 std::map< const SMDS_MeshNode*,
10207 const SMDS_MeshNode* >& theNodeNodeMap,
10208 const bool theIsDoubleElem )
10210 MESSAGE("doubleNodes");
10211 // iterate on through element and duplicate them (by nodes duplication)
10213 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10214 for ( ; elemItr != theElems.end(); ++elemItr )
10216 const SMDS_MeshElement* anElem = *elemItr;
10220 bool isDuplicate = false;
10221 // duplicate nodes to duplicate element
10222 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10223 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10225 while ( anIter->more() )
10228 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10229 SMDS_MeshNode* aNewNode = aCurrNode;
10230 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10231 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10232 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10235 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10236 theNodeNodeMap[ aCurrNode ] = aNewNode;
10237 myLastCreatedNodes.Append( aNewNode );
10239 isDuplicate |= (aCurrNode != aNewNode);
10240 newNodes[ ind++ ] = aNewNode;
10242 if ( !isDuplicate )
10245 if ( theIsDoubleElem )
10246 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10249 MESSAGE("ChangeElementNodes");
10250 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10257 //================================================================================
10259 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10260 \param theNodes - identifiers of nodes to be doubled
10261 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10262 nodes. If list of element identifiers is empty then nodes are doubled but
10263 they not assigned to elements
10264 \return TRUE if operation has been completed successfully, FALSE otherwise
10266 //================================================================================
10268 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10269 const std::list< int >& theListOfModifiedElems )
10271 MESSAGE("DoubleNodes");
10272 myLastCreatedElems.Clear();
10273 myLastCreatedNodes.Clear();
10275 if ( theListOfNodes.size() == 0 )
10278 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10282 // iterate through nodes and duplicate them
10284 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10286 std::list< int >::const_iterator aNodeIter;
10287 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10289 int aCurr = *aNodeIter;
10290 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10296 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10299 anOldNodeToNewNode[ aNode ] = aNewNode;
10300 myLastCreatedNodes.Append( aNewNode );
10304 // Create map of new nodes for modified elements
10306 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10308 std::list< int >::const_iterator anElemIter;
10309 for ( anElemIter = theListOfModifiedElems.begin();
10310 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10312 int aCurr = *anElemIter;
10313 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10317 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10319 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10321 while ( anIter->more() )
10323 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10324 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10326 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10327 aNodeArr[ ind++ ] = aNewNode;
10330 aNodeArr[ ind++ ] = aCurrNode;
10332 anElemToNodes[ anElem ] = aNodeArr;
10335 // Change nodes of elements
10337 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10338 anElemToNodesIter = anElemToNodes.begin();
10339 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10341 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10342 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10345 MESSAGE("ChangeElementNodes");
10346 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10355 //================================================================================
10357 \brief Check if element located inside shape
10358 \return TRUE if IN or ON shape, FALSE otherwise
10360 //================================================================================
10362 template<class Classifier>
10363 bool isInside(const SMDS_MeshElement* theElem,
10364 Classifier& theClassifier,
10365 const double theTol)
10367 gp_XYZ centerXYZ (0, 0, 0);
10368 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10369 while (aNodeItr->more())
10370 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10372 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10373 theClassifier.Perform(aPnt, theTol);
10374 TopAbs_State aState = theClassifier.State();
10375 return (aState == TopAbs_IN || aState == TopAbs_ON );
10378 //================================================================================
10380 * \brief Classifier of the 3D point on the TopoDS_Face
10381 * with interaface suitable for isInside()
10383 //================================================================================
10385 struct _FaceClassifier
10387 Extrema_ExtPS _extremum;
10388 BRepAdaptor_Surface _surface;
10389 TopAbs_State _state;
10391 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10393 _extremum.Initialize( _surface,
10394 _surface.FirstUParameter(), _surface.LastUParameter(),
10395 _surface.FirstVParameter(), _surface.LastVParameter(),
10396 _surface.Tolerance(), _surface.Tolerance() );
10398 void Perform(const gp_Pnt& aPnt, double theTol)
10400 _state = TopAbs_OUT;
10401 _extremum.Perform(aPnt);
10402 if ( _extremum.IsDone() )
10403 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10404 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10405 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10407 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10410 TopAbs_State State() const
10417 //================================================================================
10419 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10420 This method is the first step of DoubleNodeElemGroupsInRegion.
10421 \param theElems - list of groups of elements (edges or faces) to be replicated
10422 \param theNodesNot - list of groups of nodes not to replicated
10423 \param theShape - shape to detect affected elements (element which geometric center
10424 located on or inside shape). If the shape is null, detection is done on faces orientations
10425 (select elements with a gravity center on the side given by faces normals).
10426 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10427 The replicated nodes should be associated to affected elements.
10428 \return groups of affected elements
10429 \sa DoubleNodeElemGroupsInRegion()
10431 //================================================================================
10433 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10434 const TIDSortedElemSet& theNodesNot,
10435 const TopoDS_Shape& theShape,
10436 TIDSortedElemSet& theAffectedElems)
10438 if ( theShape.IsNull() )
10440 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10441 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10442 std::set<const SMDS_MeshElement*> edgesToCheck;
10443 alreadyCheckedNodes.clear();
10444 alreadyCheckedElems.clear();
10445 edgesToCheck.clear();
10447 // --- iterates on elements to be replicated and get elements by back references from their nodes
10449 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10451 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10453 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10454 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10457 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10458 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10459 std::set<const SMDS_MeshNode*> nodesElem;
10461 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10462 while ( nodeItr->more() )
10464 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10465 nodesElem.insert(aNode);
10467 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10468 for (; nodit != nodesElem.end(); nodit++)
10470 MESSAGE(" noeud ");
10471 const SMDS_MeshNode* aNode = *nodit;
10472 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10474 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10476 alreadyCheckedNodes.insert(aNode);
10477 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10478 while ( backElemItr->more() )
10480 MESSAGE(" backelem ");
10481 const SMDS_MeshElement* curElem = backElemItr->next();
10482 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10484 if (theElems.find(curElem) != theElems.end())
10486 alreadyCheckedElems.insert(curElem);
10487 double x=0, y=0, z=0;
10489 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10490 while ( nodeItr2->more() )
10492 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10493 x += anotherNode->X();
10494 y += anotherNode->Y();
10495 z += anotherNode->Z();
10499 p.SetCoord( x/nb -aNode->X(),
10501 z/nb -aNode->Z() );
10502 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10505 MESSAGE(" --- inserted")
10506 theAffectedElems.insert( curElem );
10508 else if (curElem->GetType() == SMDSAbs_Edge)
10509 edgesToCheck.insert(curElem);
10513 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10514 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10515 for( ; eit != edgesToCheck.end(); eit++)
10517 bool onside = true;
10518 const SMDS_MeshElement* anEdge = *eit;
10519 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10520 while ( nodeItr->more() )
10522 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10523 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10531 MESSAGE(" --- edge onside inserted")
10532 theAffectedElems.insert(anEdge);
10538 const double aTol = Precision::Confusion();
10539 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10540 auto_ptr<_FaceClassifier> aFaceClassifier;
10541 if ( theShape.ShapeType() == TopAbs_SOLID )
10543 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10544 bsc3d->PerformInfinitePoint(aTol);
10546 else if (theShape.ShapeType() == TopAbs_FACE )
10548 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10551 // iterates on indicated elements and get elements by back references from their nodes
10552 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10554 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10556 MESSAGE("element " << ielem++);
10557 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10560 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10561 while ( nodeItr->more() )
10563 MESSAGE(" noeud ");
10564 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10565 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10567 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10568 while ( backElemItr->more() )
10570 MESSAGE(" backelem ");
10571 const SMDS_MeshElement* curElem = backElemItr->next();
10572 if ( curElem && theElems.find(curElem) == theElems.end() &&
10574 isInside( curElem, *bsc3d, aTol ) :
10575 isInside( curElem, *aFaceClassifier, aTol )))
10576 theAffectedElems.insert( curElem );
10584 //================================================================================
10586 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10587 \param theElems - group of of elements (edges or faces) to be replicated
10588 \param theNodesNot - group of nodes not to replicate
10589 \param theShape - shape to detect affected elements (element which geometric center
10590 located on or inside shape).
10591 The replicated nodes should be associated to affected elements.
10592 \return TRUE if operation has been completed successfully, FALSE otherwise
10594 //================================================================================
10596 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10597 const TIDSortedElemSet& theNodesNot,
10598 const TopoDS_Shape& theShape )
10600 if ( theShape.IsNull() )
10603 const double aTol = Precision::Confusion();
10604 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10605 auto_ptr<_FaceClassifier> aFaceClassifier;
10606 if ( theShape.ShapeType() == TopAbs_SOLID )
10608 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10609 bsc3d->PerformInfinitePoint(aTol);
10611 else if (theShape.ShapeType() == TopAbs_FACE )
10613 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10616 // iterates on indicated elements and get elements by back references from their nodes
10617 TIDSortedElemSet anAffected;
10618 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10619 for ( ; elemItr != theElems.end(); ++elemItr )
10621 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10625 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10626 while ( nodeItr->more() )
10628 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10629 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10631 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10632 while ( backElemItr->more() )
10634 const SMDS_MeshElement* curElem = backElemItr->next();
10635 if ( curElem && theElems.find(curElem) == theElems.end() &&
10637 isInside( curElem, *bsc3d, aTol ) :
10638 isInside( curElem, *aFaceClassifier, aTol )))
10639 anAffected.insert( curElem );
10643 return DoubleNodes( theElems, theNodesNot, anAffected );
10647 * \brief compute an oriented angle between two planes defined by four points.
10648 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10649 * @param p0 base of the rotation axe
10650 * @param p1 extremity of the rotation axe
10651 * @param g1 belongs to the first plane
10652 * @param g2 belongs to the second plane
10654 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10656 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10657 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10658 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10659 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10660 gp_Vec vref(p0, p1);
10663 gp_Vec n1 = vref.Crossed(v1);
10664 gp_Vec n2 = vref.Crossed(v2);
10666 return n2.AngleWithRef(n1, vref);
10668 catch ( Standard_Failure ) {
10670 return Max( v1.Magnitude(), v2.Magnitude() );
10674 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10675 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10676 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10677 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10678 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10679 * 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.
10680 * 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.
10681 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10682 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10683 * \param theElems - list of groups of volumes, where a group of volume is a set of
10684 * SMDS_MeshElements sorted by Id.
10685 * \param createJointElems - if TRUE, create the elements
10686 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10687 * the boundary between \a theDomains and the rest mesh
10688 * \return TRUE if operation has been completed successfully, FALSE otherwise
10690 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10691 bool createJointElems,
10692 bool onAllBoundaries)
10694 MESSAGE("----------------------------------------------");
10695 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10696 MESSAGE("----------------------------------------------");
10698 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10699 meshDS->BuildDownWardConnectivity(true);
10701 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10703 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10704 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10705 // build the list of nodes shared by 2 or more domains, with their domain indexes
10707 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10708 std::map<int,int>celldom; // cell vtkId --> domain
10709 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10710 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10711 faceDomains.clear();
10713 cellDomains.clear();
10714 nodeDomains.clear();
10715 std::map<int,int> emptyMap;
10716 std::set<int> emptySet;
10719 MESSAGE(".. Number of domains :"<<theElems.size());
10721 TIDSortedElemSet theRestDomElems;
10722 const int iRestDom = -1;
10723 const int idom0 = onAllBoundaries ? iRestDom : 0;
10724 const int nbDomains = theElems.size();
10726 // Check if the domains do not share an element
10727 for (int idom = 0; idom < nbDomains-1; idom++)
10729 // MESSAGE("... Check of domain #" << idom);
10730 const TIDSortedElemSet& domain = theElems[idom];
10731 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10732 for (; elemItr != domain.end(); ++elemItr)
10734 const SMDS_MeshElement* anElem = *elemItr;
10735 int idombisdeb = idom + 1 ;
10736 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10738 const TIDSortedElemSet& domainbis = theElems[idombis];
10739 if ( domainbis.count(anElem) )
10741 MESSAGE(".... Domain #" << idom);
10742 MESSAGE(".... Domain #" << idombis);
10743 throw SALOME_Exception("The domains are not disjoint.");
10750 for (int idom = 0; idom < nbDomains; idom++)
10753 // --- build a map (face to duplicate --> volume to modify)
10754 // with all the faces shared by 2 domains (group of elements)
10755 // and corresponding volume of this domain, for each shared face.
10756 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10758 MESSAGE("... Neighbors of domain #" << idom);
10759 const TIDSortedElemSet& domain = theElems[idom];
10760 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10761 for (; elemItr != domain.end(); ++elemItr)
10763 const SMDS_MeshElement* anElem = *elemItr;
10766 int vtkId = anElem->getVtkId();
10767 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10768 int neighborsVtkIds[NBMAXNEIGHBORS];
10769 int downIds[NBMAXNEIGHBORS];
10770 unsigned char downTypes[NBMAXNEIGHBORS];
10771 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10772 for (int n = 0; n < nbNeighbors; n++)
10774 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10775 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10776 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10779 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10781 // MESSAGE("Domain " << idombis);
10782 const TIDSortedElemSet& domainbis = theElems[idombis];
10783 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10785 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10787 DownIdType face(downIds[n], downTypes[n]);
10788 if (!faceDomains[face].count(idom))
10790 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10791 celldom[vtkId] = idom;
10792 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10796 theRestDomElems.insert( elem );
10797 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10798 celldom[neighborsVtkIds[n]] = iRestDom;
10806 //MESSAGE("Number of shared faces " << faceDomains.size());
10807 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10809 // --- explore the shared faces domain by domain,
10810 // explore the nodes of the face and see if they belong to a cell in the domain,
10811 // which has only a node or an edge on the border (not a shared face)
10813 for (int idomain = idom0; idomain < nbDomains; idomain++)
10815 //MESSAGE("Domain " << idomain);
10816 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10817 itface = faceDomains.begin();
10818 for (; itface != faceDomains.end(); ++itface)
10820 const std::map<int, int>& domvol = itface->second;
10821 if (!domvol.count(idomain))
10823 DownIdType face = itface->first;
10824 //MESSAGE(" --- face " << face.cellId);
10825 std::set<int> oldNodes;
10827 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10828 std::set<int>::iterator itn = oldNodes.begin();
10829 for (; itn != oldNodes.end(); ++itn)
10832 //MESSAGE(" node " << oldId);
10833 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10834 for (int i=0; i<l.ncells; i++)
10836 int vtkId = l.cells[i];
10837 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10838 if (!domain.count(anElem))
10840 int vtkType = grid->GetCellType(vtkId);
10841 int downId = grid->CellIdToDownId(vtkId);
10844 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10845 continue; // not OK at this stage of the algorithm:
10846 //no cells created after BuildDownWardConnectivity
10848 DownIdType aCell(downId, vtkType);
10849 cellDomains[aCell][idomain] = vtkId;
10850 celldom[vtkId] = idomain;
10851 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10857 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10858 // for each shared face, get the nodes
10859 // for each node, for each domain of the face, create a clone of the node
10861 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10862 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10863 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10865 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10866 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10867 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10869 MESSAGE(".. Duplication of the nodes");
10870 for (int idomain = idom0; idomain < nbDomains; idomain++)
10872 itface = faceDomains.begin();
10873 for (; itface != faceDomains.end(); ++itface)
10875 const std::map<int, int>& domvol = itface->second;
10876 if (!domvol.count(idomain))
10878 DownIdType face = itface->first;
10879 //MESSAGE(" --- face " << face.cellId);
10880 std::set<int> oldNodes;
10882 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10883 std::set<int>::iterator itn = oldNodes.begin();
10884 for (; itn != oldNodes.end(); ++itn)
10887 if (nodeDomains[oldId].empty())
10889 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10890 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10892 std::map<int, int>::const_iterator itdom = domvol.begin();
10893 for (; itdom != domvol.end(); ++itdom)
10895 int idom = itdom->first;
10896 //MESSAGE(" domain " << idom);
10897 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10899 if (nodeDomains[oldId].size() >= 2) // a multiple node
10901 vector<int> orderedDoms;
10902 //MESSAGE("multiple node " << oldId);
10903 if (mutipleNodes.count(oldId))
10904 orderedDoms = mutipleNodes[oldId];
10907 map<int,int>::iterator it = nodeDomains[oldId].begin();
10908 for (; it != nodeDomains[oldId].end(); ++it)
10909 orderedDoms.push_back(it->first);
10911 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10912 //stringstream txt;
10913 //for (int i=0; i<orderedDoms.size(); i++)
10914 // txt << orderedDoms[i] << " ";
10915 //MESSAGE("orderedDoms " << txt.str());
10916 mutipleNodes[oldId] = orderedDoms;
10918 double *coords = grid->GetPoint(oldId);
10919 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10920 int newId = newNode->getVtkId();
10921 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10922 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10929 MESSAGE(".. Creation of elements");
10930 for (int idomain = idom0; idomain < nbDomains; idomain++)
10932 itface = faceDomains.begin();
10933 for (; itface != faceDomains.end(); ++itface)
10935 std::map<int, int> domvol = itface->second;
10936 if (!domvol.count(idomain))
10938 DownIdType face = itface->first;
10939 //MESSAGE(" --- face " << face.cellId);
10940 std::set<int> oldNodes;
10942 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10943 int nbMultipleNodes = 0;
10944 std::set<int>::iterator itn = oldNodes.begin();
10945 for (; itn != oldNodes.end(); ++itn)
10948 if (mutipleNodes.count(oldId))
10951 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10953 //MESSAGE("multiple Nodes detected on a shared face");
10954 int downId = itface->first.cellId;
10955 unsigned char cellType = itface->first.cellType;
10956 // --- shared edge or shared face ?
10957 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10960 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10961 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10962 if (mutipleNodes.count(nodes[i]))
10963 if (!mutipleNodesToFace.count(nodes[i]))
10964 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10966 else // shared face (between two volumes)
10968 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10969 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10970 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10971 for (int ie =0; ie < nbEdges; ie++)
10974 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10975 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10977 vector<int> vn0 = mutipleNodes[nodes[0]];
10978 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10980 for (int i0 = 0; i0 < vn0.size(); i0++)
10981 for (int i1 = 0; i1 < vn1.size(); i1++)
10982 if (vn0[i0] == vn1[i1])
10983 doms.push_back(vn0[i0]);
10984 if (doms.size() >2)
10986 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10987 double *coords = grid->GetPoint(nodes[0]);
10988 gp_Pnt p0(coords[0], coords[1], coords[2]);
10989 coords = grid->GetPoint(nodes[nbNodes - 1]);
10990 gp_Pnt p1(coords[0], coords[1], coords[2]);
10992 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10993 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10994 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10995 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10996 for (int id=0; id < doms.size(); id++)
10998 int idom = doms[id];
10999 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11000 for (int ivol=0; ivol<nbvol; ivol++)
11002 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11003 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11004 if (domain.count(elem))
11006 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11007 domvol[idom] = svol;
11008 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11010 vtkIdType npts = 0;
11011 vtkIdType* pts = 0;
11012 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11013 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11016 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11017 angleDom[idom] = 0;
11021 gp_Pnt g(values[0], values[1], values[2]);
11022 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11023 //MESSAGE(" angle=" << angleDom[idom]);
11029 map<double, int> sortedDom; // sort domains by angle
11030 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11031 sortedDom[ia->second] = ia->first;
11032 vector<int> vnodes;
11034 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11036 vdom.push_back(ib->second);
11037 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11039 for (int ino = 0; ino < nbNodes; ino++)
11040 vnodes.push_back(nodes[ino]);
11041 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11050 // --- iterate on shared faces (volumes to modify, face to extrude)
11051 // get node id's of the face (id SMDS = id VTK)
11052 // create flat element with old and new nodes if requested
11054 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11055 // (domain1 X domain2) = domain1 + MAXINT*domain2
11057 std::map<int, std::map<long,int> > nodeQuadDomains;
11058 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11060 MESSAGE(".. Creation of elements: simple junction");
11061 if (createJointElems)
11064 string joints2DName = "joints2D";
11065 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11066 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11067 string joints3DName = "joints3D";
11068 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11069 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11071 itface = faceDomains.begin();
11072 for (; itface != faceDomains.end(); ++itface)
11074 DownIdType face = itface->first;
11075 std::set<int> oldNodes;
11076 std::set<int>::iterator itn;
11078 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11080 std::map<int, int> domvol = itface->second;
11081 std::map<int, int>::iterator itdom = domvol.begin();
11082 int dom1 = itdom->first;
11083 int vtkVolId = itdom->second;
11085 int dom2 = itdom->first;
11086 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11088 stringstream grpname;
11091 grpname << dom1 << "_" << dom2;
11093 grpname << dom2 << "_" << dom1;
11094 string namegrp = grpname.str();
11095 if (!mapOfJunctionGroups.count(namegrp))
11096 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11097 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11099 sgrp->Add(vol->GetID());
11100 if (vol->GetType() == SMDSAbs_Volume)
11101 joints3DGrp->Add(vol->GetID());
11102 else if (vol->GetType() == SMDSAbs_Face)
11103 joints2DGrp->Add(vol->GetID());
11107 // --- create volumes on multiple domain intersection if requested
11108 // iterate on mutipleNodesToFace
11109 // iterate on edgesMultiDomains
11111 MESSAGE(".. Creation of elements: multiple junction");
11112 if (createJointElems)
11114 // --- iterate on mutipleNodesToFace
11116 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11117 for (; itn != mutipleNodesToFace.end(); ++itn)
11119 int node = itn->first;
11120 vector<int> orderDom = itn->second;
11121 vector<vtkIdType> orderedNodes;
11122 for (int idom = 0; idom <orderDom.size(); idom++)
11123 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11124 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11126 stringstream grpname;
11128 grpname << 0 << "_" << 0;
11130 string namegrp = grpname.str();
11131 if (!mapOfJunctionGroups.count(namegrp))
11132 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11133 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11135 sgrp->Add(face->GetID());
11138 // --- iterate on edgesMultiDomains
11140 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11141 for (; ite != edgesMultiDomains.end(); ++ite)
11143 vector<int> nodes = ite->first;
11144 vector<int> orderDom = ite->second;
11145 vector<vtkIdType> orderedNodes;
11146 if (nodes.size() == 2)
11148 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11149 for (int ino=0; ino < nodes.size(); ino++)
11150 if (orderDom.size() == 3)
11151 for (int idom = 0; idom <orderDom.size(); idom++)
11152 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11154 for (int idom = orderDom.size()-1; idom >=0; idom--)
11155 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11156 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11159 string namegrp = "jointsMultiples";
11160 if (!mapOfJunctionGroups.count(namegrp))
11161 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11162 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11164 sgrp->Add(vol->GetID());
11168 INFOS("Quadratic multiple joints not implemented");
11169 // TODO quadratic nodes
11174 // --- list the explicit faces and edges of the mesh that need to be modified,
11175 // i.e. faces and edges built with one or more duplicated nodes.
11176 // associate these faces or edges to their corresponding domain.
11177 // only the first domain found is kept when a face or edge is shared
11179 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11180 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11181 faceOrEdgeDom.clear();
11184 MESSAGE(".. Modification of elements");
11185 for (int idomain = idom0; idomain < nbDomains; idomain++)
11187 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11188 for (; itnod != nodeDomains.end(); ++itnod)
11190 int oldId = itnod->first;
11191 //MESSAGE(" node " << oldId);
11192 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11193 for (int i = 0; i < l.ncells; i++)
11195 int vtkId = l.cells[i];
11196 int vtkType = grid->GetCellType(vtkId);
11197 int downId = grid->CellIdToDownId(vtkId);
11199 continue; // new cells: not to be modified
11200 DownIdType aCell(downId, vtkType);
11201 int volParents[1000];
11202 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11203 for (int j = 0; j < nbvol; j++)
11204 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11205 if (!feDom.count(vtkId))
11207 feDom[vtkId] = idomain;
11208 faceOrEdgeDom[aCell] = emptyMap;
11209 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11210 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11211 // << " type " << vtkType << " downId " << downId);
11217 // --- iterate on shared faces (volumes to modify, face to extrude)
11218 // get node id's of the face
11219 // replace old nodes by new nodes in volumes, and update inverse connectivity
11221 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11222 for (int m=0; m<3; m++)
11224 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11225 itface = (*amap).begin();
11226 for (; itface != (*amap).end(); ++itface)
11228 DownIdType face = itface->first;
11229 std::set<int> oldNodes;
11230 std::set<int>::iterator itn;
11232 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11233 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11234 std::map<int, int> localClonedNodeIds;
11236 std::map<int, int> domvol = itface->second;
11237 std::map<int, int>::iterator itdom = domvol.begin();
11238 for (; itdom != domvol.end(); ++itdom)
11240 int idom = itdom->first;
11241 int vtkVolId = itdom->second;
11242 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11243 localClonedNodeIds.clear();
11244 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11247 if (nodeDomains[oldId].count(idom))
11249 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11250 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11253 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11258 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11259 grid->BuildLinks();
11267 * \brief Double nodes on some external faces and create flat elements.
11268 * Flat elements are mainly used by some types of mechanic calculations.
11270 * Each group of the list must be constituted of faces.
11271 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11272 * @param theElems - list of groups of faces, where a group of faces is a set of
11273 * SMDS_MeshElements sorted by Id.
11274 * @return TRUE if operation has been completed successfully, FALSE otherwise
11276 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11278 MESSAGE("-------------------------------------------------");
11279 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11280 MESSAGE("-------------------------------------------------");
11282 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11284 // --- For each group of faces
11285 // duplicate the nodes, create a flat element based on the face
11286 // replace the nodes of the faces by their clones
11288 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11289 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11290 clonedNodes.clear();
11291 intermediateNodes.clear();
11292 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11293 mapOfJunctionGroups.clear();
11295 for (int idom = 0; idom < theElems.size(); idom++)
11297 const TIDSortedElemSet& domain = theElems[idom];
11298 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11299 for (; elemItr != domain.end(); ++elemItr)
11301 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11302 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11305 // MESSAGE("aFace=" << aFace->GetID());
11306 bool isQuad = aFace->IsQuadratic();
11307 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11309 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11311 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11312 while (nodeIt->more())
11314 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11315 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11317 ln2.push_back(node);
11319 ln0.push_back(node);
11321 const SMDS_MeshNode* clone = 0;
11322 if (!clonedNodes.count(node))
11324 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11325 clonedNodes[node] = clone;
11328 clone = clonedNodes[node];
11331 ln3.push_back(clone);
11333 ln1.push_back(clone);
11335 const SMDS_MeshNode* inter = 0;
11336 if (isQuad && (!isMedium))
11338 if (!intermediateNodes.count(node))
11340 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11341 intermediateNodes[node] = inter;
11344 inter = intermediateNodes[node];
11345 ln4.push_back(inter);
11349 // --- extrude the face
11351 vector<const SMDS_MeshNode*> ln;
11352 SMDS_MeshVolume* vol = 0;
11353 vtkIdType aType = aFace->GetVtkType();
11357 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11358 // MESSAGE("vol prism " << vol->GetID());
11359 ln.push_back(ln1[0]);
11360 ln.push_back(ln1[1]);
11361 ln.push_back(ln1[2]);
11364 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11365 // MESSAGE("vol hexa " << vol->GetID());
11366 ln.push_back(ln1[0]);
11367 ln.push_back(ln1[1]);
11368 ln.push_back(ln1[2]);
11369 ln.push_back(ln1[3]);
11371 case VTK_QUADRATIC_TRIANGLE:
11372 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11373 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11374 // MESSAGE("vol quad prism " << vol->GetID());
11375 ln.push_back(ln1[0]);
11376 ln.push_back(ln1[1]);
11377 ln.push_back(ln1[2]);
11378 ln.push_back(ln3[0]);
11379 ln.push_back(ln3[1]);
11380 ln.push_back(ln3[2]);
11382 case VTK_QUADRATIC_QUAD:
11383 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11384 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11385 // ln4[0], ln4[1], ln4[2], ln4[3]);
11386 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11387 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11388 ln4[0], ln4[1], ln4[2], ln4[3]);
11389 // MESSAGE("vol quad hexa " << vol->GetID());
11390 ln.push_back(ln1[0]);
11391 ln.push_back(ln1[1]);
11392 ln.push_back(ln1[2]);
11393 ln.push_back(ln1[3]);
11394 ln.push_back(ln3[0]);
11395 ln.push_back(ln3[1]);
11396 ln.push_back(ln3[2]);
11397 ln.push_back(ln3[3]);
11407 stringstream grpname;
11411 string namegrp = grpname.str();
11412 if (!mapOfJunctionGroups.count(namegrp))
11413 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11414 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11416 sgrp->Add(vol->GetID());
11419 // --- modify the face
11421 aFace->ChangeNodes(&ln[0], ln.size());
11428 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11429 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11430 * groups of faces to remove inside the object, (idem edges).
11431 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11433 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11434 const TopoDS_Shape& theShape,
11435 SMESH_NodeSearcher* theNodeSearcher,
11436 const char* groupName,
11437 std::vector<double>& nodesCoords,
11438 std::vector<std::vector<int> >& listOfListOfNodes)
11440 MESSAGE("--------------------------------");
11441 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11442 MESSAGE("--------------------------------");
11444 // --- zone of volumes to remove is given :
11445 // 1 either by a geom shape (one or more vertices) and a radius,
11446 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11447 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11448 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11449 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11450 // defined by it's name.
11452 SMESHDS_GroupBase* groupDS = 0;
11453 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11454 while ( groupIt->more() )
11457 SMESH_Group * group = groupIt->next();
11458 if ( !group ) continue;
11459 groupDS = group->GetGroupDS();
11460 if ( !groupDS || groupDS->IsEmpty() ) continue;
11461 std::string grpName = group->GetName();
11462 //MESSAGE("grpName=" << grpName);
11463 if (grpName == groupName)
11469 bool isNodeGroup = false;
11470 bool isNodeCoords = false;
11473 if (groupDS->GetType() != SMDSAbs_Node)
11475 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11478 if (nodesCoords.size() > 0)
11479 isNodeCoords = true; // a list o nodes given by their coordinates
11480 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11482 // --- define groups to build
11484 int idg; // --- group of SMDS volumes
11485 string grpvName = groupName;
11486 grpvName += "_vol";
11487 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11490 MESSAGE("group not created " << grpvName);
11493 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11495 int idgs; // --- group of SMDS faces on the skin
11496 string grpsName = groupName;
11497 grpsName += "_skin";
11498 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11501 MESSAGE("group not created " << grpsName);
11504 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11506 int idgi; // --- group of SMDS faces internal (several shapes)
11507 string grpiName = groupName;
11508 grpiName += "_internalFaces";
11509 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11512 MESSAGE("group not created " << grpiName);
11515 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11517 int idgei; // --- group of SMDS faces internal (several shapes)
11518 string grpeiName = groupName;
11519 grpeiName += "_internalEdges";
11520 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11523 MESSAGE("group not created " << grpeiName);
11526 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11528 // --- build downward connectivity
11530 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11531 meshDS->BuildDownWardConnectivity(true);
11532 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11534 // --- set of volumes detected inside
11536 std::set<int> setOfInsideVol;
11537 std::set<int> setOfVolToCheck;
11539 std::vector<gp_Pnt> gpnts;
11542 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11544 MESSAGE("group of nodes provided");
11545 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11546 while ( elemIt->more() )
11548 const SMDS_MeshElement* elem = elemIt->next();
11551 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11554 SMDS_MeshElement* vol = 0;
11555 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11556 while (volItr->more())
11558 vol = (SMDS_MeshElement*)volItr->next();
11559 setOfInsideVol.insert(vol->getVtkId());
11560 sgrp->Add(vol->GetID());
11564 else if (isNodeCoords)
11566 MESSAGE("list of nodes coordinates provided");
11569 while (i < nodesCoords.size()-2)
11571 double x = nodesCoords[i++];
11572 double y = nodesCoords[i++];
11573 double z = nodesCoords[i++];
11574 gp_Pnt p = gp_Pnt(x, y ,z);
11575 gpnts.push_back(p);
11576 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11580 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11582 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11583 TopTools_IndexedMapOfShape vertexMap;
11584 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11585 gp_Pnt p = gp_Pnt(0,0,0);
11586 if (vertexMap.Extent() < 1)
11589 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11591 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11592 p = BRep_Tool::Pnt(vertex);
11593 gpnts.push_back(p);
11594 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11598 if (gpnts.size() > 0)
11601 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11603 nodeId = startNode->GetID();
11604 MESSAGE("nodeId " << nodeId);
11606 double radius2 = radius*radius;
11607 MESSAGE("radius2 " << radius2);
11609 // --- volumes on start node
11611 setOfVolToCheck.clear();
11612 SMDS_MeshElement* startVol = 0;
11613 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11614 while (volItr->more())
11616 startVol = (SMDS_MeshElement*)volItr->next();
11617 setOfVolToCheck.insert(startVol->getVtkId());
11619 if (setOfVolToCheck.empty())
11621 MESSAGE("No volumes found");
11625 // --- starting with central volumes then their neighbors, check if they are inside
11626 // or outside the domain, until no more new neighbor volume is inside.
11627 // Fill the group of inside volumes
11629 std::map<int, double> mapOfNodeDistance2;
11630 mapOfNodeDistance2.clear();
11631 std::set<int> setOfOutsideVol;
11632 while (!setOfVolToCheck.empty())
11634 std::set<int>::iterator it = setOfVolToCheck.begin();
11636 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11637 bool volInside = false;
11638 vtkIdType npts = 0;
11639 vtkIdType* pts = 0;
11640 grid->GetCellPoints(vtkId, npts, pts);
11641 for (int i=0; i<npts; i++)
11643 double distance2 = 0;
11644 if (mapOfNodeDistance2.count(pts[i]))
11646 distance2 = mapOfNodeDistance2[pts[i]];
11647 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11651 double *coords = grid->GetPoint(pts[i]);
11652 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11654 for (int j=0; j<gpnts.size(); j++)
11656 double d2 = aPoint.SquareDistance(gpnts[j]);
11657 if (d2 < distance2)
11660 if (distance2 < radius2)
11664 mapOfNodeDistance2[pts[i]] = distance2;
11665 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11667 if (distance2 < radius2)
11669 volInside = true; // one or more nodes inside the domain
11670 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11676 setOfInsideVol.insert(vtkId);
11677 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11678 int neighborsVtkIds[NBMAXNEIGHBORS];
11679 int downIds[NBMAXNEIGHBORS];
11680 unsigned char downTypes[NBMAXNEIGHBORS];
11681 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11682 for (int n = 0; n < nbNeighbors; n++)
11683 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11684 setOfVolToCheck.insert(neighborsVtkIds[n]);
11688 setOfOutsideVol.insert(vtkId);
11689 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11691 setOfVolToCheck.erase(vtkId);
11695 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11696 // If yes, add the volume to the inside set
11698 bool addedInside = true;
11699 std::set<int> setOfVolToReCheck;
11700 while (addedInside)
11702 MESSAGE(" --------------------------- re check");
11703 addedInside = false;
11704 std::set<int>::iterator itv = setOfInsideVol.begin();
11705 for (; itv != setOfInsideVol.end(); ++itv)
11708 int neighborsVtkIds[NBMAXNEIGHBORS];
11709 int downIds[NBMAXNEIGHBORS];
11710 unsigned char downTypes[NBMAXNEIGHBORS];
11711 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11712 for (int n = 0; n < nbNeighbors; n++)
11713 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11714 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11716 setOfVolToCheck = setOfVolToReCheck;
11717 setOfVolToReCheck.clear();
11718 while (!setOfVolToCheck.empty())
11720 std::set<int>::iterator it = setOfVolToCheck.begin();
11722 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11724 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11725 int countInside = 0;
11726 int neighborsVtkIds[NBMAXNEIGHBORS];
11727 int downIds[NBMAXNEIGHBORS];
11728 unsigned char downTypes[NBMAXNEIGHBORS];
11729 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11730 for (int n = 0; n < nbNeighbors; n++)
11731 if (setOfInsideVol.count(neighborsVtkIds[n]))
11733 MESSAGE("countInside " << countInside);
11734 if (countInside > 1)
11736 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11737 setOfInsideVol.insert(vtkId);
11738 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11739 addedInside = true;
11742 setOfVolToReCheck.insert(vtkId);
11744 setOfVolToCheck.erase(vtkId);
11748 // --- map of Downward faces at the boundary, inside the global volume
11749 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11750 // fill group of SMDS faces inside the volume (when several volume shapes)
11751 // fill group of SMDS faces on the skin of the global volume (if skin)
11753 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11754 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11755 std::set<int>::iterator it = setOfInsideVol.begin();
11756 for (; it != setOfInsideVol.end(); ++it)
11759 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11760 int neighborsVtkIds[NBMAXNEIGHBORS];
11761 int downIds[NBMAXNEIGHBORS];
11762 unsigned char downTypes[NBMAXNEIGHBORS];
11763 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11764 for (int n = 0; n < nbNeighbors; n++)
11766 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11767 if (neighborDim == 3)
11769 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11771 DownIdType face(downIds[n], downTypes[n]);
11772 boundaryFaces[face] = vtkId;
11774 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11775 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11776 if (vtkFaceId >= 0)
11778 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11779 // find also the smds edges on this face
11780 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11781 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11782 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11783 for (int i = 0; i < nbEdges; i++)
11785 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11786 if (vtkEdgeId >= 0)
11787 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11791 else if (neighborDim == 2) // skin of the volume
11793 DownIdType face(downIds[n], downTypes[n]);
11794 skinFaces[face] = vtkId;
11795 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11796 if (vtkFaceId >= 0)
11797 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11802 // --- identify the edges constituting the wire of each subshape on the skin
11803 // define polylines with the nodes of edges, equivalent to wires
11804 // project polylines on subshapes, and partition, to get geom faces
11806 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11807 std::set<int> emptySet;
11809 std::set<int> shapeIds;
11811 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11812 while (itelem->more())
11814 const SMDS_MeshElement *elem = itelem->next();
11815 int shapeId = elem->getshapeId();
11816 int vtkId = elem->getVtkId();
11817 if (!shapeIdToVtkIdSet.count(shapeId))
11819 shapeIdToVtkIdSet[shapeId] = emptySet;
11820 shapeIds.insert(shapeId);
11822 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11825 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11826 std::set<DownIdType, DownIdCompare> emptyEdges;
11827 emptyEdges.clear();
11829 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11830 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11832 int shapeId = itShape->first;
11833 MESSAGE(" --- Shape ID --- "<< shapeId);
11834 shapeIdToEdges[shapeId] = emptyEdges;
11836 std::vector<int> nodesEdges;
11838 std::set<int>::iterator its = itShape->second.begin();
11839 for (; its != itShape->second.end(); ++its)
11842 MESSAGE(" " << vtkId);
11843 int neighborsVtkIds[NBMAXNEIGHBORS];
11844 int downIds[NBMAXNEIGHBORS];
11845 unsigned char downTypes[NBMAXNEIGHBORS];
11846 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11847 for (int n = 0; n < nbNeighbors; n++)
11849 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11851 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11852 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11853 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11855 DownIdType edge(downIds[n], downTypes[n]);
11856 if (!shapeIdToEdges[shapeId].count(edge))
11858 shapeIdToEdges[shapeId].insert(edge);
11860 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11861 nodesEdges.push_back(vtkNodeId[0]);
11862 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11863 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11869 std::list<int> order;
11871 if (nodesEdges.size() > 0)
11873 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11874 nodesEdges[0] = -1;
11875 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11876 nodesEdges[1] = -1; // do not reuse this edge
11880 int nodeTofind = order.back(); // try first to push back
11882 for (i = 0; i<nodesEdges.size(); i++)
11883 if (nodesEdges[i] == nodeTofind)
11885 if (i == nodesEdges.size())
11886 found = false; // no follower found on back
11889 if (i%2) // odd ==> use the previous one
11890 if (nodesEdges[i-1] < 0)
11894 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11895 nodesEdges[i-1] = -1;
11897 else // even ==> use the next one
11898 if (nodesEdges[i+1] < 0)
11902 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11903 nodesEdges[i+1] = -1;
11908 // try to push front
11910 nodeTofind = order.front(); // try to push front
11911 for (i = 0; i<nodesEdges.size(); i++)
11912 if (nodesEdges[i] == nodeTofind)
11914 if (i == nodesEdges.size())
11916 found = false; // no predecessor found on front
11919 if (i%2) // odd ==> use the previous one
11920 if (nodesEdges[i-1] < 0)
11924 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11925 nodesEdges[i-1] = -1;
11927 else // even ==> use the next one
11928 if (nodesEdges[i+1] < 0)
11932 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11933 nodesEdges[i+1] = -1;
11939 std::vector<int> nodes;
11940 nodes.push_back(shapeId);
11941 std::list<int>::iterator itl = order.begin();
11942 for (; itl != order.end(); itl++)
11944 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11945 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11947 listOfListOfNodes.push_back(nodes);
11950 // partition geom faces with blocFissure
11951 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11952 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11958 //================================================================================
11960 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11961 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11962 * \return TRUE if operation has been completed successfully, FALSE otherwise
11964 //================================================================================
11966 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11968 // iterates on volume elements and detect all free faces on them
11969 SMESHDS_Mesh* aMesh = GetMeshDS();
11972 //bool res = false;
11973 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11974 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11977 const SMDS_MeshVolume* volume = vIt->next();
11978 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11979 vTool.SetExternalNormal();
11980 //const bool isPoly = volume->IsPoly();
11981 const int iQuad = volume->IsQuadratic();
11982 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11984 if (!vTool.IsFreeFace(iface))
11987 vector<const SMDS_MeshNode *> nodes;
11988 int nbFaceNodes = vTool.NbFaceNodes(iface);
11989 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11991 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11992 nodes.push_back(faceNodes[inode]);
11993 if (iQuad) { // add medium nodes
11994 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11995 nodes.push_back(faceNodes[inode]);
11996 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11997 nodes.push_back(faceNodes[8]);
11999 // add new face based on volume nodes
12000 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12002 continue; // face already exsist
12004 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12008 return ( nbFree==(nbExisted+nbCreated) );
12013 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12015 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12017 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12020 //================================================================================
12022 * \brief Creates missing boundary elements
12023 * \param elements - elements whose boundary is to be checked
12024 * \param dimension - defines type of boundary elements to create
12025 * \param group - a group to store created boundary elements in
12026 * \param targetMesh - a mesh to store created boundary elements in
12027 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12028 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12029 * boundary elements will be copied into the targetMesh
12030 * \param toAddExistingBondary - if true, not only new but also pre-existing
12031 * boundary elements will be added into the new group
12032 * \param aroundElements - if true, elements will be created on boundary of given
12033 * elements else, on boundary of the whole mesh.
12034 * \return nb of added boundary elements
12036 //================================================================================
12038 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12039 Bnd_Dimension dimension,
12040 SMESH_Group* group/*=0*/,
12041 SMESH_Mesh* targetMesh/*=0*/,
12042 bool toCopyElements/*=false*/,
12043 bool toCopyExistingBoundary/*=false*/,
12044 bool toAddExistingBondary/*= false*/,
12045 bool aroundElements/*= false*/)
12047 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12048 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12049 // hope that all elements are of the same type, do not check them all
12050 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12051 throw SALOME_Exception(LOCALIZED("wrong element type"));
12054 toCopyElements = toCopyExistingBoundary = false;
12056 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12057 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12058 int nbAddedBnd = 0;
12060 // editor adding present bnd elements and optionally holding elements to add to the group
12061 SMESH_MeshEditor* presentEditor;
12062 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12063 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12065 SMESH_MesherHelper helper( *myMesh );
12066 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12067 SMDS_VolumeTool vTool;
12068 TIDSortedElemSet avoidSet;
12069 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12072 typedef vector<const SMDS_MeshNode*> TConnectivity;
12074 SMDS_ElemIteratorPtr eIt;
12075 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12076 else eIt = elemSetIterator( elements );
12078 while (eIt->more())
12080 const SMDS_MeshElement* elem = eIt->next();
12081 const int iQuad = elem->IsQuadratic();
12083 // ------------------------------------------------------------------------------------
12084 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12085 // ------------------------------------------------------------------------------------
12086 vector<const SMDS_MeshElement*> presentBndElems;
12087 vector<TConnectivity> missingBndElems;
12088 TConnectivity nodes, elemNodes;
12089 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12091 vTool.SetExternalNormal();
12092 const SMDS_MeshElement* otherVol = 0;
12093 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12095 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12096 ( !aroundElements || elements.count( otherVol )))
12098 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12099 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12100 if ( missType == SMDSAbs_Edge ) // boundary edges
12102 nodes.resize( 2+iQuad );
12103 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12105 for ( int j = 0; j < nodes.size(); ++j )
12107 if ( const SMDS_MeshElement* edge =
12108 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12109 presentBndElems.push_back( edge );
12111 missingBndElems.push_back( nodes );
12114 else // boundary face
12117 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12118 nodes.push_back( nn[inode] ); // add corner nodes
12120 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12121 nodes.push_back( nn[inode] ); // add medium nodes
12122 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12124 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12126 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12127 SMDSAbs_Face, /*noMedium=*/false ))
12128 presentBndElems.push_back( f );
12130 missingBndElems.push_back( nodes );
12132 if ( targetMesh != myMesh )
12134 // add 1D elements on face boundary to be added to a new mesh
12135 const SMDS_MeshElement* edge;
12136 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12139 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12141 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12142 if ( edge && avoidSet.insert( edge ).second )
12143 presentBndElems.push_back( edge );
12149 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12151 avoidSet.clear(), avoidSet.insert( elem );
12152 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12153 SMDS_MeshElement::iterator() );
12154 elemNodes.push_back( elemNodes[0] );
12155 nodes.resize( 2 + iQuad );
12156 const int nbLinks = elem->NbCornerNodes();
12157 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12159 nodes[0] = elemNodes[iN];
12160 nodes[1] = elemNodes[iN+1+iQuad];
12161 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12162 continue; // not free link
12164 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12165 if ( const SMDS_MeshElement* edge =
12166 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12167 presentBndElems.push_back( edge );
12169 missingBndElems.push_back( nodes );
12173 // ---------------------------------
12174 // 2. Add missing boundary elements
12175 // ---------------------------------
12176 if ( targetMesh != myMesh )
12177 // instead of making a map of nodes in this mesh and targetMesh,
12178 // we create nodes with same IDs.
12179 for ( int i = 0; i < missingBndElems.size(); ++i )
12181 TConnectivity& srcNodes = missingBndElems[i];
12182 TConnectivity nodes( srcNodes.size() );
12183 for ( inode = 0; inode < nodes.size(); ++inode )
12184 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12185 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12187 /*noMedium=*/false))
12189 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12193 for ( int i = 0; i < missingBndElems.size(); ++i )
12195 TConnectivity& nodes = missingBndElems[i];
12196 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12198 /*noMedium=*/false))
12200 SMDS_MeshElement* elem =
12201 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12204 // try to set a new element to a shape
12205 if ( myMesh->HasShapeToMesh() )
12208 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12209 const int nbN = nodes.size() / (iQuad+1 );
12210 for ( inode = 0; inode < nbN && ok; ++inode )
12212 pair<int, TopAbs_ShapeEnum> i_stype =
12213 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12214 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12215 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12217 if ( ok && mediumShapes.size() > 1 )
12219 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12220 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12221 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12223 if (( ok = ( stype_i->first != stype_i_0.first )))
12224 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12225 aMesh->IndexToShape( stype_i_0.second ));
12228 if ( ok && mediumShapes.begin()->first == missShapeType )
12229 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12233 // ----------------------------------
12234 // 3. Copy present boundary elements
12235 // ----------------------------------
12236 if ( toCopyExistingBoundary )
12237 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12239 const SMDS_MeshElement* e = presentBndElems[i];
12240 TConnectivity nodes( e->NbNodes() );
12241 for ( inode = 0; inode < nodes.size(); ++inode )
12242 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12243 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12245 else // store present elements to add them to a group
12246 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12248 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12251 } // loop on given elements
12253 // ---------------------------------------------
12254 // 4. Fill group with boundary elements
12255 // ---------------------------------------------
12258 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12259 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12260 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12262 tgtEditor.myLastCreatedElems.Clear();
12263 tgtEditor2.myLastCreatedElems.Clear();
12265 // -----------------------
12266 // 5. Copy given elements
12267 // -----------------------
12268 if ( toCopyElements && targetMesh != myMesh )
12270 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12271 else eIt = elemSetIterator( elements );
12272 while (eIt->more())
12274 const SMDS_MeshElement* elem = eIt->next();
12275 TConnectivity nodes( elem->NbNodes() );
12276 for ( inode = 0; inode < nodes.size(); ++inode )
12277 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12278 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12280 tgtEditor.myLastCreatedElems.Clear();