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 //=======================================================================
1247 //function : getBadRate
1249 //=======================================================================
1251 static double getBadRate (const SMDS_MeshElement* theElem,
1252 SMESH::Controls::NumericalFunctorPtr& theCrit)
1254 SMESH::Controls::TSequenceOfXYZ P;
1255 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1257 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1258 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1261 //=======================================================================
1262 //function : QuadToTri
1263 //purpose : Cut quadrangles into triangles.
1264 // theCrit is used to select a diagonal to cut
1265 //=======================================================================
1267 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1268 SMESH::Controls::NumericalFunctorPtr theCrit)
1270 myLastCreatedElems.Clear();
1271 myLastCreatedNodes.Clear();
1273 if ( !theCrit.get() )
1276 SMESHDS_Mesh * aMesh = GetMeshDS();
1278 Handle(Geom_Surface) surface;
1279 SMESH_MesherHelper helper( *GetMesh() );
1281 TIDSortedElemSet::iterator itElem;
1282 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1284 const SMDS_MeshElement* elem = *itElem;
1285 if ( !elem || elem->GetType() != SMDSAbs_Face )
1287 if ( elem->NbCornerNodes() != 4 )
1290 // retrieve element nodes
1291 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1293 // compare two sets of possible triangles
1294 double aBadRate1, aBadRate2; // to what extent a set is bad
1295 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1296 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1297 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1299 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1300 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1301 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1303 const int aShapeId = FindShape( elem );
1304 const SMDS_MeshElement* newElem1 = 0;
1305 const SMDS_MeshElement* newElem2 = 0;
1307 if ( !elem->IsQuadratic() ) // split liner quadrangle
1309 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1310 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1311 if ( aBadRate1 <= aBadRate2 ) {
1312 // tr1 + tr2 is better
1313 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1314 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1317 // tr3 + tr4 is better
1318 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1319 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1322 else // split quadratic quadrangle
1324 helper.SetIsQuadratic( true );
1325 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1327 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1328 if ( aNodes.size() == 9 )
1330 helper.SetIsBiQuadratic( true );
1331 if ( aBadRate1 <= aBadRate2 )
1332 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1334 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1336 // create a new element
1337 if ( aBadRate1 <= aBadRate2 ) {
1338 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1339 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1342 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1343 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1347 // care of a new element
1349 myLastCreatedElems.Append(newElem1);
1350 myLastCreatedElems.Append(newElem2);
1351 AddToSameGroups( newElem1, elem, aMesh );
1352 AddToSameGroups( newElem2, elem, aMesh );
1354 // put a new triangle on the same shape
1356 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1357 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1359 aMesh->RemoveElement( elem );
1364 //=======================================================================
1366 * \brief Split each of given quadrangles into 4 triangles.
1367 * \param theElems - The faces to be splitted. If empty all faces are split.
1369 //=======================================================================
1371 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1373 myLastCreatedElems.Clear();
1374 myLastCreatedNodes.Clear();
1376 SMESH_MesherHelper helper( *GetMesh() );
1377 helper.SetElementsOnShape( true );
1379 SMDS_ElemIteratorPtr faceIt;
1380 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1381 else faceIt = elemSetIterator( theElems );
1384 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1386 vector< const SMDS_MeshNode* > nodes;
1387 SMESHDS_SubMesh* subMeshDS;
1389 Handle(Geom_Surface) surface;
1390 TopLoc_Location loc;
1392 while ( faceIt->more() )
1394 const SMDS_MeshElement* quad = faceIt->next();
1395 if ( !quad || quad->NbCornerNodes() != 4 )
1398 // get a surface the quad is on
1400 if ( quad->getshapeId() < 1 )
1403 helper.SetSubShape( 0 );
1406 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1408 helper.SetSubShape( quad->getshapeId() );
1409 if ( !helper.GetSubShape().IsNull() &&
1410 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1412 F = TopoDS::Face( helper.GetSubShape() );
1413 surface = BRep_Tool::Surface( F, loc );
1414 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1418 helper.SetSubShape( 0 );
1423 // create a central node
1425 const SMDS_MeshNode* nCentral;
1426 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1428 if ( nodes.size() == 9 )
1430 nCentral = nodes.back();
1437 for ( ; iN < nodes.size(); ++iN )
1438 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1440 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1441 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1443 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1444 xyz[0], xyz[1], xyz[2], xyz[3],
1445 xyz[4], xyz[5], xyz[6], xyz[7] );
1449 for ( ; iN < nodes.size(); ++iN )
1450 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1452 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1453 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1455 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1456 uv[0], uv[1], uv[2], uv[3],
1457 uv[4], uv[5], uv[6], uv[7] );
1459 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1463 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1464 uv[8].X(), uv[8].Y() );
1465 myLastCreatedNodes.Append( nCentral );
1468 // create 4 triangles
1470 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1472 helper.SetIsQuadratic ( nodes.size() > 4 );
1473 helper.SetIsBiQuadratic( nodes.size() == 9 );
1474 if ( helper.GetIsQuadratic() )
1475 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1477 for ( int i = 0; i < 4; ++i )
1479 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1482 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1483 myLastCreatedElems.Append( tria );
1488 //=======================================================================
1489 //function : BestSplit
1490 //purpose : Find better diagonal for cutting.
1491 //=======================================================================
1493 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1494 SMESH::Controls::NumericalFunctorPtr theCrit)
1496 myLastCreatedElems.Clear();
1497 myLastCreatedNodes.Clear();
1502 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1505 if( theQuad->NbNodes()==4 ||
1506 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1508 // retrieve element nodes
1509 const SMDS_MeshNode* aNodes [4];
1510 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1512 //while (itN->more())
1514 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1516 // compare two sets of possible triangles
1517 double aBadRate1, aBadRate2; // to what extent a set is bad
1518 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1519 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1520 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1522 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1523 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1524 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1525 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1526 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1527 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1528 return 1; // diagonal 1-3
1530 return 2; // diagonal 2-4
1537 // Methods of splitting volumes into tetra
1539 const int theHexTo5_1[5*4+1] =
1541 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1543 const int theHexTo5_2[5*4+1] =
1545 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1547 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1549 const int theHexTo6_1[6*4+1] =
1551 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
1553 const int theHexTo6_2[6*4+1] =
1555 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
1557 const int theHexTo6_3[6*4+1] =
1559 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
1561 const int theHexTo6_4[6*4+1] =
1563 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
1565 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1567 const int thePyraTo2_1[2*4+1] =
1569 0, 1, 2, 4, 0, 2, 3, 4, -1
1571 const int thePyraTo2_2[2*4+1] =
1573 1, 2, 3, 4, 1, 3, 0, 4, -1
1575 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1577 const int thePentaTo3_1[3*4+1] =
1579 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1581 const int thePentaTo3_2[3*4+1] =
1583 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1585 const int thePentaTo3_3[3*4+1] =
1587 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1589 const int thePentaTo3_4[3*4+1] =
1591 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1593 const int thePentaTo3_5[3*4+1] =
1595 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1597 const int thePentaTo3_6[3*4+1] =
1599 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1601 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1602 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1604 // Methods of splitting hexahedron into prisms
1606 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1608 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
1610 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1612 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
1614 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1616 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
1619 const int theHexTo2Prisms_BT_1[6*2+1] =
1621 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1623 const int theHexTo2Prisms_BT_2[6*2+1] =
1625 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1627 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1629 const int theHexTo2Prisms_LR_1[6*2+1] =
1631 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1633 const int theHexTo2Prisms_LR_2[6*2+1] =
1635 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1637 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1639 const int theHexTo2Prisms_FB_1[6*2+1] =
1641 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1643 const int theHexTo2Prisms_FB_2[6*2+1] =
1645 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1647 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1650 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1653 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1654 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1655 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1656 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1662 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1663 bool _baryNode; //!< additional node is to be created at cell barycenter
1664 bool _ownConn; //!< to delete _connectivity in destructor
1665 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1667 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1668 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1669 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1670 bool hasFacet( const TTriangleFacet& facet ) const
1672 if ( _nbCorners == 4 )
1674 const int* tetConn = _connectivity;
1675 for ( ; tetConn[0] >= 0; tetConn += 4 )
1676 if (( facet.contains( tetConn[0] ) +
1677 facet.contains( tetConn[1] ) +
1678 facet.contains( tetConn[2] ) +
1679 facet.contains( tetConn[3] )) == 3 )
1682 else // prism, _nbCorners == 6
1684 const int* prismConn = _connectivity;
1685 for ( ; prismConn[0] >= 0; prismConn += 6 )
1687 if (( facet.contains( prismConn[0] ) &&
1688 facet.contains( prismConn[1] ) &&
1689 facet.contains( prismConn[2] ))
1691 ( facet.contains( prismConn[3] ) &&
1692 facet.contains( prismConn[4] ) &&
1693 facet.contains( prismConn[5] )))
1701 //=======================================================================
1703 * \brief return TSplitMethod for the given element to split into tetrahedra
1705 //=======================================================================
1707 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1709 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1711 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1712 // an edge and a face barycenter; tertaherdons are based on triangles and
1713 // a volume barycenter
1714 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1716 // Find out how adjacent volumes are split
1718 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1719 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1720 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1722 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1723 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1724 if ( nbNodes < 4 ) continue;
1726 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1727 const int* nInd = vol.GetFaceNodesIndices( iF );
1730 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1731 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1732 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1733 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1737 int iCom = 0; // common node of triangle faces to split into
1738 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1740 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1741 nInd[ iQ * ( (iCom+1)%nbNodes )],
1742 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1743 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1744 nInd[ iQ * ( (iCom+2)%nbNodes )],
1745 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1746 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1748 triaSplits.push_back( t012 );
1749 triaSplits.push_back( t023 );
1754 if ( !triaSplits.empty() )
1755 hasAdjacentSplits = true;
1758 // Among variants of split method select one compliant with adjacent volumes
1760 TSplitMethod method;
1761 if ( !vol.Element()->IsPoly() && !is24TetMode )
1763 int nbVariants = 2, nbTet = 0;
1764 const int** connVariants = 0;
1765 switch ( vol.Element()->GetEntityType() )
1767 case SMDSEntity_Hexa:
1768 case SMDSEntity_Quad_Hexa:
1769 case SMDSEntity_TriQuad_Hexa:
1770 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1771 connVariants = theHexTo5, nbTet = 5;
1773 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1775 case SMDSEntity_Pyramid:
1776 case SMDSEntity_Quad_Pyramid:
1777 connVariants = thePyraTo2; nbTet = 2;
1779 case SMDSEntity_Penta:
1780 case SMDSEntity_Quad_Penta:
1781 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1786 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1788 // check method compliancy with adjacent tetras,
1789 // all found splits must be among facets of tetras described by this method
1790 method = TSplitMethod( nbTet, connVariants[variant] );
1791 if ( hasAdjacentSplits && method._nbSplits > 0 )
1793 bool facetCreated = true;
1794 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1796 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1797 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1798 facetCreated = method.hasFacet( *facet );
1800 if ( !facetCreated )
1801 method = TSplitMethod(0); // incompatible method
1805 if ( method._nbSplits < 1 )
1807 // No standard method is applicable, use a generic solution:
1808 // each facet of a volume is split into triangles and
1809 // each of triangles and a volume barycenter form a tetrahedron.
1811 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1813 int* connectivity = new int[ maxTetConnSize + 1 ];
1814 method._connectivity = connectivity;
1815 method._ownConn = true;
1816 method._baryNode = !isHex27; // to create central node or not
1819 int baryCenInd = vol.NbNodes() - int( isHex27 );
1820 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1822 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1823 const int* nInd = vol.GetFaceNodesIndices( iF );
1824 // find common node of triangle facets of tetra to create
1825 int iCommon = 0; // index in linear numeration
1826 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1827 if ( !triaSplits.empty() )
1830 const TTriangleFacet* facet = &triaSplits.front();
1831 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1832 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1833 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1836 else if ( nbNodes > 3 && !is24TetMode )
1838 // find the best method of splitting into triangles by aspect ratio
1839 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1840 map< double, int > badness2iCommon;
1841 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1842 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1843 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1846 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1848 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1849 nodes[ iQ*((iLast-1)%nbNodes)],
1850 nodes[ iQ*((iLast )%nbNodes)]);
1851 badness += getBadRate( &tria, aspectRatio );
1853 badness2iCommon.insert( make_pair( badness, iCommon ));
1855 // use iCommon with lowest badness
1856 iCommon = badness2iCommon.begin()->second;
1858 if ( iCommon >= nbNodes )
1859 iCommon = 0; // something wrong
1861 // fill connectivity of tetrahedra based on a current face
1862 int nbTet = nbNodes - 2;
1863 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1868 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1869 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1873 method._faceBaryNode[ iF ] = 0;
1874 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1877 for ( int i = 0; i < nbTet; ++i )
1879 int i1 = i, i2 = (i+1) % nbNodes;
1880 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1881 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1882 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1883 connectivity[ connSize++ ] = faceBaryCenInd;
1884 connectivity[ connSize++ ] = baryCenInd;
1889 for ( int i = 0; i < nbTet; ++i )
1891 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1892 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1893 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1894 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1895 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1896 connectivity[ connSize++ ] = baryCenInd;
1899 method._nbSplits += nbTet;
1901 } // loop on volume faces
1903 connectivity[ connSize++ ] = -1;
1905 } // end of generic solution
1909 //=======================================================================
1911 * \brief return TSplitMethod to split haxhedron into prisms
1913 //=======================================================================
1915 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1916 const int methodFlags,
1917 const int facetToSplit)
1919 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1921 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1923 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1925 static TSplitMethod to4methods[4]; // order BT, LR, FB
1926 if ( to4methods[iF]._nbSplits == 0 )
1930 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1931 to4methods[iF]._faceBaryNode[ 0 ] = 0;
1932 to4methods[iF]._faceBaryNode[ 1 ] = 0;
1935 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1936 to4methods[iF]._faceBaryNode[ 2 ] = 0;
1937 to4methods[iF]._faceBaryNode[ 4 ] = 0;
1940 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1941 to4methods[iF]._faceBaryNode[ 3 ] = 0;
1942 to4methods[iF]._faceBaryNode[ 5 ] = 0;
1944 default: return to4methods[3];
1946 to4methods[iF]._nbSplits = 4;
1947 to4methods[iF]._nbCorners = 6;
1949 return to4methods[iF];
1951 // else if ( methodFlags == HEXA_TO_2_PRISMS )
1953 TSplitMethod method;
1955 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1957 const int nbVariants = 2, nbSplits = 2;
1958 const int** connVariants = 0;
1960 case 0: connVariants = theHexTo2Prisms_BT; break;
1961 case 1: connVariants = theHexTo2Prisms_LR; break;
1962 case 2: connVariants = theHexTo2Prisms_FB; break;
1963 default: return method;
1966 // look for prisms adjacent via facetToSplit and an opposite one
1967 for ( int is2nd = 0; is2nd < 2; ++is2nd )
1969 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1970 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1971 if ( nbNodes != 4 ) return method;
1973 const int* nInd = vol.GetFaceNodesIndices( iFacet );
1974 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1975 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1977 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1979 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1984 // there are adjacent prism
1985 for ( int variant = 0; variant < nbVariants; ++variant )
1987 // check method compliancy with adjacent prisms,
1988 // the found prism facets must be among facets of prisms described by current method
1989 method._nbSplits = nbSplits;
1990 method._nbCorners = 6;
1991 method._connectivity = connVariants[ variant ];
1992 if ( method.hasFacet( *t ))
1997 // No adjacent prisms. Select a variant with a best aspect ratio.
1999 double badness[2] = { 0, 0 };
2000 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2001 const SMDS_MeshNode** nodes = vol.GetNodes();
2002 for ( int variant = 0; variant < nbVariants; ++variant )
2003 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2005 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2006 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2008 method._connectivity = connVariants[ variant ];
2009 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2010 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2011 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2013 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2016 badness[ variant ] += getBadRate( &tria, aspectRatio );
2018 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2020 method._nbSplits = nbSplits;
2021 method._nbCorners = 6;
2022 method._connectivity = connVariants[ iBetter ];
2027 //================================================================================
2029 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2031 //================================================================================
2033 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2034 const SMDSAbs_GeometryType geom ) const
2036 // find the tetrahedron including the three nodes of facet
2037 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2038 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2039 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2040 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2041 while ( volIt1->more() )
2043 const SMDS_MeshElement* v = volIt1->next();
2044 if ( v->GetGeomType() != geom )
2046 const int lastCornerInd = v->NbCornerNodes() - 1;
2047 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2048 continue; // medium node not allowed
2049 const int ind2 = v->GetNodeIndex( n2 );
2050 if ( ind2 < 0 || lastCornerInd < ind2 )
2052 const int ind3 = v->GetNodeIndex( n3 );
2053 if ( ind3 < 0 || lastCornerInd < ind3 )
2060 //=======================================================================
2062 * \brief A key of a face of volume
2064 //=======================================================================
2066 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2068 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2070 TIDSortedNodeSet sortedNodes;
2071 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2072 int nbNodes = vol.NbFaceNodes( iF );
2073 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2074 for ( int i = 0; i < nbNodes; i += iQ )
2075 sortedNodes.insert( fNodes[i] );
2076 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2077 first.first = (*(n++))->GetID();
2078 first.second = (*(n++))->GetID();
2079 second.first = (*(n++))->GetID();
2080 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2085 //=======================================================================
2086 //function : SplitVolumes
2087 //purpose : Split volume elements into tetrahedra or prisms.
2088 // If facet ID < 0, element is split into tetrahedra,
2089 // else a hexahedron is split into prisms so that the given facet is
2090 // split into triangles
2091 //=======================================================================
2093 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2094 const int theMethodFlags)
2096 // std-like iterator on coordinates of nodes of mesh element
2097 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2098 NXyzIterator xyzEnd;
2100 SMDS_VolumeTool volTool;
2101 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2102 fHelper.ToFixNodeParameters( true );
2104 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2105 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2107 SMESH_SequenceOfElemPtr newNodes, newElems;
2109 // map face of volume to it's baricenrtic node
2110 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2113 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2114 for ( ; elem2facet != theElems.end(); ++elem2facet )
2116 const SMDS_MeshElement* elem = elem2facet->first;
2117 const int facetToSplit = elem2facet->second;
2118 if ( elem->GetType() != SMDSAbs_Volume )
2120 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2121 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2124 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2126 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2127 getTetraSplitMethod( volTool, theMethodFlags ) :
2128 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2129 if ( splitMethod._nbSplits < 1 ) continue;
2131 // find submesh to add new tetras to
2132 if ( !subMesh || !subMesh->Contains( elem ))
2134 int shapeID = FindShape( elem );
2135 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2136 subMesh = GetMeshDS()->MeshElements( shapeID );
2139 if ( elem->IsQuadratic() )
2142 // add quadratic links to the helper
2143 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2145 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2146 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2147 for ( int iN = 0; iN < nbN; iN += iQ )
2148 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2150 helper.SetIsQuadratic( true );
2155 helper.SetIsQuadratic( false );
2157 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2158 volTool.GetNodes() + elem->NbNodes() );
2159 helper.SetElementsOnShape( true );
2160 if ( splitMethod._baryNode )
2162 // make a node at barycenter
2163 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2164 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2165 nodes.push_back( gcNode );
2166 newNodes.Append( gcNode );
2168 if ( !splitMethod._faceBaryNode.empty() )
2170 // make or find baricentric nodes of faces
2171 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2172 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2174 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2175 volFace2BaryNode.insert
2176 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2179 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2180 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2182 nodes.push_back( iF_n->second = f_n->second );
2187 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2188 const int* volConn = splitMethod._connectivity;
2189 if ( splitMethod._nbCorners == 4 ) // tetra
2190 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2191 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2192 nodes[ volConn[1] ],
2193 nodes[ volConn[2] ],
2194 nodes[ volConn[3] ]));
2196 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2197 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2198 nodes[ volConn[1] ],
2199 nodes[ volConn[2] ],
2200 nodes[ volConn[3] ],
2201 nodes[ volConn[4] ],
2202 nodes[ volConn[5] ]));
2204 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2206 // Split faces on sides of the split volume
2208 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2209 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2211 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2212 if ( nbNodes < 4 ) continue;
2214 // find an existing face
2215 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2216 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2217 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2218 /*noMedium=*/false))
2221 helper.SetElementsOnShape( false );
2222 vector< const SMDS_MeshElement* > triangles;
2224 // find submesh to add new triangles in
2225 if ( !fSubMesh || !fSubMesh->Contains( face ))
2227 int shapeID = FindShape( face );
2228 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2230 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2231 if ( iF_n != splitMethod._faceBaryNode.end() )
2233 const SMDS_MeshNode *baryNode = iF_n->second;
2234 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2236 const SMDS_MeshNode* n1 = fNodes[iN];
2237 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2238 const SMDS_MeshNode *n3 = baryNode;
2239 if ( !volTool.IsFaceExternal( iF ))
2241 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2243 if ( fSubMesh ) // update position of the bary node on geometry
2246 subMesh->RemoveNode( baryNode, false );
2247 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2248 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2249 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2251 fHelper.SetSubShape( s );
2252 gp_XY uv( 1e100, 1e100 );
2254 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2255 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2258 // node is too far from the surface
2259 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2260 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2261 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2268 // among possible triangles create ones discribed by split method
2269 const int* nInd = volTool.GetFaceNodesIndices( iF );
2270 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2271 int iCom = 0; // common node of triangle faces to split into
2272 list< TTriangleFacet > facets;
2273 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2275 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2276 nInd[ iQ * ( (iCom+1)%nbNodes )],
2277 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2278 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2279 nInd[ iQ * ( (iCom+2)%nbNodes )],
2280 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2281 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2283 facets.push_back( t012 );
2284 facets.push_back( t023 );
2285 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2286 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2287 nInd[ iQ * ((iLast-1)%nbNodes )],
2288 nInd[ iQ * ((iLast )%nbNodes )]));
2292 list< TTriangleFacet >::iterator facet = facets.begin();
2293 if ( facet == facets.end() )
2295 for ( ; facet != facets.end(); ++facet )
2297 if ( !volTool.IsFaceExternal( iF ))
2298 swap( facet->_n2, facet->_n3 );
2299 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2300 volNodes[ facet->_n2 ],
2301 volNodes[ facet->_n3 ]));
2304 for ( int i = 0; i < triangles.size(); ++i )
2306 if ( !triangles[i] ) continue;
2308 fSubMesh->AddElement( triangles[i]);
2309 newElems.Append( triangles[i] );
2311 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2312 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2314 } // while a face based on facet nodes exists
2315 } // loop on volume faces to split them into triangles
2317 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2319 if ( geomType == SMDSEntity_TriQuad_Hexa )
2321 // remove medium nodes that could become free
2322 for ( int i = 20; i < volTool.NbNodes(); ++i )
2323 if ( volNodes[i]->NbInverseElements() == 0 )
2324 GetMeshDS()->RemoveNode( volNodes[i] );
2326 } // loop on volumes to split
2328 myLastCreatedNodes = newNodes;
2329 myLastCreatedElems = newElems;
2332 //=======================================================================
2333 //function : GetHexaFacetsToSplit
2334 //purpose : For hexahedra that will be split into prisms, finds facets to
2335 // split into triangles. Only hexahedra adjacent to the one closest
2336 // to theFacetNormal.Location() are returned.
2337 //param [in,out] theHexas - the hexahedra
2338 //param [in] theFacetNormal - facet normal
2339 //param [out] theFacets - the hexahedra and found facet IDs
2340 //=======================================================================
2342 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2343 const gp_Ax1& theFacetNormal,
2344 TFacetOfElem & theFacets)
2346 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2348 // Find a hexa closest to the location of theFacetNormal
2350 const SMDS_MeshElement* startHex;
2352 // get SMDS_ElemIteratorPtr on theHexas
2353 typedef const SMDS_MeshElement* TValue;
2354 typedef TIDSortedElemSet::iterator TSetIterator;
2355 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2356 typedef SMDS_MeshElement::GeomFilter TFilter;
2357 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2358 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2359 ( new TElemSetIter( theHexas.begin(),
2361 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2363 SMESH_ElementSearcher* searcher =
2364 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2366 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2371 throw SALOME_Exception( THIS_METHOD "startHex not found");
2374 // Select a facet of startHex by theFacetNormal
2376 SMDS_VolumeTool vTool( startHex );
2377 double norm[3], dot, maxDot = 0;
2379 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2380 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2382 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2390 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2392 // Fill theFacets starting from facetID of startHex
2394 // facets used for seach of volumes adjacent to already treated ones
2395 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2396 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2397 TFacetMap facetsToCheck;
2399 set<const SMDS_MeshNode*> facetNodes;
2400 const SMDS_MeshElement* curHex;
2402 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2406 // move in two directions from startHex via facetID
2407 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2410 int curFacet = facetID;
2411 if ( is2nd ) // do not treat startHex twice
2413 vTool.Set( curHex );
2414 if ( vTool.IsFreeFace( curFacet, &curHex ))
2420 vTool.GetFaceNodes( curFacet, facetNodes );
2421 vTool.Set( curHex );
2422 curFacet = vTool.GetFaceIndex( facetNodes );
2427 // store a facet to split
2428 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2430 theFacets.insert( make_pair( curHex, -1 ));
2433 if ( !allHex && !theHexas.count( curHex ))
2436 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2437 theFacets.insert( make_pair( curHex, curFacet ));
2438 if ( !facetIt2isNew.second )
2441 // remember not-to-split facets in facetsToCheck
2442 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2443 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2445 if ( iF == curFacet && iF == oppFacet )
2447 TVolumeFaceKey facetKey ( vTool, iF );
2448 TElemFacets elemFacet( facetIt2isNew.first, iF );
2449 pair< TFacetMap::iterator, bool > it2isnew =
2450 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2451 if ( !it2isnew.second )
2452 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2454 // pass to a volume adjacent via oppFacet
2455 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2461 // get a new curFacet
2462 vTool.GetFaceNodes( oppFacet, facetNodes );
2463 vTool.Set( curHex );
2464 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2467 } // move in two directions from startHex via facetID
2469 // Find a new startHex by facetsToCheck
2473 TFacetMap::iterator fIt = facetsToCheck.begin();
2474 while ( !startHex && fIt != facetsToCheck.end() )
2476 const TElemFacets& elemFacets = fIt->second;
2477 const SMDS_MeshElement* hex = elemFacets.first->first;
2478 int splitFacet = elemFacets.first->second;
2479 int lateralFacet = elemFacets.second;
2480 facetsToCheck.erase( fIt );
2481 fIt = facetsToCheck.begin();
2484 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2485 curHex->GetGeomType() != SMDSGeom_HEXA )
2487 if ( !allHex && !theHexas.count( curHex ))
2492 // find a facet of startHex to split
2494 set<const SMDS_MeshNode*> lateralNodes;
2495 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2496 vTool.GetFaceNodes( splitFacet, facetNodes );
2497 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2498 vTool.Set( startHex );
2499 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2501 // look for a facet of startHex having common nodes with facetNodes
2502 // but not lateralFacet
2503 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2505 if ( iF == lateralFacet )
2507 int nbCommonNodes = 0;
2508 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2509 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2510 nbCommonNodes += facetNodes.count( nn[ iN ]);
2512 if ( nbCommonNodes >= 2 )
2519 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2521 } // while ( startHex )
2524 //=======================================================================
2525 //function : AddToSameGroups
2526 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2527 //=======================================================================
2529 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2530 const SMDS_MeshElement* elemInGroups,
2531 SMESHDS_Mesh * aMesh)
2533 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2534 if (!groups.empty()) {
2535 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2536 for ( ; grIt != groups.end(); grIt++ ) {
2537 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2538 if ( group && group->Contains( elemInGroups ))
2539 group->SMDSGroup().Add( elemToAdd );
2545 //=======================================================================
2546 //function : RemoveElemFromGroups
2547 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2548 //=======================================================================
2549 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2550 SMESHDS_Mesh * aMesh)
2552 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2553 if (!groups.empty())
2555 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2556 for (; GrIt != groups.end(); GrIt++)
2558 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2559 if (!grp || grp->IsEmpty()) continue;
2560 grp->SMDSGroup().Remove(removeelem);
2565 //================================================================================
2567 * \brief Replace elemToRm by elemToAdd in the all groups
2569 //================================================================================
2571 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2572 const SMDS_MeshElement* elemToAdd,
2573 SMESHDS_Mesh * aMesh)
2575 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2576 if (!groups.empty()) {
2577 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2578 for ( ; grIt != groups.end(); grIt++ ) {
2579 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2580 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2581 group->SMDSGroup().Add( elemToAdd );
2586 //================================================================================
2588 * \brief Replace elemToRm by elemToAdd in the all groups
2590 //================================================================================
2592 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2593 const vector<const SMDS_MeshElement*>& elemToAdd,
2594 SMESHDS_Mesh * aMesh)
2596 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2597 if (!groups.empty())
2599 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2600 for ( ; grIt != groups.end(); grIt++ ) {
2601 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2602 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2603 for ( int i = 0; i < elemToAdd.size(); ++i )
2604 group->SMDSGroup().Add( elemToAdd[ i ] );
2609 //=======================================================================
2610 //function : QuadToTri
2611 //purpose : Cut quadrangles into triangles.
2612 // theCrit is used to select a diagonal to cut
2613 //=======================================================================
2615 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2616 const bool the13Diag)
2618 myLastCreatedElems.Clear();
2619 myLastCreatedNodes.Clear();
2621 MESSAGE( "::QuadToTri()" );
2623 SMESHDS_Mesh * aMesh = GetMeshDS();
2625 Handle(Geom_Surface) surface;
2626 SMESH_MesherHelper helper( *GetMesh() );
2628 TIDSortedElemSet::iterator itElem;
2629 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2630 const SMDS_MeshElement* elem = *itElem;
2631 if ( !elem || elem->GetType() != SMDSAbs_Face )
2633 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2634 if(!isquad) continue;
2636 if(elem->NbNodes()==4) {
2637 // retrieve element nodes
2638 const SMDS_MeshNode* aNodes [4];
2639 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2641 while ( itN->more() )
2642 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2644 int aShapeId = FindShape( elem );
2645 const SMDS_MeshElement* newElem1 = 0;
2646 const SMDS_MeshElement* newElem2 = 0;
2648 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2649 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2652 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2653 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2655 myLastCreatedElems.Append(newElem1);
2656 myLastCreatedElems.Append(newElem2);
2657 // put a new triangle on the same shape and add to the same groups
2660 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2661 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2663 AddToSameGroups( newElem1, elem, aMesh );
2664 AddToSameGroups( newElem2, elem, aMesh );
2665 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2666 aMesh->RemoveElement( elem );
2669 // Quadratic quadrangle
2671 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2673 // get surface elem is on
2674 int aShapeId = FindShape( elem );
2675 if ( aShapeId != helper.GetSubShapeID() ) {
2679 shape = aMesh->IndexToShape( aShapeId );
2680 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2681 TopoDS_Face face = TopoDS::Face( shape );
2682 surface = BRep_Tool::Surface( face );
2683 if ( !surface.IsNull() )
2684 helper.SetSubShape( shape );
2688 const SMDS_MeshNode* aNodes [8];
2689 const SMDS_MeshNode* inFaceNode = 0;
2690 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2692 while ( itN->more() ) {
2693 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2694 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2695 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2697 inFaceNode = aNodes[ i-1 ];
2701 // find middle point for (0,1,2,3)
2702 // and create a node in this point;
2704 if ( surface.IsNull() ) {
2706 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2710 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2713 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2715 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2717 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2718 myLastCreatedNodes.Append(newN);
2720 // create a new element
2721 const SMDS_MeshElement* newElem1 = 0;
2722 const SMDS_MeshElement* newElem2 = 0;
2724 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2725 aNodes[6], aNodes[7], newN );
2726 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2727 newN, aNodes[4], aNodes[5] );
2730 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2731 aNodes[7], aNodes[4], newN );
2732 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2733 newN, aNodes[5], aNodes[6] );
2735 myLastCreatedElems.Append(newElem1);
2736 myLastCreatedElems.Append(newElem2);
2737 // put a new triangle on the same shape and add to the same groups
2740 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2741 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2743 AddToSameGroups( newElem1, elem, aMesh );
2744 AddToSameGroups( newElem2, elem, aMesh );
2745 aMesh->RemoveElement( elem );
2752 //=======================================================================
2753 //function : getAngle
2755 //=======================================================================
2757 double getAngle(const SMDS_MeshElement * tr1,
2758 const SMDS_MeshElement * tr2,
2759 const SMDS_MeshNode * n1,
2760 const SMDS_MeshNode * n2)
2762 double angle = 2. * M_PI; // bad angle
2765 SMESH::Controls::TSequenceOfXYZ P1, P2;
2766 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2767 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2770 if(!tr1->IsQuadratic())
2771 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2773 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2774 if ( N1.SquareMagnitude() <= gp::Resolution() )
2776 if(!tr2->IsQuadratic())
2777 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2779 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2780 if ( N2.SquareMagnitude() <= gp::Resolution() )
2783 // find the first diagonal node n1 in the triangles:
2784 // take in account a diagonal link orientation
2785 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2786 for ( int t = 0; t < 2; t++ ) {
2787 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2788 int i = 0, iDiag = -1;
2789 while ( it->more()) {
2790 const SMDS_MeshElement *n = it->next();
2791 if ( n == n1 || n == n2 ) {
2795 if ( i - iDiag == 1 )
2796 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2805 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2808 angle = N1.Angle( N2 );
2813 // =================================================
2814 // class generating a unique ID for a pair of nodes
2815 // and able to return nodes by that ID
2816 // =================================================
2820 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2821 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2824 long GetLinkID (const SMDS_MeshNode * n1,
2825 const SMDS_MeshNode * n2) const
2827 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2830 bool GetNodes (const long theLinkID,
2831 const SMDS_MeshNode* & theNode1,
2832 const SMDS_MeshNode* & theNode2) const
2834 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2835 if ( !theNode1 ) return false;
2836 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2837 if ( !theNode2 ) return false;
2843 const SMESHDS_Mesh* myMesh;
2848 //=======================================================================
2849 //function : TriToQuad
2850 //purpose : Fuse neighbour triangles into quadrangles.
2851 // theCrit is used to select a neighbour to fuse with.
2852 // theMaxAngle is a max angle between element normals at which
2853 // fusion is still performed.
2854 //=======================================================================
2856 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2857 SMESH::Controls::NumericalFunctorPtr theCrit,
2858 const double theMaxAngle)
2860 myLastCreatedElems.Clear();
2861 myLastCreatedNodes.Clear();
2863 MESSAGE( "::TriToQuad()" );
2865 if ( !theCrit.get() )
2868 SMESHDS_Mesh * aMesh = GetMeshDS();
2870 // Prepare data for algo: build
2871 // 1. map of elements with their linkIDs
2872 // 2. map of linkIDs with their elements
2874 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2875 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2876 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2877 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2879 TIDSortedElemSet::iterator itElem;
2880 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2882 const SMDS_MeshElement* elem = *itElem;
2883 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2884 bool IsTria = ( elem->NbCornerNodes()==3 );
2885 if (!IsTria) continue;
2887 // retrieve element nodes
2888 const SMDS_MeshNode* aNodes [4];
2889 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2892 aNodes[ i++ ] = itN->next();
2893 aNodes[ 3 ] = aNodes[ 0 ];
2896 for ( i = 0; i < 3; i++ ) {
2897 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2898 // check if elements sharing a link can be fused
2899 itLE = mapLi_listEl.find( link );
2900 if ( itLE != mapLi_listEl.end() ) {
2901 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2903 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2904 //if ( FindShape( elem ) != FindShape( elem2 ))
2905 // continue; // do not fuse triangles laying on different shapes
2906 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2907 continue; // avoid making badly shaped quads
2908 (*itLE).second.push_back( elem );
2911 mapLi_listEl[ link ].push_back( elem );
2913 mapEl_setLi [ elem ].insert( link );
2916 // Clean the maps from the links shared by a sole element, ie
2917 // links to which only one element is bound in mapLi_listEl
2919 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2920 int nbElems = (*itLE).second.size();
2921 if ( nbElems < 2 ) {
2922 const SMDS_MeshElement* elem = (*itLE).second.front();
2923 SMESH_TLink link = (*itLE).first;
2924 mapEl_setLi[ elem ].erase( link );
2925 if ( mapEl_setLi[ elem ].empty() )
2926 mapEl_setLi.erase( elem );
2930 // Algo: fuse triangles into quadrangles
2932 while ( ! mapEl_setLi.empty() ) {
2933 // Look for the start element:
2934 // the element having the least nb of shared links
2935 const SMDS_MeshElement* startElem = 0;
2937 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2938 int nbLinks = (*itEL).second.size();
2939 if ( nbLinks < minNbLinks ) {
2940 startElem = (*itEL).first;
2941 minNbLinks = nbLinks;
2942 if ( minNbLinks == 1 )
2947 // search elements to fuse starting from startElem or links of elements
2948 // fused earlyer - startLinks
2949 list< SMESH_TLink > startLinks;
2950 while ( startElem || !startLinks.empty() ) {
2951 while ( !startElem && !startLinks.empty() ) {
2952 // Get an element to start, by a link
2953 SMESH_TLink linkId = startLinks.front();
2954 startLinks.pop_front();
2955 itLE = mapLi_listEl.find( linkId );
2956 if ( itLE != mapLi_listEl.end() ) {
2957 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2958 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2959 for ( ; itE != listElem.end() ; itE++ )
2960 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2962 mapLi_listEl.erase( itLE );
2967 // Get candidates to be fused
2968 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2969 const SMESH_TLink *link12, *link13;
2971 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2972 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2973 ASSERT( !setLi.empty() );
2974 set< SMESH_TLink >::iterator itLi;
2975 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2977 const SMESH_TLink & link = (*itLi);
2978 itLE = mapLi_listEl.find( link );
2979 if ( itLE == mapLi_listEl.end() )
2982 const SMDS_MeshElement* elem = (*itLE).second.front();
2984 elem = (*itLE).second.back();
2985 mapLi_listEl.erase( itLE );
2986 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2997 // add other links of elem to list of links to re-start from
2998 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2999 set< SMESH_TLink >::iterator it;
3000 for ( it = links.begin(); it != links.end(); it++ ) {
3001 const SMESH_TLink& link2 = (*it);
3002 if ( link2 != link )
3003 startLinks.push_back( link2 );
3007 // Get nodes of possible quadrangles
3008 const SMDS_MeshNode *n12 [4], *n13 [4];
3009 bool Ok12 = false, Ok13 = false;
3010 const SMDS_MeshNode *linkNode1, *linkNode2;
3012 linkNode1 = link12->first;
3013 linkNode2 = link12->second;
3014 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3018 linkNode1 = link13->first;
3019 linkNode2 = link13->second;
3020 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3024 // Choose a pair to fuse
3025 if ( Ok12 && Ok13 ) {
3026 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3027 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3028 double aBadRate12 = getBadRate( &quad12, theCrit );
3029 double aBadRate13 = getBadRate( &quad13, theCrit );
3030 if ( aBadRate13 < aBadRate12 )
3037 // and remove fused elems and remove links from the maps
3038 mapEl_setLi.erase( tr1 );
3041 mapEl_setLi.erase( tr2 );
3042 mapLi_listEl.erase( *link12 );
3043 if ( tr1->NbNodes() == 3 )
3045 const SMDS_MeshElement* newElem = 0;
3046 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3047 myLastCreatedElems.Append(newElem);
3048 AddToSameGroups( newElem, tr1, aMesh );
3049 int aShapeId = tr1->getshapeId();
3051 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3052 aMesh->RemoveElement( tr1 );
3053 aMesh->RemoveElement( tr2 );
3056 vector< const SMDS_MeshNode* > N1;
3057 vector< const SMDS_MeshNode* > N2;
3058 getNodesFromTwoTria(tr1,tr2,N1,N2);
3059 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3060 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3061 // i.e. first nodes from both arrays form a new diagonal
3062 const SMDS_MeshNode* aNodes[8];
3071 const SMDS_MeshElement* newElem = 0;
3072 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3073 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3074 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3076 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3077 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3078 myLastCreatedElems.Append(newElem);
3079 AddToSameGroups( newElem, tr1, aMesh );
3080 int aShapeId = tr1->getshapeId();
3082 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3083 aMesh->RemoveElement( tr1 );
3084 aMesh->RemoveElement( tr2 );
3085 // remove middle node (9)
3086 if ( N1[4]->NbInverseElements() == 0 )
3087 aMesh->RemoveNode( N1[4] );
3088 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3089 aMesh->RemoveNode( N1[6] );
3090 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3091 aMesh->RemoveNode( N2[6] );
3096 mapEl_setLi.erase( tr3 );
3097 mapLi_listEl.erase( *link13 );
3098 if ( tr1->NbNodes() == 3 ) {
3099 const SMDS_MeshElement* newElem = 0;
3100 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3101 myLastCreatedElems.Append(newElem);
3102 AddToSameGroups( newElem, tr1, aMesh );
3103 int aShapeId = tr1->getshapeId();
3105 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3106 aMesh->RemoveElement( tr1 );
3107 aMesh->RemoveElement( tr3 );
3110 vector< const SMDS_MeshNode* > N1;
3111 vector< const SMDS_MeshNode* > N2;
3112 getNodesFromTwoTria(tr1,tr3,N1,N2);
3113 // now we receive following N1 and N2 (using numeration as above image)
3114 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3115 // i.e. first nodes from both arrays form a new diagonal
3116 const SMDS_MeshNode* aNodes[8];
3125 const SMDS_MeshElement* newElem = 0;
3126 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3127 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3128 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3130 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3131 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3132 myLastCreatedElems.Append(newElem);
3133 AddToSameGroups( newElem, tr1, aMesh );
3134 int aShapeId = tr1->getshapeId();
3136 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3137 aMesh->RemoveElement( tr1 );
3138 aMesh->RemoveElement( tr3 );
3139 // remove middle node (9)
3140 if ( N1[4]->NbInverseElements() == 0 )
3141 aMesh->RemoveNode( N1[4] );
3142 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3143 aMesh->RemoveNode( N1[6] );
3144 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3145 aMesh->RemoveNode( N2[6] );
3149 // Next element to fuse: the rejected one
3151 startElem = Ok12 ? tr3 : tr2;
3153 } // if ( startElem )
3154 } // while ( startElem || !startLinks.empty() )
3155 } // while ( ! mapEl_setLi.empty() )
3161 /*#define DUMPSO(txt) \
3162 // cout << txt << endl;
3163 //=============================================================================
3167 //=============================================================================
3168 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3172 int tmp = idNodes[ i1 ];
3173 idNodes[ i1 ] = idNodes[ i2 ];
3174 idNodes[ i2 ] = tmp;
3175 gp_Pnt Ptmp = P[ i1 ];
3178 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3181 //=======================================================================
3182 //function : SortQuadNodes
3183 //purpose : Set 4 nodes of a quadrangle face in a good order.
3184 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3186 //=======================================================================
3188 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3193 for ( i = 0; i < 4; i++ ) {
3194 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3196 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3199 gp_Vec V1(P[0], P[1]);
3200 gp_Vec V2(P[0], P[2]);
3201 gp_Vec V3(P[0], P[3]);
3203 gp_Vec Cross1 = V1 ^ V2;
3204 gp_Vec Cross2 = V2 ^ V3;
3207 if (Cross1.Dot(Cross2) < 0)
3212 if (Cross1.Dot(Cross2) < 0)
3216 swap ( i, i + 1, idNodes, P );
3218 // for ( int ii = 0; ii < 4; ii++ ) {
3219 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3220 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3226 //=======================================================================
3227 //function : SortHexaNodes
3228 //purpose : Set 8 nodes of a hexahedron in a good order.
3229 // Return success status
3230 //=======================================================================
3232 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3237 DUMPSO( "INPUT: ========================================");
3238 for ( i = 0; i < 8; i++ ) {
3239 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3240 if ( !n ) return false;
3241 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3242 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3244 DUMPSO( "========================================");
3247 set<int> faceNodes; // ids of bottom face nodes, to be found
3248 set<int> checkedId1; // ids of tried 2-nd nodes
3249 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3250 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3251 int iMin, iLoop1 = 0;
3253 // Loop to try the 2-nd nodes
3255 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3257 // Find not checked 2-nd node
3258 for ( i = 1; i < 8; i++ )
3259 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3260 int id1 = idNodes[i];
3261 swap ( 1, i, idNodes, P );
3262 checkedId1.insert ( id1 );
3266 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3267 // ie that all but meybe one (id3 which is on the same face) nodes
3268 // lay on the same side from the triangle plane.
3270 bool manyInPlane = false; // more than 4 nodes lay in plane
3272 while ( ++iLoop2 < 6 ) {
3274 // get 1-2-3 plane coeffs
3275 Standard_Real A, B, C, D;
3276 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3277 if ( N.SquareMagnitude() > gp::Resolution() )
3279 gp_Pln pln ( P[0], N );
3280 pln.Coefficients( A, B, C, D );
3282 // find the node (iMin) closest to pln
3283 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3285 for ( i = 3; i < 8; i++ ) {
3286 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3287 if ( fabs( dist[i] ) < minDist ) {
3288 minDist = fabs( dist[i] );
3291 if ( fabs( dist[i] ) <= tol )
3292 idInPln.insert( idNodes[i] );
3295 // there should not be more than 4 nodes in bottom plane
3296 if ( idInPln.size() > 1 )
3298 DUMPSO( "### idInPln.size() = " << idInPln.size());
3299 // idInPlane does not contain the first 3 nodes
3300 if ( manyInPlane || idInPln.size() == 5)
3301 return false; // all nodes in one plane
3304 // set the 1-st node to be not in plane
3305 for ( i = 3; i < 8; i++ ) {
3306 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3307 DUMPSO( "### Reset 0-th node");
3308 swap( 0, i, idNodes, P );
3313 // reset to re-check second nodes
3314 leastDist = DBL_MAX;
3318 break; // from iLoop2;
3321 // check that the other 4 nodes are on the same side
3322 bool sameSide = true;
3323 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3324 for ( i = 3; sameSide && i < 8; i++ ) {
3326 sameSide = ( isNeg == dist[i] <= 0.);
3329 // keep best solution
3330 if ( sameSide && minDist < leastDist ) {
3331 leastDist = minDist;
3333 faceNodes.insert( idNodes[ 1 ] );
3334 faceNodes.insert( idNodes[ 2 ] );
3335 faceNodes.insert( idNodes[ iMin ] );
3336 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3337 << " leastDist = " << leastDist);
3338 if ( leastDist <= DBL_MIN )
3343 // set next 3-d node to check
3344 int iNext = 2 + iLoop2;
3346 DUMPSO( "Try 2-nd");
3347 swap ( 2, iNext, idNodes, P );
3349 } // while ( iLoop2 < 6 )
3352 if ( faceNodes.empty() ) return false;
3354 // Put the faceNodes in proper places
3355 for ( i = 4; i < 8; i++ ) {
3356 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3357 // find a place to put
3359 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3361 DUMPSO( "Set faceNodes");
3362 swap ( iTo, i, idNodes, P );
3367 // Set nodes of the found bottom face in good order
3368 DUMPSO( " Found bottom face: ");
3369 i = SortQuadNodes( theMesh, idNodes );
3371 gp_Pnt Ptmp = P[ i ];
3376 // for ( int ii = 0; ii < 4; ii++ ) {
3377 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3378 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3381 // Gravity center of the top and bottom faces
3382 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3383 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3385 // Get direction from the bottom to the top face
3386 gp_Vec upDir ( aGCb, aGCt );
3387 Standard_Real upDirSize = upDir.Magnitude();
3388 if ( upDirSize <= gp::Resolution() ) return false;
3391 // Assure that the bottom face normal points up
3392 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3393 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3394 if ( Nb.Dot( upDir ) < 0 ) {
3395 DUMPSO( "Reverse bottom face");
3396 swap( 1, 3, idNodes, P );
3399 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3400 Standard_Real minDist = DBL_MAX;
3401 for ( i = 4; i < 8; i++ ) {
3402 // projection of P[i] to the plane defined by P[0] and upDir
3403 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3404 Standard_Real sqDist = P[0].SquareDistance( Pp );
3405 if ( sqDist < minDist ) {
3410 DUMPSO( "Set 4-th");
3411 swap ( 4, iMin, idNodes, P );
3413 // Set nodes of the top face in good order
3414 DUMPSO( "Sort top face");
3415 i = SortQuadNodes( theMesh, &idNodes[4] );
3418 gp_Pnt Ptmp = P[ i ];
3423 // Assure that direction of the top face normal is from the bottom face
3424 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3425 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3426 if ( Nt.Dot( upDir ) < 0 ) {
3427 DUMPSO( "Reverse top face");
3428 swap( 5, 7, idNodes, P );
3431 // DUMPSO( "OUTPUT: ========================================");
3432 // for ( i = 0; i < 8; i++ ) {
3433 // float *p = ugrid->GetPoint(idNodes[i]);
3434 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3440 //================================================================================
3442 * \brief Return nodes linked to the given one
3443 * \param theNode - the node
3444 * \param linkedNodes - the found nodes
3445 * \param type - the type of elements to check
3447 * Medium nodes are ignored
3449 //================================================================================
3451 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3452 TIDSortedElemSet & linkedNodes,
3453 SMDSAbs_ElementType type )
3455 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3456 while ( elemIt->more() )
3458 const SMDS_MeshElement* elem = elemIt->next();
3459 if(elem->GetType() == SMDSAbs_0DElement)
3462 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3463 if ( elem->GetType() == SMDSAbs_Volume )
3465 SMDS_VolumeTool vol( elem );
3466 while ( nodeIt->more() ) {
3467 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3468 if ( theNode != n && vol.IsLinked( theNode, n ))
3469 linkedNodes.insert( n );
3474 for ( int i = 0; nodeIt->more(); ++i ) {
3475 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3476 if ( n == theNode ) {
3477 int iBefore = i - 1;
3479 if ( elem->IsQuadratic() ) {
3480 int nb = elem->NbNodes() / 2;
3481 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3482 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3484 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3485 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3492 //=======================================================================
3493 //function : laplacianSmooth
3494 //purpose : pulls theNode toward the center of surrounding nodes directly
3495 // connected to that node along an element edge
3496 //=======================================================================
3498 void laplacianSmooth(const SMDS_MeshNode* theNode,
3499 const Handle(Geom_Surface)& theSurface,
3500 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3502 // find surrounding nodes
3504 TIDSortedElemSet nodeSet;
3505 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3507 // compute new coodrs
3509 double coord[] = { 0., 0., 0. };
3510 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3511 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3512 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3513 if ( theSurface.IsNull() ) { // smooth in 3D
3514 coord[0] += node->X();
3515 coord[1] += node->Y();
3516 coord[2] += node->Z();
3518 else { // smooth in 2D
3519 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3520 gp_XY* uv = theUVMap[ node ];
3521 coord[0] += uv->X();
3522 coord[1] += uv->Y();
3525 int nbNodes = nodeSet.size();
3528 coord[0] /= nbNodes;
3529 coord[1] /= nbNodes;
3531 if ( !theSurface.IsNull() ) {
3532 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3533 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3534 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3540 coord[2] /= nbNodes;
3544 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3547 //=======================================================================
3548 //function : centroidalSmooth
3549 //purpose : pulls theNode toward the element-area-weighted centroid of the
3550 // surrounding elements
3551 //=======================================================================
3553 void centroidalSmooth(const SMDS_MeshNode* theNode,
3554 const Handle(Geom_Surface)& theSurface,
3555 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3557 gp_XYZ aNewXYZ(0.,0.,0.);
3558 SMESH::Controls::Area anAreaFunc;
3559 double totalArea = 0.;
3564 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3565 while ( elemIt->more() )
3567 const SMDS_MeshElement* elem = elemIt->next();
3570 gp_XYZ elemCenter(0.,0.,0.);
3571 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3572 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3573 int nn = elem->NbNodes();
3574 if(elem->IsQuadratic()) nn = nn/2;
3576 //while ( itN->more() ) {
3578 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3580 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3581 aNodePoints.push_back( aP );
3582 if ( !theSurface.IsNull() ) { // smooth in 2D
3583 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3584 gp_XY* uv = theUVMap[ aNode ];
3585 aP.SetCoord( uv->X(), uv->Y(), 0. );
3589 double elemArea = anAreaFunc.GetValue( aNodePoints );
3590 totalArea += elemArea;
3592 aNewXYZ += elemCenter * elemArea;
3594 aNewXYZ /= totalArea;
3595 if ( !theSurface.IsNull() ) {
3596 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3597 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3602 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3605 //=======================================================================
3606 //function : getClosestUV
3607 //purpose : return UV of closest projection
3608 //=======================================================================
3610 static bool getClosestUV (Extrema_GenExtPS& projector,
3611 const gp_Pnt& point,
3614 projector.Perform( point );
3615 if ( projector.IsDone() ) {
3616 double u, v, minVal = DBL_MAX;
3617 for ( int i = projector.NbExt(); i > 0; i-- )
3618 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3619 if ( projector.SquareDistance( i ) < minVal ) {
3620 minVal = projector.SquareDistance( i );
3622 if ( projector.Value( i ) < minVal ) {
3623 minVal = projector.Value( i );
3625 projector.Point( i ).Parameter( u, v );
3627 result.SetCoord( u, v );
3633 //=======================================================================
3635 //purpose : Smooth theElements during theNbIterations or until a worst
3636 // element has aspect ratio <= theTgtAspectRatio.
3637 // Aspect Ratio varies in range [1.0, inf].
3638 // If theElements is empty, the whole mesh is smoothed.
3639 // theFixedNodes contains additionally fixed nodes. Nodes built
3640 // on edges and boundary nodes are always fixed.
3641 //=======================================================================
3643 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3644 set<const SMDS_MeshNode*> & theFixedNodes,
3645 const SmoothMethod theSmoothMethod,
3646 const int theNbIterations,
3647 double theTgtAspectRatio,
3650 myLastCreatedElems.Clear();
3651 myLastCreatedNodes.Clear();
3653 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3655 if ( theTgtAspectRatio < 1.0 )
3656 theTgtAspectRatio = 1.0;
3658 const double disttol = 1.e-16;
3660 SMESH::Controls::AspectRatio aQualityFunc;
3662 SMESHDS_Mesh* aMesh = GetMeshDS();
3664 if ( theElems.empty() ) {
3665 // add all faces to theElems
3666 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3667 while ( fIt->more() ) {
3668 const SMDS_MeshElement* face = fIt->next();
3669 theElems.insert( theElems.end(), face );
3672 // get all face ids theElems are on
3673 set< int > faceIdSet;
3674 TIDSortedElemSet::iterator itElem;
3676 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3677 int fId = FindShape( *itElem );
3678 // check that corresponding submesh exists and a shape is face
3680 faceIdSet.find( fId ) == faceIdSet.end() &&
3681 aMesh->MeshElements( fId )) {
3682 TopoDS_Shape F = aMesh->IndexToShape( fId );
3683 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3684 faceIdSet.insert( fId );
3687 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3689 // ===============================================
3690 // smooth elements on each TopoDS_Face separately
3691 // ===============================================
3693 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3694 for ( ; fId != faceIdSet.rend(); ++fId ) {
3695 // get face surface and submesh
3696 Handle(Geom_Surface) surface;
3697 SMESHDS_SubMesh* faceSubMesh = 0;
3699 double fToler2 = 0, f,l;
3700 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3701 bool isUPeriodic = false, isVPeriodic = false;
3703 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3704 surface = BRep_Tool::Surface( face );
3705 faceSubMesh = aMesh->MeshElements( *fId );
3706 fToler2 = BRep_Tool::Tolerance( face );
3707 fToler2 *= fToler2 * 10.;
3708 isUPeriodic = surface->IsUPeriodic();
3711 isVPeriodic = surface->IsVPeriodic();
3714 surface->Bounds( u1, u2, v1, v2 );
3716 // ---------------------------------------------------------
3717 // for elements on a face, find movable and fixed nodes and
3718 // compute UV for them
3719 // ---------------------------------------------------------
3720 bool checkBoundaryNodes = false;
3721 bool isQuadratic = false;
3722 set<const SMDS_MeshNode*> setMovableNodes;
3723 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3724 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3725 list< const SMDS_MeshElement* > elemsOnFace;
3727 Extrema_GenExtPS projector;
3728 GeomAdaptor_Surface surfAdaptor;
3729 if ( !surface.IsNull() ) {
3730 surfAdaptor.Load( surface );
3731 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3733 int nbElemOnFace = 0;
3734 itElem = theElems.begin();
3735 // loop on not yet smoothed elements: look for elems on a face
3736 while ( itElem != theElems.end() ) {
3737 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3738 break; // all elements found
3740 const SMDS_MeshElement* elem = *itElem;
3741 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3742 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3746 elemsOnFace.push_back( elem );
3747 theElems.erase( itElem++ );
3751 isQuadratic = elem->IsQuadratic();
3753 // get movable nodes of elem
3754 const SMDS_MeshNode* node;
3755 SMDS_TypeOfPosition posType;
3756 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3757 int nn = 0, nbn = elem->NbNodes();
3758 if(elem->IsQuadratic())
3760 while ( nn++ < nbn ) {
3761 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3762 const SMDS_PositionPtr& pos = node->GetPosition();
3763 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3764 if (posType != SMDS_TOP_EDGE &&
3765 posType != SMDS_TOP_VERTEX &&
3766 theFixedNodes.find( node ) == theFixedNodes.end())
3768 // check if all faces around the node are on faceSubMesh
3769 // because a node on edge may be bound to face
3770 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3772 if ( faceSubMesh ) {
3773 while ( eIt->more() && all ) {
3774 const SMDS_MeshElement* e = eIt->next();
3775 all = faceSubMesh->Contains( e );
3779 setMovableNodes.insert( node );
3781 checkBoundaryNodes = true;
3783 if ( posType == SMDS_TOP_3DSPACE )
3784 checkBoundaryNodes = true;
3787 if ( surface.IsNull() )
3790 // get nodes to check UV
3791 list< const SMDS_MeshNode* > uvCheckNodes;
3792 itN = elem->nodesIterator();
3793 nn = 0; nbn = elem->NbNodes();
3794 if(elem->IsQuadratic())
3796 while ( nn++ < nbn ) {
3797 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3798 if ( uvMap.find( node ) == uvMap.end() )
3799 uvCheckNodes.push_back( node );
3800 // add nodes of elems sharing node
3801 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3802 // while ( eIt->more() ) {
3803 // const SMDS_MeshElement* e = eIt->next();
3804 // if ( e != elem ) {
3805 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3806 // while ( nIt->more() ) {
3807 // const SMDS_MeshNode* n =
3808 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3809 // if ( uvMap.find( n ) == uvMap.end() )
3810 // uvCheckNodes.push_back( n );
3816 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3817 for ( ; n != uvCheckNodes.end(); ++n ) {
3820 const SMDS_PositionPtr& pos = node->GetPosition();
3821 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3823 switch ( posType ) {
3824 case SMDS_TOP_FACE: {
3825 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3826 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3829 case SMDS_TOP_EDGE: {
3830 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3831 Handle(Geom2d_Curve) pcurve;
3832 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3833 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3834 if ( !pcurve.IsNull() ) {
3835 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3836 uv = pcurve->Value( u ).XY();
3840 case SMDS_TOP_VERTEX: {
3841 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3842 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3843 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3848 // check existing UV
3849 bool project = true;
3850 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3851 double dist1 = DBL_MAX, dist2 = 0;
3852 if ( posType != SMDS_TOP_3DSPACE ) {
3853 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3854 project = dist1 > fToler2;
3856 if ( project ) { // compute new UV
3858 if ( !getClosestUV( projector, pNode, newUV )) {
3859 MESSAGE("Node Projection Failed " << node);
3863 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3865 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3867 if ( posType != SMDS_TOP_3DSPACE )
3868 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3869 if ( dist2 < dist1 )
3873 // store UV in the map
3874 listUV.push_back( uv );
3875 uvMap.insert( make_pair( node, &listUV.back() ));
3877 } // loop on not yet smoothed elements
3879 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3880 checkBoundaryNodes = true;
3882 // fix nodes on mesh boundary
3884 if ( checkBoundaryNodes ) {
3885 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3886 map< SMESH_TLink, int >::iterator link_nb;
3887 // put all elements links to linkNbMap
3888 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3889 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3890 const SMDS_MeshElement* elem = (*elemIt);
3891 int nbn = elem->NbCornerNodes();
3892 // loop on elem links: insert them in linkNbMap
3893 for ( int iN = 0; iN < nbn; ++iN ) {
3894 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3895 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3896 SMESH_TLink link( n1, n2 );
3897 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3901 // remove nodes that are in links encountered only once from setMovableNodes
3902 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3903 if ( link_nb->second == 1 ) {
3904 setMovableNodes.erase( link_nb->first.node1() );
3905 setMovableNodes.erase( link_nb->first.node2() );
3910 // -----------------------------------------------------
3911 // for nodes on seam edge, compute one more UV ( uvMap2 );
3912 // find movable nodes linked to nodes on seam and which
3913 // are to be smoothed using the second UV ( uvMap2 )
3914 // -----------------------------------------------------
3916 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3917 if ( !surface.IsNull() ) {
3918 TopExp_Explorer eExp( face, TopAbs_EDGE );
3919 for ( ; eExp.More(); eExp.Next() ) {
3920 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3921 if ( !BRep_Tool::IsClosed( edge, face ))
3923 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3924 if ( !sm ) continue;
3925 // find out which parameter varies for a node on seam
3928 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3929 if ( pcurve.IsNull() ) continue;
3930 uv1 = pcurve->Value( f );
3932 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3933 if ( pcurve.IsNull() ) continue;
3934 uv2 = pcurve->Value( f );
3935 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3937 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3938 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3940 // get nodes on seam and its vertices
3941 list< const SMDS_MeshNode* > seamNodes;
3942 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3943 while ( nSeamIt->more() ) {
3944 const SMDS_MeshNode* node = nSeamIt->next();
3945 if ( !isQuadratic || !IsMedium( node ))
3946 seamNodes.push_back( node );
3948 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3949 for ( ; vExp.More(); vExp.Next() ) {
3950 sm = aMesh->MeshElements( vExp.Current() );
3952 nSeamIt = sm->GetNodes();
3953 while ( nSeamIt->more() )
3954 seamNodes.push_back( nSeamIt->next() );
3957 // loop on nodes on seam
3958 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3959 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3960 const SMDS_MeshNode* nSeam = *noSeIt;
3961 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3962 if ( n_uv == uvMap.end() )
3965 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3966 // set the second UV
3967 listUV.push_back( *n_uv->second );
3968 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3969 if ( uvMap2.empty() )
3970 uvMap2 = uvMap; // copy the uvMap contents
3971 uvMap2[ nSeam ] = &listUV.back();
3973 // collect movable nodes linked to ones on seam in nodesNearSeam
3974 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3975 while ( eIt->more() ) {
3976 const SMDS_MeshElement* e = eIt->next();
3977 int nbUseMap1 = 0, nbUseMap2 = 0;
3978 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3979 int nn = 0, nbn = e->NbNodes();
3980 if(e->IsQuadratic()) nbn = nbn/2;
3981 while ( nn++ < nbn )
3983 const SMDS_MeshNode* n =
3984 static_cast<const SMDS_MeshNode*>( nIt->next() );
3986 setMovableNodes.find( n ) == setMovableNodes.end() )
3988 // add only nodes being closer to uv2 than to uv1
3989 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3990 0.5 * ( n->Y() + nSeam->Y() ),
3991 0.5 * ( n->Z() + nSeam->Z() ));
3993 getClosestUV( projector, pMid, uv );
3994 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3995 nodesNearSeam.insert( n );
4001 // for centroidalSmooth all element nodes must
4002 // be on one side of a seam
4003 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4004 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4006 while ( nn++ < nbn ) {
4007 const SMDS_MeshNode* n =
4008 static_cast<const SMDS_MeshNode*>( nIt->next() );
4009 setMovableNodes.erase( n );
4013 } // loop on nodes on seam
4014 } // loop on edge of a face
4015 } // if ( !face.IsNull() )
4017 if ( setMovableNodes.empty() ) {
4018 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4019 continue; // goto next face
4027 double maxRatio = -1., maxDisplacement = -1.;
4028 set<const SMDS_MeshNode*>::iterator nodeToMove;
4029 for ( it = 0; it < theNbIterations; it++ ) {
4030 maxDisplacement = 0.;
4031 nodeToMove = setMovableNodes.begin();
4032 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4033 const SMDS_MeshNode* node = (*nodeToMove);
4034 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4037 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4038 if ( theSmoothMethod == LAPLACIAN )
4039 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4041 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4043 // node displacement
4044 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4045 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4046 if ( aDispl > maxDisplacement )
4047 maxDisplacement = aDispl;
4049 // no node movement => exit
4050 //if ( maxDisplacement < 1.e-16 ) {
4051 if ( maxDisplacement < disttol ) {
4052 MESSAGE("-- no node movement --");
4056 // check elements quality
4058 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4059 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4060 const SMDS_MeshElement* elem = (*elemIt);
4061 if ( !elem || elem->GetType() != SMDSAbs_Face )
4063 SMESH::Controls::TSequenceOfXYZ aPoints;
4064 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4065 double aValue = aQualityFunc.GetValue( aPoints );
4066 if ( aValue > maxRatio )
4070 if ( maxRatio <= theTgtAspectRatio ) {
4071 MESSAGE("-- quality achived --");
4074 if (it+1 == theNbIterations) {
4075 MESSAGE("-- Iteration limit exceeded --");
4077 } // smoothing iterations
4079 MESSAGE(" Face id: " << *fId <<
4080 " Nb iterstions: " << it <<
4081 " Displacement: " << maxDisplacement <<
4082 " Aspect Ratio " << maxRatio);
4084 // ---------------------------------------
4085 // new nodes positions are computed,
4086 // record movement in DS and set new UV
4087 // ---------------------------------------
4088 nodeToMove = setMovableNodes.begin();
4089 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4090 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4091 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4092 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4093 if ( node_uv != uvMap.end() ) {
4094 gp_XY* uv = node_uv->second;
4096 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4100 // move medium nodes of quadratic elements
4103 SMESH_MesherHelper helper( *GetMesh() );
4104 helper.SetSubShape( face );
4105 vector<const SMDS_MeshNode*> nodes;
4107 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4108 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4110 const SMDS_MeshElement* QF = *elemIt;
4111 if ( QF->IsQuadratic() )
4113 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4114 SMDS_MeshElement::iterator() );
4115 nodes.push_back( nodes[0] );
4117 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4119 if ( !surface.IsNull() )
4121 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4122 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4123 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4124 xyz = surface->Value( uv.X(), uv.Y() );
4127 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4129 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4130 // we have to move a medium node
4131 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4137 } // loop on face ids
4141 //=======================================================================
4142 //function : isReverse
4143 //purpose : Return true if normal of prevNodes is not co-directied with
4144 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4145 // iNotSame is where prevNodes and nextNodes are different.
4146 // If result is true then future volume orientation is OK
4147 //=======================================================================
4149 static bool isReverse(const SMDS_MeshElement* face,
4150 const vector<const SMDS_MeshNode*>& prevNodes,
4151 const vector<const SMDS_MeshNode*>& nextNodes,
4155 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4156 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4157 gp_XYZ extrDir( pN - pP ), faceNorm;
4158 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4160 return faceNorm * extrDir < 0.0;
4163 //=======================================================================
4165 * \brief Create elements by sweeping an element
4166 * \param elem - element to sweep
4167 * \param newNodesItVec - nodes generated from each node of the element
4168 * \param newElems - generated elements
4169 * \param nbSteps - number of sweeping steps
4170 * \param srcElements - to append elem for each generated element
4172 //=======================================================================
4174 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4175 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4176 list<const SMDS_MeshElement*>& newElems,
4178 SMESH_SequenceOfElemPtr& srcElements)
4180 //MESSAGE("sweepElement " << nbSteps);
4181 SMESHDS_Mesh* aMesh = GetMeshDS();
4183 const int nbNodes = elem->NbNodes();
4184 const int nbCorners = elem->NbCornerNodes();
4185 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4186 polyhedron creation !!! */
4187 // Loop on elem nodes:
4188 // find new nodes and detect same nodes indices
4189 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4190 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4191 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4192 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4194 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4195 vector<int> sames(nbNodes);
4196 vector<bool> isSingleNode(nbNodes);
4198 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4199 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4200 const SMDS_MeshNode* node = nnIt->first;
4201 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4202 if ( listNewNodes.empty() )
4205 itNN [ iNode ] = listNewNodes.begin();
4206 prevNod[ iNode ] = node;
4207 nextNod[ iNode ] = listNewNodes.front();
4209 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4210 corner node of linear */
4211 if ( prevNod[ iNode ] != nextNod [ iNode ])
4212 nbDouble += !isSingleNode[iNode];
4214 if( iNode < nbCorners ) { // check corners only
4215 if ( prevNod[ iNode ] == nextNod [ iNode ])
4216 sames[nbSame++] = iNode;
4218 iNotSameNode = iNode;
4222 if ( nbSame == nbNodes || nbSame > 2) {
4223 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4227 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4229 // fix nodes order to have bottom normal external
4230 if ( baseType == SMDSEntity_Polygon )
4232 std::reverse( itNN.begin(), itNN.end() );
4233 std::reverse( prevNod.begin(), prevNod.end() );
4234 std::reverse( midlNod.begin(), midlNod.end() );
4235 std::reverse( nextNod.begin(), nextNod.end() );
4236 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4240 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4241 SMDS_MeshCell::applyInterlace( ind, itNN );
4242 SMDS_MeshCell::applyInterlace( ind, prevNod );
4243 SMDS_MeshCell::applyInterlace( ind, nextNod );
4244 SMDS_MeshCell::applyInterlace( ind, midlNod );
4245 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4248 sames[nbSame] = iNotSameNode;
4249 for ( int j = 0; j <= nbSame; ++j )
4250 for ( size_t i = 0; i < ind.size(); ++i )
4251 if ( ind[i] == sames[j] )
4256 iNotSameNode = sames[nbSame];
4261 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4263 iSameNode = sames[ nbSame-1 ];
4264 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4265 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4266 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4269 // make new elements
4270 for (int iStep = 0; iStep < nbSteps; iStep++ )
4273 for ( iNode = 0; iNode < nbNodes; iNode++ )
4275 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4276 nextNod[ iNode ] = *itNN[ iNode ]++;
4279 SMDS_MeshElement* aNewElem = 0;
4280 /*if(!elem->IsPoly())*/ {
4281 switch ( baseType ) {
4283 case SMDSEntity_Node: { // sweep NODE
4284 if ( nbSame == 0 ) {
4285 if ( isSingleNode[0] )
4286 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4288 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4294 case SMDSEntity_Edge: { // sweep EDGE
4295 if ( nbDouble == 0 )
4297 if ( nbSame == 0 ) // ---> quadrangle
4298 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4299 nextNod[ 1 ], nextNod[ 0 ] );
4300 else // ---> triangle
4301 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4302 nextNod[ iNotSameNode ] );
4304 else // ---> polygon
4306 vector<const SMDS_MeshNode*> poly_nodes;
4307 poly_nodes.push_back( prevNod[0] );
4308 poly_nodes.push_back( prevNod[1] );
4309 if ( prevNod[1] != nextNod[1] )
4311 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4312 poly_nodes.push_back( nextNod[1] );
4314 if ( prevNod[0] != nextNod[0] )
4316 poly_nodes.push_back( nextNod[0] );
4317 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4319 switch ( poly_nodes.size() ) {
4321 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4324 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4325 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4328 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4333 case SMDSEntity_Triangle: // TRIANGLE --->
4335 if ( nbDouble > 0 ) break;
4336 if ( nbSame == 0 ) // ---> pentahedron
4337 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4338 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4340 else if ( nbSame == 1 ) // ---> pyramid
4341 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4342 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4343 nextNod[ iSameNode ]);
4345 else // 2 same nodes: ---> tetrahedron
4346 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4347 nextNod[ iNotSameNode ]);
4350 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4354 if ( nbDouble+nbSame == 2 )
4356 if(nbSame==0) { // ---> quadratic quadrangle
4357 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4358 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4360 else { //(nbSame==1) // ---> quadratic triangle
4362 return; // medium node on axis
4364 else if(sames[0]==0)
4365 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4366 nextNod[2], midlNod[1], prevNod[2]);
4368 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4369 midlNod[0], nextNod[2], prevNod[2]);
4372 else if ( nbDouble == 3 )
4374 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4375 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4376 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4383 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4384 if ( nbDouble > 0 ) break;
4386 if ( nbSame == 0 ) // ---> hexahedron
4387 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4388 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4390 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4391 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4392 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4393 nextNod[ iSameNode ]);
4394 newElems.push_back( aNewElem );
4395 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4396 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4397 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4399 else if ( nbSame == 2 ) { // ---> pentahedron
4400 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4401 // iBeforeSame is same too
4402 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4403 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4404 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4406 // iAfterSame is same too
4407 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4408 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4409 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4413 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4414 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4415 if ( nbDouble+nbSame != 3 ) break;
4417 // ---> pentahedron with 15 nodes
4418 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4419 nextNod[0], nextNod[1], nextNod[2],
4420 prevNod[3], prevNod[4], prevNod[5],
4421 nextNod[3], nextNod[4], nextNod[5],
4422 midlNod[0], midlNod[1], midlNod[2]);
4424 else if(nbSame==1) {
4425 // ---> 2d order pyramid of 13 nodes
4426 int apex = iSameNode;
4427 int i0 = ( apex + 1 ) % nbCorners;
4428 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4432 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4433 nextNod[i0], nextNod[i1], prevNod[apex],
4434 prevNod[i01], midlNod[i0],
4435 nextNod[i01], midlNod[i1],
4436 prevNod[i1a], prevNod[i0a],
4437 nextNod[i0a], nextNod[i1a]);
4439 else if(nbSame==2) {
4440 // ---> 2d order tetrahedron of 10 nodes
4441 int n1 = iNotSameNode;
4442 int n2 = ( n1 + 1 ) % nbCorners;
4443 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4447 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4448 prevNod[n12], prevNod[n23], prevNod[n31],
4449 midlNod[n1], nextNod[n12], nextNod[n31]);
4453 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4455 if ( nbDouble != 4 ) break;
4456 // ---> hexahedron with 20 nodes
4457 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4458 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4459 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4460 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4461 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4463 else if(nbSame==1) {
4464 // ---> pyramid + pentahedron - can not be created since it is needed
4465 // additional middle node at the center of face
4466 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4469 else if( nbSame == 2 ) {
4470 if ( nbDouble != 2 ) break;
4471 // ---> 2d order Pentahedron with 15 nodes
4473 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4474 // iBeforeSame is same too
4481 // iAfterSame is same too
4491 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4492 prevNod[n4], prevNod[n5], nextNod[n5],
4493 prevNod[n12], midlNod[n2], nextNod[n12],
4494 prevNod[n45], midlNod[n5], nextNod[n45],
4495 prevNod[n14], prevNod[n25], nextNod[n25]);
4499 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4501 if( nbSame == 0 && nbDouble == 9 ) {
4502 // ---> tri-quadratic hexahedron with 27 nodes
4503 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4504 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4505 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4506 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4507 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4508 prevNod[8], // bottom center
4509 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4510 nextNod[8], // top center
4511 midlNod[8]);// elem center
4519 case SMDSEntity_Polygon: { // sweep POLYGON
4521 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4522 // ---> hexagonal prism
4523 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4524 prevNod[3], prevNod[4], prevNod[5],
4525 nextNod[0], nextNod[1], nextNod[2],
4526 nextNod[3], nextNod[4], nextNod[5]);
4530 case SMDSEntity_Ball:
4538 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4540 if ( baseType != SMDSEntity_Polygon )
4542 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4543 SMDS_MeshCell::applyInterlace( ind, prevNod );
4544 SMDS_MeshCell::applyInterlace( ind, nextNod );
4545 SMDS_MeshCell::applyInterlace( ind, midlNod );
4546 SMDS_MeshCell::applyInterlace( ind, itNN );
4547 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4548 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4550 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4551 vector<int> quantities (nbNodes + 2);
4552 polyedre_nodes.clear();
4556 for (int inode = 0; inode < nbNodes; inode++)
4557 polyedre_nodes.push_back( prevNod[inode] );
4558 quantities.push_back( nbNodes );
4561 polyedre_nodes.push_back( nextNod[0] );
4562 for (int inode = nbNodes; inode-1; --inode )
4563 polyedre_nodes.push_back( nextNod[inode-1] );
4564 quantities.push_back( nbNodes );
4567 for (int iface = 0; iface < nbNodes; iface++)
4569 const int prevNbNodes = polyedre_nodes.size();
4570 int inextface = (iface+1) % nbNodes;
4571 polyedre_nodes.push_back( prevNod[inextface] );
4572 polyedre_nodes.push_back( prevNod[iface] );
4573 if ( prevNod[iface] != nextNod[iface] )
4575 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4576 polyedre_nodes.push_back( nextNod[iface] );
4578 if ( prevNod[inextface] != nextNod[inextface] )
4580 polyedre_nodes.push_back( nextNod[inextface] );
4581 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4583 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4584 if ( nbFaceNodes > 2 )
4585 quantities.push_back( nbFaceNodes );
4586 else // degenerated face
4587 polyedre_nodes.resize( prevNbNodes );
4589 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4593 newElems.push_back( aNewElem );
4594 myLastCreatedElems.Append(aNewElem);
4595 srcElements.Append( elem );
4598 // set new prev nodes
4599 for ( iNode = 0; iNode < nbNodes; iNode++ )
4600 prevNod[ iNode ] = nextNod[ iNode ];
4605 //=======================================================================
4607 * \brief Create 1D and 2D elements around swept elements
4608 * \param mapNewNodes - source nodes and ones generated from them
4609 * \param newElemsMap - source elements and ones generated from them
4610 * \param elemNewNodesMap - nodes generated from each node of each element
4611 * \param elemSet - all swept elements
4612 * \param nbSteps - number of sweeping steps
4613 * \param srcElements - to append elem for each generated element
4615 //=======================================================================
4617 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4618 TTElemOfElemListMap & newElemsMap,
4619 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4620 TIDSortedElemSet& elemSet,
4622 SMESH_SequenceOfElemPtr& srcElements)
4624 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4625 SMESHDS_Mesh* aMesh = GetMeshDS();
4627 // Find nodes belonging to only one initial element - sweep them into edges.
4629 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4630 for ( ; nList != mapNewNodes.end(); nList++ )
4632 const SMDS_MeshNode* node =
4633 static_cast<const SMDS_MeshNode*>( nList->first );
4634 if ( newElemsMap.count( node ))
4635 continue; // node was extruded into edge
4636 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4637 int nbInitElems = 0;
4638 const SMDS_MeshElement* el = 0;
4639 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4640 while ( eIt->more() && nbInitElems < 2 ) {
4642 SMDSAbs_ElementType type = el->GetType();
4643 if ( type == SMDSAbs_Volume || type < highType ) continue;
4644 if ( type > highType ) {
4648 nbInitElems += elemSet.count(el);
4650 if ( nbInitElems < 2 ) {
4651 bool NotCreateEdge = el && el->IsMediumNode(node);
4652 if(!NotCreateEdge) {
4653 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4654 list<const SMDS_MeshElement*> newEdges;
4655 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4660 // Make a ceiling for each element ie an equal element of last new nodes.
4661 // Find free links of faces - make edges and sweep them into faces.
4663 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4664 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4665 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4667 const SMDS_MeshElement* elem = itElem->first;
4668 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4670 if(itElem->second.size()==0) continue;
4672 const bool isQuadratic = elem->IsQuadratic();
4674 if ( elem->GetType() == SMDSAbs_Edge ) {
4675 // create a ceiling edge
4676 if ( !isQuadratic ) {
4677 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4678 vecNewNodes[ 1 ]->second.back())) {
4679 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4680 vecNewNodes[ 1 ]->second.back()));
4681 srcElements.Append( elem );
4685 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4686 vecNewNodes[ 1 ]->second.back(),
4687 vecNewNodes[ 2 ]->second.back())) {
4688 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4689 vecNewNodes[ 1 ]->second.back(),
4690 vecNewNodes[ 2 ]->second.back()));
4691 srcElements.Append( elem );
4695 if ( elem->GetType() != SMDSAbs_Face )
4698 bool hasFreeLinks = false;
4700 TIDSortedElemSet avoidSet;
4701 avoidSet.insert( elem );
4703 set<const SMDS_MeshNode*> aFaceLastNodes;
4704 int iNode, nbNodes = vecNewNodes.size();
4705 if ( !isQuadratic ) {
4706 // loop on the face nodes
4707 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4708 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4709 // look for free links of the face
4710 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4711 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4712 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4713 // check if a link n1-n2 is free
4714 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4715 hasFreeLinks = true;
4716 // make a new edge and a ceiling for a new edge
4717 const SMDS_MeshElement* edge;
4718 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4719 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4720 srcElements.Append( myLastCreatedElems.Last() );
4722 n1 = vecNewNodes[ iNode ]->second.back();
4723 n2 = vecNewNodes[ iNext ]->second.back();
4724 if ( !aMesh->FindEdge( n1, n2 )) {
4725 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4726 srcElements.Append( edge );
4731 else { // elem is quadratic face
4732 int nbn = nbNodes/2;
4733 for ( iNode = 0; iNode < nbn; iNode++ ) {
4734 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4735 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4736 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4737 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4738 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4739 // check if a link is free
4740 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4741 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4742 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4743 hasFreeLinks = true;
4744 // make an edge and a ceiling for a new edge
4746 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4747 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4748 srcElements.Append( elem );
4750 n1 = vecNewNodes[ iNode ]->second.back();
4751 n2 = vecNewNodes[ iNext ]->second.back();
4752 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4753 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4754 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4755 srcElements.Append( elem );
4759 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4760 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4764 // sweep free links into faces
4766 if ( hasFreeLinks ) {
4767 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4768 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4770 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4771 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4772 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4773 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4774 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4776 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4777 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4778 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4780 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4781 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4782 std::advance( v, volNb );
4783 // find indices of free faces of a volume and their source edges
4784 list< int > freeInd;
4785 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4786 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4787 int iF, nbF = vTool.NbFaces();
4788 for ( iF = 0; iF < nbF; iF ++ ) {
4789 if (vTool.IsFreeFace( iF ) &&
4790 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4791 initNodeSet != faceNodeSet) // except an initial face
4793 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4795 if ( faceNodeSet == initNodeSetNoCenter )
4797 freeInd.push_back( iF );
4798 // find source edge of a free face iF
4799 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4800 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4801 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4802 initNodeSet.begin(), initNodeSet.end(),
4803 commonNodes.begin());
4804 if ( (*v)->IsQuadratic() )
4805 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4807 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4809 if ( !srcEdges.back() )
4811 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4812 << iF << " of volume #" << vTool.ID() << endl;
4817 if ( freeInd.empty() )
4820 // create faces for all steps;
4821 // if such a face has been already created by sweep of edge,
4822 // assure that its orientation is OK
4823 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4824 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4825 vTool.SetExternalNormal();
4826 const int nextShift = vTool.IsForward() ? +1 : -1;
4827 list< int >::iterator ind = freeInd.begin();
4828 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4829 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4831 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4832 int nbn = vTool.NbFaceNodes( *ind );
4833 const SMDS_MeshElement * f = 0;
4834 if ( nbn == 3 ) ///// triangle
4836 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4838 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4840 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4842 nodes[ 1 + nextShift ] };
4844 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4846 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4850 else if ( nbn == 4 ) ///// quadrangle
4852 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4854 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4856 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4857 nodes[ 2 ], nodes[ 2+nextShift ] };
4859 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4861 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4862 newOrder[ 2 ], newOrder[ 3 ]));
4865 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4867 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4869 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4871 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4873 nodes[2 + 2*nextShift],
4874 nodes[3 - 2*nextShift],
4876 nodes[3 + 2*nextShift]};
4878 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4880 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4888 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4890 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4891 nodes[1], nodes[3], nodes[5], nodes[7] );
4893 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4895 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4896 nodes[4 - 2*nextShift],
4898 nodes[4 + 2*nextShift],
4900 nodes[5 - 2*nextShift],
4902 nodes[5 + 2*nextShift] };
4904 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4906 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4907 newOrder[ 2 ], newOrder[ 3 ],
4908 newOrder[ 4 ], newOrder[ 5 ],
4909 newOrder[ 6 ], newOrder[ 7 ]));
4912 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4914 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4915 SMDSAbs_Face, /*noMedium=*/false);
4917 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4919 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4920 nodes[4 - 2*nextShift],
4922 nodes[4 + 2*nextShift],
4924 nodes[5 - 2*nextShift],
4926 nodes[5 + 2*nextShift],
4929 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4931 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4932 newOrder[ 2 ], newOrder[ 3 ],
4933 newOrder[ 4 ], newOrder[ 5 ],
4934 newOrder[ 6 ], newOrder[ 7 ],
4938 else //////// polygon
4940 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4941 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4943 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4945 if ( !vTool.IsForward() )
4946 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4948 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4950 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4954 while ( srcElements.Length() < myLastCreatedElems.Length() )
4955 srcElements.Append( *srcEdge );
4957 } // loop on free faces
4959 // go to the next volume
4961 while ( iVol++ < nbVolumesByStep ) v++;
4964 } // loop on volumes of one step
4965 } // sweep free links into faces
4967 // Make a ceiling face with a normal external to a volume
4969 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4970 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4971 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4973 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4974 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4975 iF = lastVol.GetFaceIndex( aFaceLastNodes );
4978 lastVol.SetExternalNormal();
4979 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4980 int nbn = lastVol.NbFaceNodes( iF );
4981 // we do not use this->AddElement() because nodes are interlaced
4982 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4983 if ( !hasFreeLinks ||
4984 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4987 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4989 else if ( nbn == 4 )
4990 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4992 else if ( nbn == 6 && isQuadratic )
4993 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4994 nodes[1], nodes[3], nodes[5]));
4995 else if ( nbn == 7 && isQuadratic )
4996 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4997 nodes[1], nodes[3], nodes[5], nodes[6]));
4998 else if ( nbn == 8 && isQuadratic )
4999 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5000 nodes[1], nodes[3], nodes[5], nodes[7]));
5001 else if ( nbn == 9 && isQuadratic )
5002 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5003 nodes[1], nodes[3], nodes[5], nodes[7],
5006 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5008 while ( srcElements.Length() < myLastCreatedElems.Length() )
5009 srcElements.Append( elem );
5012 } // loop on swept elements
5015 //=======================================================================
5016 //function : RotationSweep
5018 //=======================================================================
5020 SMESH_MeshEditor::PGroupIDs
5021 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5022 const gp_Ax1& theAxis,
5023 const double theAngle,
5024 const int theNbSteps,
5025 const double theTol,
5026 const bool theMakeGroups,
5027 const bool theMakeWalls)
5029 myLastCreatedElems.Clear();
5030 myLastCreatedNodes.Clear();
5032 // source elements for each generated one
5033 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5035 MESSAGE( "RotationSweep()");
5037 aTrsf.SetRotation( theAxis, theAngle );
5039 aTrsf2.SetRotation( theAxis, theAngle/2. );
5041 gp_Lin aLine( theAxis );
5042 double aSqTol = theTol * theTol;
5044 SMESHDS_Mesh* aMesh = GetMeshDS();
5046 TNodeOfNodeListMap mapNewNodes;
5047 TElemOfVecOfNnlmiMap mapElemNewNodes;
5048 TTElemOfElemListMap newElemsMap;
5050 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5051 myMesh->NbFaces(ORDER_QUADRATIC) +
5052 myMesh->NbVolumes(ORDER_QUADRATIC) );
5054 TIDSortedElemSet::iterator itElem;
5055 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5056 const SMDS_MeshElement* elem = *itElem;
5057 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5059 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5060 newNodesItVec.reserve( elem->NbNodes() );
5062 // loop on elem nodes
5063 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5064 while ( itN->more() )
5066 // check if a node has been already sweeped
5067 const SMDS_MeshNode* node = cast2Node( itN->next() );
5069 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5071 aXYZ.Coord( coord[0], coord[1], coord[2] );
5072 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5074 TNodeOfNodeListMapItr nIt =
5075 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5076 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5077 if ( listNewNodes.empty() )
5079 // check if we are to create medium nodes between corner ones
5080 bool needMediumNodes = false;
5081 if ( isQuadraticMesh )
5083 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5084 while (it->more() && !needMediumNodes )
5086 const SMDS_MeshElement* invElem = it->next();
5087 if ( invElem != elem && !theElems.count( invElem )) continue;
5088 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5089 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5090 needMediumNodes = true;
5095 const SMDS_MeshNode * newNode = node;
5096 for ( int i = 0; i < theNbSteps; i++ ) {
5098 if ( needMediumNodes ) // create a medium node
5100 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5101 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5102 myLastCreatedNodes.Append(newNode);
5103 srcNodes.Append( node );
5104 listNewNodes.push_back( newNode );
5105 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5108 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5110 // create a corner node
5111 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5112 myLastCreatedNodes.Append(newNode);
5113 srcNodes.Append( node );
5114 listNewNodes.push_back( newNode );
5117 listNewNodes.push_back( newNode );
5118 // if ( needMediumNodes )
5119 // listNewNodes.push_back( newNode );
5123 newNodesItVec.push_back( nIt );
5125 // make new elements
5126 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5130 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5132 PGroupIDs newGroupIDs;
5133 if ( theMakeGroups )
5134 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5140 //=======================================================================
5141 //function : CreateNode
5143 //=======================================================================
5144 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5147 const double tolnode,
5148 SMESH_SequenceOfNode& aNodes)
5150 // myLastCreatedElems.Clear();
5151 // myLastCreatedNodes.Clear();
5154 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5156 // try to search in sequence of existing nodes
5157 // if aNodes.Length()>0 we 'nave to use given sequence
5158 // else - use all nodes of mesh
5159 if(aNodes.Length()>0) {
5161 for(i=1; i<=aNodes.Length(); i++) {
5162 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5163 if(P1.Distance(P2)<tolnode)
5164 return aNodes.Value(i);
5168 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5169 while(itn->more()) {
5170 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5171 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5172 if(P1.Distance(P2)<tolnode)
5177 // create new node and return it
5178 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5179 //myLastCreatedNodes.Append(NewNode);
5184 //=======================================================================
5185 //function : ExtrusionSweep
5187 //=======================================================================
5189 SMESH_MeshEditor::PGroupIDs
5190 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5191 const gp_Vec& theStep,
5192 const int theNbSteps,
5193 TTElemOfElemListMap& newElemsMap,
5194 const bool theMakeGroups,
5196 const double theTolerance)
5198 ExtrusParam aParams;
5199 aParams.myDir = gp_Dir(theStep);
5200 aParams.myNodes.Clear();
5201 aParams.mySteps = new TColStd_HSequenceOfReal;
5203 for(i=1; i<=theNbSteps; i++)
5204 aParams.mySteps->Append(theStep.Magnitude());
5207 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5211 //=======================================================================
5212 //function : ExtrusionSweep
5214 //=======================================================================
5216 SMESH_MeshEditor::PGroupIDs
5217 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5218 ExtrusParam& theParams,
5219 TTElemOfElemListMap& newElemsMap,
5220 const bool theMakeGroups,
5222 const double theTolerance)
5224 myLastCreatedElems.Clear();
5225 myLastCreatedNodes.Clear();
5227 // source elements for each generated one
5228 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5230 SMESHDS_Mesh* aMesh = GetMeshDS();
5232 int nbsteps = theParams.mySteps->Length();
5234 TNodeOfNodeListMap mapNewNodes;
5235 //TNodeOfNodeVecMap mapNewNodes;
5236 TElemOfVecOfNnlmiMap mapElemNewNodes;
5237 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5239 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5240 myMesh->NbFaces(ORDER_QUADRATIC) +
5241 myMesh->NbVolumes(ORDER_QUADRATIC) );
5243 TIDSortedElemSet::iterator itElem;
5244 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5245 // check element type
5246 const SMDS_MeshElement* elem = *itElem;
5247 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5250 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5251 newNodesItVec.reserve( elem->NbNodes() );
5253 // loop on elem nodes
5254 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5255 while ( itN->more() )
5257 // check if a node has been already sweeped
5258 const SMDS_MeshNode* node = cast2Node( itN->next() );
5259 TNodeOfNodeListMap::iterator nIt =
5260 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5261 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5262 if ( listNewNodes.empty() )
5266 // check if we are to create medium nodes between corner ones
5267 bool needMediumNodes = false;
5268 if ( isQuadraticMesh )
5270 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5271 while (it->more() && !needMediumNodes )
5273 const SMDS_MeshElement* invElem = it->next();
5274 if ( invElem != elem && !theElems.count( invElem )) continue;
5275 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5276 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5277 needMediumNodes = true;
5281 double coord[] = { node->X(), node->Y(), node->Z() };
5282 for ( int i = 0; i < nbsteps; i++ )
5284 if ( needMediumNodes ) // create a medium node
5286 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5287 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5288 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5289 if( theFlags & EXTRUSION_FLAG_SEW ) {
5290 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5291 theTolerance, theParams.myNodes);
5292 listNewNodes.push_back( newNode );
5295 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5296 myLastCreatedNodes.Append(newNode);
5297 srcNodes.Append( node );
5298 listNewNodes.push_back( newNode );
5301 // create a corner node
5302 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5303 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5304 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5305 if( theFlags & EXTRUSION_FLAG_SEW ) {
5306 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5307 theTolerance, theParams.myNodes);
5308 listNewNodes.push_back( newNode );
5311 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5312 myLastCreatedNodes.Append(newNode);
5313 srcNodes.Append( node );
5314 listNewNodes.push_back( newNode );
5318 newNodesItVec.push_back( nIt );
5320 // make new elements
5321 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5324 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5325 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5327 PGroupIDs newGroupIDs;
5328 if ( theMakeGroups )
5329 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5334 //=======================================================================
5335 //function : ExtrusionAlongTrack
5337 //=======================================================================
5338 SMESH_MeshEditor::Extrusion_Error
5339 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5340 SMESH_subMesh* theTrack,
5341 const SMDS_MeshNode* theN1,
5342 const bool theHasAngles,
5343 list<double>& theAngles,
5344 const bool theLinearVariation,
5345 const bool theHasRefPoint,
5346 const gp_Pnt& theRefPoint,
5347 const bool theMakeGroups)
5349 MESSAGE("ExtrusionAlongTrack");
5350 myLastCreatedElems.Clear();
5351 myLastCreatedNodes.Clear();
5354 std::list<double> aPrms;
5355 TIDSortedElemSet::iterator itElem;
5358 TopoDS_Edge aTrackEdge;
5359 TopoDS_Vertex aV1, aV2;
5361 SMDS_ElemIteratorPtr aItE;
5362 SMDS_NodeIteratorPtr aItN;
5363 SMDSAbs_ElementType aTypeE;
5365 TNodeOfNodeListMap mapNewNodes;
5368 aNbE = theElements.size();
5371 return EXTR_NO_ELEMENTS;
5373 // 1.1 Track Pattern
5376 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5378 aItE = pSubMeshDS->GetElements();
5379 while ( aItE->more() ) {
5380 const SMDS_MeshElement* pE = aItE->next();
5381 aTypeE = pE->GetType();
5382 // Pattern must contain links only
5383 if ( aTypeE != SMDSAbs_Edge )
5384 return EXTR_PATH_NOT_EDGE;
5387 list<SMESH_MeshEditor_PathPoint> fullList;
5389 const TopoDS_Shape& aS = theTrack->GetSubShape();
5390 // Sub-shape for the Pattern must be an Edge or Wire
5391 if( aS.ShapeType() == TopAbs_EDGE ) {
5392 aTrackEdge = TopoDS::Edge( aS );
5393 // the Edge must not be degenerated
5394 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5395 return EXTR_BAD_PATH_SHAPE;
5396 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5397 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5398 const SMDS_MeshNode* aN1 = aItN->next();
5399 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5400 const SMDS_MeshNode* aN2 = aItN->next();
5401 // starting node must be aN1 or aN2
5402 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5403 return EXTR_BAD_STARTING_NODE;
5404 aItN = pSubMeshDS->GetNodes();
5405 while ( aItN->more() ) {
5406 const SMDS_MeshNode* pNode = aItN->next();
5407 const SMDS_EdgePosition* pEPos =
5408 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5409 double aT = pEPos->GetUParameter();
5410 aPrms.push_back( aT );
5412 //Extrusion_Error err =
5413 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5414 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5415 list< SMESH_subMesh* > LSM;
5416 TopTools_SequenceOfShape Edges;
5417 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5418 while(itSM->more()) {
5419 SMESH_subMesh* SM = itSM->next();
5421 const TopoDS_Shape& aS = SM->GetSubShape();
5424 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5425 int startNid = theN1->GetID();
5426 TColStd_MapOfInteger UsedNums;
5428 int NbEdges = Edges.Length();
5430 for(; i<=NbEdges; i++) {
5432 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5433 for(; itLSM!=LSM.end(); itLSM++) {
5435 if(UsedNums.Contains(k)) continue;
5436 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5437 SMESH_subMesh* locTrack = *itLSM;
5438 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5439 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5440 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5441 const SMDS_MeshNode* aN1 = aItN->next();
5442 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5443 const SMDS_MeshNode* aN2 = aItN->next();
5444 // starting node must be aN1 or aN2
5445 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5446 // 2. Collect parameters on the track edge
5448 aItN = locMeshDS->GetNodes();
5449 while ( aItN->more() ) {
5450 const SMDS_MeshNode* pNode = aItN->next();
5451 const SMDS_EdgePosition* pEPos =
5452 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5453 double aT = pEPos->GetUParameter();
5454 aPrms.push_back( aT );
5456 list<SMESH_MeshEditor_PathPoint> LPP;
5457 //Extrusion_Error err =
5458 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5459 LLPPs.push_back(LPP);
5461 // update startN for search following egde
5462 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5463 else startNid = aN1->GetID();
5467 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5468 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5469 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5470 for(; itPP!=firstList.end(); itPP++) {
5471 fullList.push_back( *itPP );
5473 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5474 fullList.pop_back();
5476 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5477 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5478 itPP = currList.begin();
5479 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5480 gp_Dir D1 = PP1.Tangent();
5481 gp_Dir D2 = PP2.Tangent();
5482 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5483 (D1.Z()+D2.Z())/2 ) );
5484 PP1.SetTangent(Dnew);
5485 fullList.push_back(PP1);
5487 for(; itPP!=firstList.end(); itPP++) {
5488 fullList.push_back( *itPP );
5490 PP1 = fullList.back();
5491 fullList.pop_back();
5493 // if wire not closed
5494 fullList.push_back(PP1);
5498 return EXTR_BAD_PATH_SHAPE;
5501 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5502 theHasRefPoint, theRefPoint, theMakeGroups);
5506 //=======================================================================
5507 //function : ExtrusionAlongTrack
5509 //=======================================================================
5510 SMESH_MeshEditor::Extrusion_Error
5511 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5512 SMESH_Mesh* theTrack,
5513 const SMDS_MeshNode* theN1,
5514 const bool theHasAngles,
5515 list<double>& theAngles,
5516 const bool theLinearVariation,
5517 const bool theHasRefPoint,
5518 const gp_Pnt& theRefPoint,
5519 const bool theMakeGroups)
5521 myLastCreatedElems.Clear();
5522 myLastCreatedNodes.Clear();
5525 std::list<double> aPrms;
5526 TIDSortedElemSet::iterator itElem;
5529 TopoDS_Edge aTrackEdge;
5530 TopoDS_Vertex aV1, aV2;
5532 SMDS_ElemIteratorPtr aItE;
5533 SMDS_NodeIteratorPtr aItN;
5534 SMDSAbs_ElementType aTypeE;
5536 TNodeOfNodeListMap mapNewNodes;
5539 aNbE = theElements.size();
5542 return EXTR_NO_ELEMENTS;
5544 // 1.1 Track Pattern
5547 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5549 aItE = pMeshDS->elementsIterator();
5550 while ( aItE->more() ) {
5551 const SMDS_MeshElement* pE = aItE->next();
5552 aTypeE = pE->GetType();
5553 // Pattern must contain links only
5554 if ( aTypeE != SMDSAbs_Edge )
5555 return EXTR_PATH_NOT_EDGE;
5558 list<SMESH_MeshEditor_PathPoint> fullList;
5560 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5562 if ( !theTrack->HasShapeToMesh() ) {
5563 //Mesh without shape
5564 const SMDS_MeshNode* currentNode = NULL;
5565 const SMDS_MeshNode* prevNode = theN1;
5566 std::vector<const SMDS_MeshNode*> aNodesList;
5567 aNodesList.push_back(theN1);
5568 int nbEdges = 0, conn=0;
5569 const SMDS_MeshElement* prevElem = NULL;
5570 const SMDS_MeshElement* currentElem = NULL;
5571 int totalNbEdges = theTrack->NbEdges();
5572 SMDS_ElemIteratorPtr nIt;
5575 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5576 return EXTR_BAD_STARTING_NODE;
5579 conn = nbEdgeConnectivity(theN1);
5581 return EXTR_PATH_NOT_EDGE;
5583 aItE = theN1->GetInverseElementIterator();
5584 prevElem = aItE->next();
5585 currentElem = prevElem;
5587 if(totalNbEdges == 1 ) {
5588 nIt = currentElem->nodesIterator();
5589 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5590 if(currentNode == prevNode)
5591 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592 aNodesList.push_back(currentNode);
5594 nIt = currentElem->nodesIterator();
5595 while( nIt->more() ) {
5596 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5597 if(currentNode == prevNode)
5598 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599 aNodesList.push_back(currentNode);
5601 //case of the closed mesh
5602 if(currentNode == theN1) {
5607 conn = nbEdgeConnectivity(currentNode);
5609 return EXTR_PATH_NOT_EDGE;
5610 }else if( conn == 1 && nbEdges > 0 ) {
5615 prevNode = currentNode;
5616 aItE = currentNode->GetInverseElementIterator();
5617 currentElem = aItE->next();
5618 if( currentElem == prevElem)
5619 currentElem = aItE->next();
5620 nIt = currentElem->nodesIterator();
5621 prevElem = currentElem;
5627 if(nbEdges != totalNbEdges)
5628 return EXTR_PATH_NOT_EDGE;
5630 TopTools_SequenceOfShape Edges;
5631 double x1,x2,y1,y2,z1,z2;
5632 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5633 int startNid = theN1->GetID();
5634 for(int i = 1; i < aNodesList.size(); i++) {
5635 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5636 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5637 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5638 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5639 list<SMESH_MeshEditor_PathPoint> LPP;
5641 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5642 LLPPs.push_back(LPP);
5643 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5644 else startNid = aNodesList[i-1]->GetID();
5648 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5649 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5650 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5651 for(; itPP!=firstList.end(); itPP++) {
5652 fullList.push_back( *itPP );
5655 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5656 SMESH_MeshEditor_PathPoint PP2;
5657 fullList.pop_back();
5659 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5660 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5661 itPP = currList.begin();
5662 PP2 = currList.front();
5663 gp_Dir D1 = PP1.Tangent();
5664 gp_Dir D2 = PP2.Tangent();
5665 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5666 (D1.Z()+D2.Z())/2 ) );
5667 PP1.SetTangent(Dnew);
5668 fullList.push_back(PP1);
5670 for(; itPP!=currList.end(); itPP++) {
5671 fullList.push_back( *itPP );
5673 PP1 = fullList.back();
5674 fullList.pop_back();
5676 fullList.push_back(PP1);
5678 } // Sub-shape for the Pattern must be an Edge or Wire
5679 else if( aS.ShapeType() == TopAbs_EDGE ) {
5680 aTrackEdge = TopoDS::Edge( aS );
5681 // the Edge must not be degenerated
5682 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5683 return EXTR_BAD_PATH_SHAPE;
5684 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5685 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5686 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5687 // starting node must be aN1 or aN2
5688 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5689 return EXTR_BAD_STARTING_NODE;
5690 aItN = pMeshDS->nodesIterator();
5691 while ( aItN->more() ) {
5692 const SMDS_MeshNode* pNode = aItN->next();
5693 if( pNode==aN1 || pNode==aN2 ) continue;
5694 const SMDS_EdgePosition* pEPos =
5695 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5696 double aT = pEPos->GetUParameter();
5697 aPrms.push_back( aT );
5699 //Extrusion_Error err =
5700 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5702 else if( aS.ShapeType() == TopAbs_WIRE ) {
5703 list< SMESH_subMesh* > LSM;
5704 TopTools_SequenceOfShape Edges;
5705 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5706 for(; eExp.More(); eExp.Next()) {
5707 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5708 if( SMESH_Algo::isDegenerated(E) ) continue;
5709 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5715 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5716 TopoDS_Vertex aVprev;
5717 TColStd_MapOfInteger UsedNums;
5718 int NbEdges = Edges.Length();
5720 for(; i<=NbEdges; i++) {
5722 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5723 for(; itLSM!=LSM.end(); itLSM++) {
5725 if(UsedNums.Contains(k)) continue;
5726 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5727 SMESH_subMesh* locTrack = *itLSM;
5728 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5729 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5730 bool aN1isOK = false, aN2isOK = false;
5731 if ( aVprev.IsNull() ) {
5732 // if previous vertex is not yet defined, it means that we in the beginning of wire
5733 // and we have to find initial vertex corresponding to starting node theN1
5734 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5735 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5736 // starting node must be aN1 or aN2
5737 aN1isOK = ( aN1 && aN1 == theN1 );
5738 aN2isOK = ( aN2 && aN2 == theN1 );
5741 // we have specified ending vertex of the previous edge on the previous iteration
5742 // and we have just to check that it corresponds to any vertex in current segment
5743 aN1isOK = aVprev.IsSame( aV1 );
5744 aN2isOK = aVprev.IsSame( aV2 );
5746 if ( !aN1isOK && !aN2isOK ) continue;
5747 // 2. Collect parameters on the track edge
5749 aItN = locMeshDS->GetNodes();
5750 while ( aItN->more() ) {
5751 const SMDS_MeshNode* pNode = aItN->next();
5752 const SMDS_EdgePosition* pEPos =
5753 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5754 double aT = pEPos->GetUParameter();
5755 aPrms.push_back( aT );
5757 list<SMESH_MeshEditor_PathPoint> LPP;
5758 //Extrusion_Error err =
5759 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5760 LLPPs.push_back(LPP);
5762 // update startN for search following egde
5763 if ( aN1isOK ) aVprev = aV2;
5768 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5769 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5770 fullList.splice( fullList.end(), firstList );
5772 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5773 fullList.pop_back();
5775 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5776 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5777 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5778 gp_Dir D1 = PP1.Tangent();
5779 gp_Dir D2 = PP2.Tangent();
5780 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5781 PP1.SetTangent(Dnew);
5782 fullList.push_back(PP1);
5783 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5784 PP1 = fullList.back();
5785 fullList.pop_back();
5787 // if wire not closed
5788 fullList.push_back(PP1);
5792 return EXTR_BAD_PATH_SHAPE;
5795 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5796 theHasRefPoint, theRefPoint, theMakeGroups);
5800 //=======================================================================
5801 //function : MakeEdgePathPoints
5802 //purpose : auxilary for ExtrusionAlongTrack
5803 //=======================================================================
5804 SMESH_MeshEditor::Extrusion_Error
5805 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5806 const TopoDS_Edge& aTrackEdge,
5808 list<SMESH_MeshEditor_PathPoint>& LPP)
5810 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5812 aTolVec2=aTolVec*aTolVec;
5814 TopoDS_Vertex aV1, aV2;
5815 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5816 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5817 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5818 // 2. Collect parameters on the track edge
5819 aPrms.push_front( aT1 );
5820 aPrms.push_back( aT2 );
5823 if( FirstIsStart ) {
5834 SMESH_MeshEditor_PathPoint aPP;
5835 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5836 std::list<double>::iterator aItD = aPrms.begin();
5837 for(; aItD != aPrms.end(); ++aItD) {
5841 aC3D->D1( aT, aP3D, aVec );
5842 aL2 = aVec.SquareMagnitude();
5843 if ( aL2 < aTolVec2 )
5844 return EXTR_CANT_GET_TANGENT;
5845 gp_Dir aTgt( aVec );
5847 aPP.SetTangent( aTgt );
5848 aPP.SetParameter( aT );
5855 //=======================================================================
5856 //function : MakeExtrElements
5857 //purpose : auxilary for ExtrusionAlongTrack
5858 //=======================================================================
5859 SMESH_MeshEditor::Extrusion_Error
5860 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5861 list<SMESH_MeshEditor_PathPoint>& fullList,
5862 const bool theHasAngles,
5863 list<double>& theAngles,
5864 const bool theLinearVariation,
5865 const bool theHasRefPoint,
5866 const gp_Pnt& theRefPoint,
5867 const bool theMakeGroups)
5869 const int aNbTP = fullList.size();
5871 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5872 LinearAngleVariation(aNbTP-1, theAngles);
5873 // fill vector of path points with angles
5874 vector<SMESH_MeshEditor_PathPoint> aPPs;
5875 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5876 list<double>::iterator itAngles = theAngles.begin();
5877 aPPs.push_back( *itPP++ );
5878 for( ; itPP != fullList.end(); itPP++) {
5879 aPPs.push_back( *itPP );
5880 if ( theHasAngles && itAngles != theAngles.end() )
5881 aPPs.back().SetAngle( *itAngles++ );
5884 TNodeOfNodeListMap mapNewNodes;
5885 TElemOfVecOfNnlmiMap mapElemNewNodes;
5886 TTElemOfElemListMap newElemsMap;
5887 TIDSortedElemSet::iterator itElem;
5888 // source elements for each generated one
5889 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5891 // 3. Center of rotation aV0
5892 gp_Pnt aV0 = theRefPoint;
5893 if ( !theHasRefPoint )
5895 gp_XYZ aGC( 0.,0.,0. );
5896 TIDSortedElemSet newNodes;
5898 itElem = theElements.begin();
5899 for ( ; itElem != theElements.end(); itElem++ ) {
5900 const SMDS_MeshElement* elem = *itElem;
5902 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5903 while ( itN->more() ) {
5904 const SMDS_MeshElement* node = itN->next();
5905 if ( newNodes.insert( node ).second )
5906 aGC += SMESH_TNodeXYZ( node );
5909 aGC /= newNodes.size();
5911 } // if (!theHasRefPoint) {
5913 // 4. Processing the elements
5914 SMESHDS_Mesh* aMesh = GetMeshDS();
5916 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5917 // check element type
5918 const SMDS_MeshElement* elem = *itElem;
5919 SMDSAbs_ElementType aTypeE = elem->GetType();
5920 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5923 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5924 newNodesItVec.reserve( elem->NbNodes() );
5926 // loop on elem nodes
5928 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5929 while ( itN->more() )
5932 // check if a node has been already processed
5933 const SMDS_MeshNode* node =
5934 static_cast<const SMDS_MeshNode*>( itN->next() );
5935 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5936 if ( nIt == mapNewNodes.end() ) {
5937 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5938 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5941 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5942 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5943 gp_Ax1 anAx1, anAxT1T0;
5944 gp_Dir aDT1x, aDT0x, aDT1T0;
5949 aPN0 = SMESH_TNodeXYZ( node );
5951 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5953 aDT0x= aPP0.Tangent();
5954 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5956 for ( int j = 1; j < aNbTP; ++j ) {
5957 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5959 aDT1x = aPP1.Tangent();
5960 aAngle1x = aPP1.Angle();
5962 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5964 gp_Vec aV01x( aP0x, aP1x );
5965 aTrsf.SetTranslation( aV01x );
5968 aV1x = aV0x.Transformed( aTrsf );
5969 aPN1 = aPN0.Transformed( aTrsf );
5971 // rotation 1 [ T1,T0 ]
5972 aAngleT1T0=-aDT1x.Angle( aDT0x );
5973 if (fabs(aAngleT1T0) > aTolAng) {
5975 anAxT1T0.SetLocation( aV1x );
5976 anAxT1T0.SetDirection( aDT1T0 );
5977 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5979 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5983 if ( theHasAngles ) {
5984 anAx1.SetLocation( aV1x );
5985 anAx1.SetDirection( aDT1x );
5986 aTrsfRot.SetRotation( anAx1, aAngle1x );
5988 aPN1 = aPN1.Transformed( aTrsfRot );
5992 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5993 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5994 // create additional node
5995 double x = ( aPN1.X() + aPN0.X() )/2.;
5996 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5997 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5998 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5999 myLastCreatedNodes.Append(newNode);
6000 srcNodes.Append( node );
6001 listNewNodes.push_back( newNode );
6003 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6004 myLastCreatedNodes.Append(newNode);
6005 srcNodes.Append( node );
6006 listNewNodes.push_back( newNode );
6016 // if current elem is quadratic and current node is not medium
6017 // we have to check - may be it is needed to insert additional nodes
6018 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6019 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6020 if(listNewNodes.size()==aNbTP-1) {
6021 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6022 gp_XYZ P(node->X(), node->Y(), node->Z());
6023 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6025 for(i=0; i<aNbTP-1; i++) {
6026 const SMDS_MeshNode* N = *it;
6027 double x = ( N->X() + P.X() )/2.;
6028 double y = ( N->Y() + P.Y() )/2.;
6029 double z = ( N->Z() + P.Z() )/2.;
6030 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6031 srcNodes.Append( node );
6032 myLastCreatedNodes.Append(newN);
6035 P = gp_XYZ(N->X(),N->Y(),N->Z());
6037 listNewNodes.clear();
6038 for(i=0; i<2*(aNbTP-1); i++) {
6039 listNewNodes.push_back(aNodes[i]);
6045 newNodesItVec.push_back( nIt );
6047 // make new elements
6048 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6049 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6050 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6053 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6055 if ( theMakeGroups )
6056 generateGroups( srcNodes, srcElems, "extruded");
6062 //=======================================================================
6063 //function : LinearAngleVariation
6064 //purpose : auxilary for ExtrusionAlongTrack
6065 //=======================================================================
6066 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6067 list<double>& Angles)
6069 int nbAngles = Angles.size();
6070 if( nbSteps > nbAngles ) {
6071 vector<double> theAngles(nbAngles);
6072 list<double>::iterator it = Angles.begin();
6074 for(; it!=Angles.end(); it++) {
6076 theAngles[i] = (*it);
6079 double rAn2St = double( nbAngles ) / double( nbSteps );
6080 double angPrev = 0, angle;
6081 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6082 double angCur = rAn2St * ( iSt+1 );
6083 double angCurFloor = floor( angCur );
6084 double angPrevFloor = floor( angPrev );
6085 if ( angPrevFloor == angCurFloor )
6086 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6088 int iP = int( angPrevFloor );
6089 double angPrevCeil = ceil(angPrev);
6090 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6092 int iC = int( angCurFloor );
6093 if ( iC < nbAngles )
6094 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6096 iP = int( angPrevCeil );
6098 angle += theAngles[ iC ];
6100 res.push_back(angle);
6105 for(; it!=res.end(); it++)
6106 Angles.push_back( *it );
6111 //================================================================================
6113 * \brief Move or copy theElements applying theTrsf to their nodes
6114 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6115 * \param theTrsf - transformation to apply
6116 * \param theCopy - if true, create translated copies of theElems
6117 * \param theMakeGroups - if true and theCopy, create translated groups
6118 * \param theTargetMesh - mesh to copy translated elements into
6119 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6121 //================================================================================
6123 SMESH_MeshEditor::PGroupIDs
6124 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6125 const gp_Trsf& theTrsf,
6127 const bool theMakeGroups,
6128 SMESH_Mesh* theTargetMesh)
6130 myLastCreatedElems.Clear();
6131 myLastCreatedNodes.Clear();
6133 bool needReverse = false;
6134 string groupPostfix;
6135 switch ( theTrsf.Form() ) {
6137 MESSAGE("gp_PntMirror");
6139 groupPostfix = "mirrored";
6142 MESSAGE("gp_Ax1Mirror");
6143 groupPostfix = "mirrored";
6146 MESSAGE("gp_Ax2Mirror");
6148 groupPostfix = "mirrored";
6151 MESSAGE("gp_Rotation");
6152 groupPostfix = "rotated";
6154 case gp_Translation:
6155 MESSAGE("gp_Translation");
6156 groupPostfix = "translated";
6159 MESSAGE("gp_Scale");
6160 groupPostfix = "scaled";
6162 case gp_CompoundTrsf: // different scale by axis
6163 MESSAGE("gp_CompoundTrsf");
6164 groupPostfix = "scaled";
6168 needReverse = false;
6169 groupPostfix = "transformed";
6172 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6173 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6174 SMESHDS_Mesh* aMesh = GetMeshDS();
6177 // map old node to new one
6178 TNodeNodeMap nodeMap;
6180 // elements sharing moved nodes; those of them which have all
6181 // nodes mirrored but are not in theElems are to be reversed
6182 TIDSortedElemSet inverseElemSet;
6184 // source elements for each generated one
6185 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6187 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6188 TIDSortedElemSet orphanNode;
6190 if ( theElems.empty() ) // transform the whole mesh
6193 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6194 while ( eIt->more() ) theElems.insert( eIt->next() );
6196 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6197 while ( nIt->more() )
6199 const SMDS_MeshNode* node = nIt->next();
6200 if ( node->NbInverseElements() == 0)
6201 orphanNode.insert( node );
6205 // loop on elements to transform nodes : first orphan nodes then elems
6206 TIDSortedElemSet::iterator itElem;
6207 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6208 for (int i=0; i<2; i++)
6209 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6210 const SMDS_MeshElement* elem = *itElem;
6214 // loop on elem nodes
6215 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6216 while ( itN->more() ) {
6218 const SMDS_MeshNode* node = cast2Node( itN->next() );
6219 // check if a node has been already transformed
6220 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6221 nodeMap.insert( make_pair ( node, node ));
6222 if ( !n2n_isnew.second )
6226 coord[0] = node->X();
6227 coord[1] = node->Y();
6228 coord[2] = node->Z();
6229 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6230 if ( theTargetMesh ) {
6231 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6232 n2n_isnew.first->second = newNode;
6233 myLastCreatedNodes.Append(newNode);
6234 srcNodes.Append( node );
6236 else if ( theCopy ) {
6237 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6238 n2n_isnew.first->second = newNode;
6239 myLastCreatedNodes.Append(newNode);
6240 srcNodes.Append( node );
6243 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6244 // node position on shape becomes invalid
6245 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6246 ( SMDS_SpacePosition::originSpacePosition() );
6249 // keep inverse elements
6250 if ( !theCopy && !theTargetMesh && needReverse ) {
6251 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6252 while ( invElemIt->more() ) {
6253 const SMDS_MeshElement* iel = invElemIt->next();
6254 inverseElemSet.insert( iel );
6260 // either create new elements or reverse mirrored ones
6261 if ( !theCopy && !needReverse && !theTargetMesh )
6264 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6265 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6266 theElems.insert( *invElemIt );
6268 // Replicate or reverse elements
6270 std::vector<int> iForw;
6271 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6273 const SMDS_MeshElement* elem = *itElem;
6274 if ( !elem ) continue;
6276 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6277 int nbNodes = elem->NbNodes();
6278 if ( geomType == SMDSGeom_NONE ) continue; // node
6280 switch ( geomType ) {
6282 case SMDSGeom_POLYGON: // ---------------------- polygon
6284 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6286 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6287 while (itN->more()) {
6288 const SMDS_MeshNode* node =
6289 static_cast<const SMDS_MeshNode*>(itN->next());
6290 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6291 if (nodeMapIt == nodeMap.end())
6292 break; // not all nodes transformed
6294 // reverse mirrored faces and volumes
6295 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6297 poly_nodes[iNode] = (*nodeMapIt).second;
6301 if ( iNode != nbNodes )
6302 continue; // not all nodes transformed
6304 if ( theTargetMesh ) {
6305 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6306 srcElems.Append( elem );
6308 else if ( theCopy ) {
6309 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6310 srcElems.Append( elem );
6313 aMesh->ChangePolygonNodes(elem, poly_nodes);
6318 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6320 const SMDS_VtkVolume* aPolyedre =
6321 dynamic_cast<const SMDS_VtkVolume*>( elem );
6323 MESSAGE("Warning: bad volumic element");
6327 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6328 vector<int> quantities; quantities.reserve( nbNodes );
6330 bool allTransformed = true;
6331 int nbFaces = aPolyedre->NbFaces();
6332 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6333 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6334 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6335 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6336 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6337 if (nodeMapIt == nodeMap.end()) {
6338 allTransformed = false; // not all nodes transformed
6340 poly_nodes.push_back((*nodeMapIt).second);
6342 if ( needReverse && allTransformed )
6343 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6345 quantities.push_back(nbFaceNodes);
6347 if ( !allTransformed )
6348 continue; // not all nodes transformed
6350 if ( theTargetMesh ) {
6351 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6352 srcElems.Append( elem );
6354 else if ( theCopy ) {
6355 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6356 srcElems.Append( elem );
6359 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6364 case SMDSGeom_BALL: // -------------------- Ball
6366 if ( !theCopy && !theTargetMesh ) continue;
6368 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6369 if (nodeMapIt == nodeMap.end())
6370 continue; // not all nodes transformed
6372 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6373 if ( theTargetMesh ) {
6374 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6375 srcElems.Append( elem );
6378 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6379 srcElems.Append( elem );
6384 default: // ----------------------- Regular elements
6386 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6387 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6388 const std::vector<int>& i = needReverse ? iRev : iForw;
6390 // find transformed nodes
6391 vector<const SMDS_MeshNode*> nodes(nbNodes);
6393 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6394 while ( itN->more() ) {
6395 const SMDS_MeshNode* node =
6396 static_cast<const SMDS_MeshNode*>( itN->next() );
6397 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6398 if ( nodeMapIt == nodeMap.end() )
6399 break; // not all nodes transformed
6400 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6402 if ( iNode != nbNodes )
6403 continue; // not all nodes transformed
6405 if ( theTargetMesh ) {
6406 if ( SMDS_MeshElement* copy =
6407 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6408 myLastCreatedElems.Append( copy );
6409 srcElems.Append( elem );
6412 else if ( theCopy ) {
6413 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6414 srcElems.Append( elem );
6417 // reverse element as it was reversed by transformation
6419 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6421 } // switch ( geomType )
6423 } // loop on elements
6425 PGroupIDs newGroupIDs;
6427 if ( ( theMakeGroups && theCopy ) ||
6428 ( theMakeGroups && theTargetMesh ) )
6429 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6434 //=======================================================================
6436 * \brief Create groups of elements made during transformation
6437 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6438 * \param elemGens - elements making corresponding myLastCreatedElems
6439 * \param postfix - to append to names of new groups
6440 * \param targetMesh - mesh to create groups in
6441 * \param topPresent - is there "top" elements that are created by sweeping
6443 //=======================================================================
6445 SMESH_MeshEditor::PGroupIDs
6446 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6447 const SMESH_SequenceOfElemPtr& elemGens,
6448 const std::string& postfix,
6449 SMESH_Mesh* targetMesh,
6450 const bool topPresent)
6452 PGroupIDs newGroupIDs( new list<int> );
6453 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6455 // Sort existing groups by types and collect their names
6457 // containers to store an old group and generated new ones;
6458 // 1st new group is for result elems of different type than a source one;
6459 // 2nd new group is for same type result elems ("top" group at extrusion)
6461 using boost::make_tuple;
6462 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6463 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6464 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6466 set< string > groupNames;
6468 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6469 if ( !groupIt->more() ) return newGroupIDs;
6471 int newGroupID = mesh->GetGroupIds().back()+1;
6472 while ( groupIt->more() )
6474 SMESH_Group * group = groupIt->next();
6475 if ( !group ) continue;
6476 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6477 if ( !groupDS || groupDS->IsEmpty() ) continue;
6478 groupNames.insert ( group->GetName() );
6479 groupDS->SetStoreName( group->GetName() );
6480 const SMDSAbs_ElementType type = groupDS->GetType();
6481 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6482 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6483 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6484 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6487 // Loop on nodes and elements to add them in new groups
6489 vector< const SMDS_MeshElement* > resultElems;
6490 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6492 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6493 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6494 if ( gens.Length() != elems.Length() )
6495 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6497 // loop on created elements
6498 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6500 const SMDS_MeshElement* sourceElem = gens( iElem );
6501 if ( !sourceElem ) {
6502 MESSAGE("generateGroups(): NULL source element");
6505 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6506 if ( groupsOldNew.empty() ) { // no groups of this type at all
6507 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6508 ++iElem; // skip all elements made by sourceElem
6511 // collect all elements made by the iElem-th sourceElem
6512 resultElems.clear();
6513 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6514 if ( resElem != sourceElem )
6515 resultElems.push_back( resElem );
6516 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6517 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6518 if ( resElem != sourceElem )
6519 resultElems.push_back( resElem );
6521 const SMDS_MeshElement* topElem = 0;
6522 if ( isNodes ) // there must be a top element
6524 topElem = resultElems.back();
6525 resultElems.pop_back();
6529 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6530 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6531 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6533 topElem = *resElemIt;
6534 *resElemIt = 0; // erase *resElemIt
6538 // add resultElems to groups originted from ones the sourceElem belongs to
6539 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6540 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6542 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6543 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6545 // fill in a new group
6546 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6547 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6548 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6550 newGroup.Add( *resElemIt );
6552 // fill a "top" group
6555 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6556 newTopGroup.Add( topElem );
6560 } // loop on created elements
6561 }// loop on nodes and elements
6563 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6565 list<int> topGrouIds;
6566 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6568 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6569 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6570 orderedOldNewGroups[i]->get<2>() };
6571 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6573 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6574 if ( newGroupDS->IsEmpty() )
6576 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6581 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6584 const bool isTop = ( topPresent &&
6585 newGroupDS->GetType() == oldGroupDS->GetType() &&
6588 string name = oldGroupDS->GetStoreName();
6589 { // remove trailing whitespaces (issue 22599)
6590 size_t size = name.size();
6591 while ( size > 1 && isspace( name[ size-1 ]))
6593 if ( size != name.size() )
6595 name.resize( size );
6596 oldGroupDS->SetStoreName( name.c_str() );
6599 if ( !targetMesh ) {
6600 string suffix = ( isTop ? "top": postfix.c_str() );
6604 while ( !groupNames.insert( name ).second ) // name exists
6605 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6610 newGroupDS->SetStoreName( name.c_str() );
6612 // make a SMESH_Groups
6613 mesh->AddGroup( newGroupDS );
6615 topGrouIds.push_back( newGroupDS->GetID() );
6617 newGroupIDs->push_back( newGroupDS->GetID() );
6621 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6626 //================================================================================
6628 * \brief Return list of group of nodes close to each other within theTolerance
6629 * Search among theNodes or in the whole mesh if theNodes is empty using
6630 * an Octree algorithm
6632 //================================================================================
6634 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6635 const double theTolerance,
6636 TListOfListOfNodes & theGroupsOfNodes)
6638 myLastCreatedElems.Clear();
6639 myLastCreatedNodes.Clear();
6641 if ( theNodes.empty() )
6642 { // get all nodes in the mesh
6643 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6644 while ( nIt->more() )
6645 theNodes.insert( theNodes.end(),nIt->next());
6648 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6651 //=======================================================================
6652 //function : SimplifyFace
6654 //=======================================================================
6656 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6657 vector<const SMDS_MeshNode *>& poly_nodes,
6658 vector<int>& quantities) const
6660 int nbNodes = faceNodes.size();
6665 set<const SMDS_MeshNode*> nodeSet;
6667 // get simple seq of nodes
6668 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6669 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6670 int iSimple = 0, nbUnique = 0;
6672 simpleNodes[iSimple++] = faceNodes[0];
6674 for (int iCur = 1; iCur < nbNodes; iCur++) {
6675 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6676 simpleNodes[iSimple++] = faceNodes[iCur];
6677 if (nodeSet.insert( faceNodes[iCur] ).second)
6681 int nbSimple = iSimple;
6682 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6692 bool foundLoop = (nbSimple > nbUnique);
6695 set<const SMDS_MeshNode*> loopSet;
6696 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6697 const SMDS_MeshNode* n = simpleNodes[iSimple];
6698 if (!loopSet.insert( n ).second) {
6702 int iC = 0, curLast = iSimple;
6703 for (; iC < curLast; iC++) {
6704 if (simpleNodes[iC] == n) break;
6706 int loopLen = curLast - iC;
6708 // create sub-element
6710 quantities.push_back(loopLen);
6711 for (; iC < curLast; iC++) {
6712 poly_nodes.push_back(simpleNodes[iC]);
6715 // shift the rest nodes (place from the first loop position)
6716 for (iC = curLast + 1; iC < nbSimple; iC++) {
6717 simpleNodes[iC - loopLen] = simpleNodes[iC];
6719 nbSimple -= loopLen;
6722 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6723 } // while (foundLoop)
6727 quantities.push_back(iSimple);
6728 for (int i = 0; i < iSimple; i++)
6729 poly_nodes.push_back(simpleNodes[i]);
6735 //=======================================================================
6736 //function : MergeNodes
6737 //purpose : In each group, the cdr of nodes are substituted by the first one
6739 //=======================================================================
6741 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6743 MESSAGE("MergeNodes");
6744 myLastCreatedElems.Clear();
6745 myLastCreatedNodes.Clear();
6747 SMESHDS_Mesh* aMesh = GetMeshDS();
6749 TNodeNodeMap nodeNodeMap; // node to replace - new node
6750 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6751 list< int > rmElemIds, rmNodeIds;
6753 // Fill nodeNodeMap and elems
6755 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6756 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6757 list<const SMDS_MeshNode*>& nodes = *grIt;
6758 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6759 const SMDS_MeshNode* nToKeep = *nIt;
6760 //MESSAGE("node to keep " << nToKeep->GetID());
6761 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6762 const SMDS_MeshNode* nToRemove = *nIt;
6763 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6764 if ( nToRemove != nToKeep ) {
6765 //MESSAGE(" node to remove " << nToRemove->GetID());
6766 rmNodeIds.push_back( nToRemove->GetID() );
6767 AddToSameGroups( nToKeep, nToRemove, aMesh );
6768 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6769 // after MergeNodes() w/o creating node in place of merged ones.
6770 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6771 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6772 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6773 sm->SetIsAlwaysComputed( true );
6776 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6777 while ( invElemIt->more() ) {
6778 const SMDS_MeshElement* elem = invElemIt->next();
6783 // Change element nodes or remove an element
6785 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6786 for ( ; eIt != elems.end(); eIt++ ) {
6787 const SMDS_MeshElement* elem = *eIt;
6788 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6789 int nbNodes = elem->NbNodes();
6790 int aShapeId = FindShape( elem );
6792 set<const SMDS_MeshNode*> nodeSet;
6793 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6794 int iUnique = 0, iCur = 0, nbRepl = 0;
6795 vector<int> iRepl( nbNodes );
6797 // get new seq of nodes
6798 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6799 while ( itN->more() ) {
6800 const SMDS_MeshNode* n =
6801 static_cast<const SMDS_MeshNode*>( itN->next() );
6803 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6804 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6806 // BUG 0020185: begin
6808 bool stopRecur = false;
6809 set<const SMDS_MeshNode*> nodesRecur;
6810 nodesRecur.insert(n);
6811 while (!stopRecur) {
6812 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6813 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6814 n = (*nnIt_i).second;
6815 if (!nodesRecur.insert(n).second) {
6816 // error: recursive dependancy
6826 curNodes[ iCur ] = n;
6827 bool isUnique = nodeSet.insert( n ).second;
6829 uniqueNodes[ iUnique++ ] = n;
6831 iRepl[ nbRepl++ ] = iCur;
6835 // Analyse element topology after replacement
6838 int nbUniqueNodes = nodeSet.size();
6839 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6840 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6841 // Polygons and Polyhedral volumes
6842 if (elem->IsPoly()) {
6844 if (elem->GetType() == SMDSAbs_Face) {
6846 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6848 for (; inode < nbNodes; inode++) {
6849 face_nodes[inode] = curNodes[inode];
6852 vector<const SMDS_MeshNode *> polygons_nodes;
6853 vector<int> quantities;
6854 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6857 for (int iface = 0; iface < nbNew; iface++) {
6858 int nbNodes = quantities[iface];
6859 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6860 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6861 poly_nodes[ii] = polygons_nodes[inode];
6863 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6864 myLastCreatedElems.Append(newElem);
6866 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6869 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6870 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6871 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6873 if (nbNew > 0) quid = nbNew - 1;
6874 vector<int> newquant(quantities.begin()+quid, quantities.end());
6875 const SMDS_MeshElement* newElem = 0;
6876 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6877 myLastCreatedElems.Append(newElem);
6878 if ( aShapeId && newElem )
6879 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6880 rmElemIds.push_back(elem->GetID());
6883 rmElemIds.push_back(elem->GetID());
6887 else if (elem->GetType() == SMDSAbs_Volume) {
6888 // Polyhedral volume
6889 if (nbUniqueNodes < 4) {
6890 rmElemIds.push_back(elem->GetID());
6893 // each face has to be analyzed in order to check volume validity
6894 const SMDS_VtkVolume* aPolyedre =
6895 dynamic_cast<const SMDS_VtkVolume*>( elem );
6897 int nbFaces = aPolyedre->NbFaces();
6899 vector<const SMDS_MeshNode *> poly_nodes;
6900 vector<int> quantities;
6902 for (int iface = 1; iface <= nbFaces; iface++) {
6903 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6904 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6906 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6907 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6908 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6909 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6910 faceNode = (*nnIt).second;
6912 faceNodes[inode - 1] = faceNode;
6915 SimplifyFace(faceNodes, poly_nodes, quantities);
6918 if (quantities.size() > 3) {
6919 // to be done: remove coincident faces
6922 if (quantities.size() > 3)
6924 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6925 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6926 const SMDS_MeshElement* newElem = 0;
6927 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6928 myLastCreatedElems.Append(newElem);
6929 if ( aShapeId && newElem )
6930 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6931 rmElemIds.push_back(elem->GetID());
6935 rmElemIds.push_back(elem->GetID());
6946 // TODO not all the possible cases are solved. Find something more generic?
6947 switch ( nbNodes ) {
6948 case 2: ///////////////////////////////////// EDGE
6949 isOk = false; break;
6950 case 3: ///////////////////////////////////// TRIANGLE
6951 isOk = false; break;
6953 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6955 else { //////////////////////////////////// QUADRANGLE
6956 if ( nbUniqueNodes < 3 )
6958 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6959 isOk = false; // opposite nodes stick
6960 //MESSAGE("isOk " << isOk);
6963 case 6: ///////////////////////////////////// PENTAHEDRON
6964 if ( nbUniqueNodes == 4 ) {
6965 // ---------------------------------> tetrahedron
6967 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6968 // all top nodes stick: reverse a bottom
6969 uniqueNodes[ 0 ] = curNodes [ 1 ];
6970 uniqueNodes[ 1 ] = curNodes [ 0 ];
6972 else if (nbRepl == 3 &&
6973 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6974 // all bottom nodes stick: set a top before
6975 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6976 uniqueNodes[ 0 ] = curNodes [ 3 ];
6977 uniqueNodes[ 1 ] = curNodes [ 4 ];
6978 uniqueNodes[ 2 ] = curNodes [ 5 ];
6980 else if (nbRepl == 4 &&
6981 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6982 // a lateral face turns into a line: reverse a bottom
6983 uniqueNodes[ 0 ] = curNodes [ 1 ];
6984 uniqueNodes[ 1 ] = curNodes [ 0 ];
6989 else if ( nbUniqueNodes == 5 ) {
6990 // PENTAHEDRON --------------------> 2 tetrahedrons
6991 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6992 // a bottom node sticks with a linked top one
6994 SMDS_MeshElement* newElem =
6995 aMesh->AddVolume(curNodes[ 3 ],
6998 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6999 myLastCreatedElems.Append(newElem);
7001 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7002 // 2. : reverse a bottom
7003 uniqueNodes[ 0 ] = curNodes [ 1 ];
7004 uniqueNodes[ 1 ] = curNodes [ 0 ];
7014 if(elem->IsQuadratic()) { // Quadratic quadrangle
7026 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7029 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7031 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7032 uniqueNodes[0] = curNodes[0];
7033 uniqueNodes[1] = curNodes[2];
7034 uniqueNodes[2] = curNodes[3];
7035 uniqueNodes[3] = curNodes[5];
7036 uniqueNodes[4] = curNodes[6];
7037 uniqueNodes[5] = curNodes[7];
7040 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7041 uniqueNodes[0] = curNodes[0];
7042 uniqueNodes[1] = curNodes[1];
7043 uniqueNodes[2] = curNodes[2];
7044 uniqueNodes[3] = curNodes[4];
7045 uniqueNodes[4] = curNodes[5];
7046 uniqueNodes[5] = curNodes[6];
7049 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7050 uniqueNodes[0] = curNodes[1];
7051 uniqueNodes[1] = curNodes[2];
7052 uniqueNodes[2] = curNodes[3];
7053 uniqueNodes[3] = curNodes[5];
7054 uniqueNodes[4] = curNodes[6];
7055 uniqueNodes[5] = curNodes[0];
7058 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7059 uniqueNodes[0] = curNodes[0];
7060 uniqueNodes[1] = curNodes[1];
7061 uniqueNodes[2] = curNodes[3];
7062 uniqueNodes[3] = curNodes[4];
7063 uniqueNodes[4] = curNodes[6];
7064 uniqueNodes[5] = curNodes[7];
7067 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7068 uniqueNodes[0] = curNodes[0];
7069 uniqueNodes[1] = curNodes[2];
7070 uniqueNodes[2] = curNodes[3];
7071 uniqueNodes[3] = curNodes[1];
7072 uniqueNodes[4] = curNodes[6];
7073 uniqueNodes[5] = curNodes[7];
7076 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7077 uniqueNodes[0] = curNodes[0];
7078 uniqueNodes[1] = curNodes[1];
7079 uniqueNodes[2] = curNodes[2];
7080 uniqueNodes[3] = curNodes[4];
7081 uniqueNodes[4] = curNodes[5];
7082 uniqueNodes[5] = curNodes[7];
7085 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7086 uniqueNodes[0] = curNodes[0];
7087 uniqueNodes[1] = curNodes[1];
7088 uniqueNodes[2] = curNodes[3];
7089 uniqueNodes[3] = curNodes[4];
7090 uniqueNodes[4] = curNodes[2];
7091 uniqueNodes[5] = curNodes[7];
7094 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7095 uniqueNodes[0] = curNodes[0];
7096 uniqueNodes[1] = curNodes[1];
7097 uniqueNodes[2] = curNodes[2];
7098 uniqueNodes[3] = curNodes[4];
7099 uniqueNodes[4] = curNodes[5];
7100 uniqueNodes[5] = curNodes[3];
7105 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7108 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7112 //////////////////////////////////// HEXAHEDRON
7114 SMDS_VolumeTool hexa (elem);
7115 hexa.SetExternalNormal();
7116 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7117 //////////////////////// HEX ---> 1 tetrahedron
7118 for ( int iFace = 0; iFace < 6; iFace++ ) {
7119 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7120 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7121 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7122 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7123 // one face turns into a point ...
7124 int iOppFace = hexa.GetOppFaceIndex( iFace );
7125 ind = hexa.GetFaceNodesIndices( iOppFace );
7127 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7128 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7131 if ( nbStick == 1 ) {
7132 // ... and the opposite one - into a triangle.
7134 ind = hexa.GetFaceNodesIndices( iFace );
7135 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7142 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7143 //////////////////////// HEX ---> 1 prism
7144 int nbTria = 0, iTria[3];
7145 const int *ind; // indices of face nodes
7146 // look for triangular faces
7147 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7148 ind = hexa.GetFaceNodesIndices( iFace );
7149 TIDSortedNodeSet faceNodes;
7150 for ( iCur = 0; iCur < 4; iCur++ )
7151 faceNodes.insert( curNodes[ind[iCur]] );
7152 if ( faceNodes.size() == 3 )
7153 iTria[ nbTria++ ] = iFace;
7155 // check if triangles are opposite
7156 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7159 // set nodes of the bottom triangle
7160 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7162 for ( iCur = 0; iCur < 4; iCur++ )
7163 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7164 indB.push_back( ind[iCur] );
7165 if ( !hexa.IsForward() )
7166 std::swap( indB[0], indB[2] );
7167 for ( iCur = 0; iCur < 3; iCur++ )
7168 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7169 // set nodes of the top triangle
7170 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7171 for ( iCur = 0; iCur < 3; ++iCur )
7172 for ( int j = 0; j < 4; ++j )
7173 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7175 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7181 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7182 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7183 for ( int iFace = 0; iFace < 6; iFace++ ) {
7184 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7185 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7186 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7187 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7188 // one face turns into a point ...
7189 int iOppFace = hexa.GetOppFaceIndex( iFace );
7190 ind = hexa.GetFaceNodesIndices( iOppFace );
7192 iUnique = 2; // reverse a tetrahedron 1 bottom
7193 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7194 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7196 else if ( iUnique >= 0 )
7197 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7199 if ( nbStick == 0 ) {
7200 // ... and the opposite one is a quadrangle
7202 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7203 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7206 SMDS_MeshElement* newElem =
7207 aMesh->AddVolume(curNodes[ind[ 0 ]],
7210 curNodes[indTop[ 0 ]]);
7211 myLastCreatedElems.Append(newElem);
7213 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7220 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7221 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7222 // find indices of quad and tri faces
7223 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7224 for ( iFace = 0; iFace < 6; iFace++ ) {
7225 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7227 for ( iCur = 0; iCur < 4; iCur++ )
7228 nodeSet.insert( curNodes[ind[ iCur ]] );
7229 nbUniqueNodes = nodeSet.size();
7230 if ( nbUniqueNodes == 3 )
7231 iTriFace[ nbTri++ ] = iFace;
7232 else if ( nbUniqueNodes == 4 )
7233 iQuadFace[ nbQuad++ ] = iFace;
7235 if (nbQuad == 2 && nbTri == 4 &&
7236 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7237 // 2 opposite quadrangles stuck with a diagonal;
7238 // sample groups of merged indices: (0-4)(2-6)
7239 // --------------------------------------------> 2 tetrahedrons
7240 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7241 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7242 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7243 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7244 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7245 // stuck with 0-2 diagonal
7253 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7254 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7255 // stuck with 1-3 diagonal
7267 uniqueNodes[ 0 ] = curNodes [ i0 ];
7268 uniqueNodes[ 1 ] = curNodes [ i1d ];
7269 uniqueNodes[ 2 ] = curNodes [ i3d ];
7270 uniqueNodes[ 3 ] = curNodes [ i0t ];
7273 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7277 myLastCreatedElems.Append(newElem);
7279 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7282 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7283 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7284 // --------------------------------------------> prism
7285 // find 2 opposite triangles
7287 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7288 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7289 // find indices of kept and replaced nodes
7290 // and fill unique nodes of 2 opposite triangles
7291 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7292 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7293 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7294 // fill unique nodes
7297 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7298 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7299 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7301 // iCur of a linked node of the opposite face (make normals co-directed):
7302 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7303 // check that correspondent corners of triangles are linked
7304 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7307 uniqueNodes[ iUnique ] = n;
7308 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7317 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7320 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7327 } // switch ( nbNodes )
7329 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7331 if ( isOk ) { // the elem remains valid after sticking nodes
7332 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7334 // Change nodes of polyedre
7335 const SMDS_VtkVolume* aPolyedre =
7336 dynamic_cast<const SMDS_VtkVolume*>( elem );
7338 int nbFaces = aPolyedre->NbFaces();
7340 vector<const SMDS_MeshNode *> poly_nodes;
7341 vector<int> quantities (nbFaces);
7343 for (int iface = 1; iface <= nbFaces; iface++) {
7344 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7345 quantities[iface - 1] = nbFaceNodes;
7347 for (inode = 1; inode <= nbFaceNodes; inode++) {
7348 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7350 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7351 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7352 curNode = (*nnIt).second;
7354 poly_nodes.push_back(curNode);
7357 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7360 else // replace non-polyhedron elements
7362 const SMDSAbs_ElementType etyp = elem->GetType();
7363 const int elemId = elem->GetID();
7364 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7365 uniqueNodes.resize(nbUniqueNodes);
7367 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7369 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7370 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7371 if ( sm && newElem )
7372 sm->AddElement( newElem );
7373 if ( elem != newElem )
7374 ReplaceElemInGroups( elem, newElem, aMesh );
7378 // Remove invalid regular element or invalid polygon
7379 rmElemIds.push_back( elem->GetID() );
7382 } // loop on elements
7384 // Remove bad elements, then equal nodes (order important)
7386 Remove( rmElemIds, false );
7387 Remove( rmNodeIds, true );
7392 // ========================================================
7393 // class : SortableElement
7394 // purpose : allow sorting elements basing on their nodes
7395 // ========================================================
7396 class SortableElement : public set <const SMDS_MeshElement*>
7400 SortableElement( const SMDS_MeshElement* theElem )
7403 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7404 while ( nodeIt->more() )
7405 this->insert( nodeIt->next() );
7408 const SMDS_MeshElement* Get() const
7411 void Set(const SMDS_MeshElement* e) const
7416 mutable const SMDS_MeshElement* myElem;
7419 //=======================================================================
7420 //function : FindEqualElements
7421 //purpose : Return list of group of elements built on the same nodes.
7422 // Search among theElements or in the whole mesh if theElements is empty
7423 //=======================================================================
7425 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7426 TListOfListOfElementsID & theGroupsOfElementsID)
7428 myLastCreatedElems.Clear();
7429 myLastCreatedNodes.Clear();
7431 typedef map< SortableElement, int > TMapOfNodeSet;
7432 typedef list<int> TGroupOfElems;
7434 if ( theElements.empty() )
7435 { // get all elements in the mesh
7436 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7437 while ( eIt->more() )
7438 theElements.insert( theElements.end(), eIt->next());
7441 vector< TGroupOfElems > arrayOfGroups;
7442 TGroupOfElems groupOfElems;
7443 TMapOfNodeSet mapOfNodeSet;
7445 TIDSortedElemSet::iterator elemIt = theElements.begin();
7446 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7447 const SMDS_MeshElement* curElem = *elemIt;
7448 SortableElement SE(curElem);
7451 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7452 if( !(pp.second) ) {
7453 TMapOfNodeSet::iterator& itSE = pp.first;
7454 ind = (*itSE).second;
7455 arrayOfGroups[ind].push_back(curElem->GetID());
7458 groupOfElems.clear();
7459 groupOfElems.push_back(curElem->GetID());
7460 arrayOfGroups.push_back(groupOfElems);
7465 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7466 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7467 groupOfElems = *groupIt;
7468 if ( groupOfElems.size() > 1 ) {
7469 groupOfElems.sort();
7470 theGroupsOfElementsID.push_back(groupOfElems);
7475 //=======================================================================
7476 //function : MergeElements
7477 //purpose : In each given group, substitute all elements by the first one.
7478 //=======================================================================
7480 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7482 myLastCreatedElems.Clear();
7483 myLastCreatedNodes.Clear();
7485 typedef list<int> TListOfIDs;
7486 TListOfIDs rmElemIds; // IDs of elems to remove
7488 SMESHDS_Mesh* aMesh = GetMeshDS();
7490 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7491 while ( groupsIt != theGroupsOfElementsID.end() ) {
7492 TListOfIDs& aGroupOfElemID = *groupsIt;
7493 aGroupOfElemID.sort();
7494 int elemIDToKeep = aGroupOfElemID.front();
7495 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7496 aGroupOfElemID.pop_front();
7497 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7498 while ( idIt != aGroupOfElemID.end() ) {
7499 int elemIDToRemove = *idIt;
7500 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7501 // add the kept element in groups of removed one (PAL15188)
7502 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7503 rmElemIds.push_back( elemIDToRemove );
7509 Remove( rmElemIds, false );
7512 //=======================================================================
7513 //function : MergeEqualElements
7514 //purpose : Remove all but one of elements built on the same nodes.
7515 //=======================================================================
7517 void SMESH_MeshEditor::MergeEqualElements()
7519 TIDSortedElemSet aMeshElements; /* empty input ==
7520 to merge equal elements in the whole mesh */
7521 TListOfListOfElementsID aGroupsOfElementsID;
7522 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7523 MergeElements(aGroupsOfElementsID);
7526 //=======================================================================
7527 //function : findAdjacentFace
7529 //=======================================================================
7531 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7532 const SMDS_MeshNode* n2,
7533 const SMDS_MeshElement* elem)
7535 TIDSortedElemSet elemSet, avoidSet;
7537 avoidSet.insert ( elem );
7538 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7541 //=======================================================================
7542 //function : FindFreeBorder
7544 //=======================================================================
7546 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7548 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7549 const SMDS_MeshNode* theSecondNode,
7550 const SMDS_MeshNode* theLastNode,
7551 list< const SMDS_MeshNode* > & theNodes,
7552 list< const SMDS_MeshElement* >& theFaces)
7554 if ( !theFirstNode || !theSecondNode )
7556 // find border face between theFirstNode and theSecondNode
7557 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7561 theFaces.push_back( curElem );
7562 theNodes.push_back( theFirstNode );
7563 theNodes.push_back( theSecondNode );
7565 //vector<const SMDS_MeshNode*> nodes;
7566 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7567 TIDSortedElemSet foundElems;
7568 bool needTheLast = ( theLastNode != 0 );
7570 while ( nStart != theLastNode ) {
7571 if ( nStart == theFirstNode )
7572 return !needTheLast;
7574 // find all free border faces sharing form nStart
7576 list< const SMDS_MeshElement* > curElemList;
7577 list< const SMDS_MeshNode* > nStartList;
7578 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7579 while ( invElemIt->more() ) {
7580 const SMDS_MeshElement* e = invElemIt->next();
7581 if ( e == curElem || foundElems.insert( e ).second ) {
7583 int iNode = 0, nbNodes = e->NbNodes();
7584 //const SMDS_MeshNode* nodes[nbNodes+1];
7585 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7587 if(e->IsQuadratic()) {
7588 const SMDS_VtkFace* F =
7589 dynamic_cast<const SMDS_VtkFace*>(e);
7590 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7591 // use special nodes iterator
7592 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7593 while( anIter->more() ) {
7594 nodes[ iNode++ ] = cast2Node(anIter->next());
7598 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7599 while ( nIt->more() )
7600 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7602 nodes[ iNode ] = nodes[ 0 ];
7604 for ( iNode = 0; iNode < nbNodes; iNode++ )
7605 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7606 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7607 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7609 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7610 curElemList.push_back( e );
7614 // analyse the found
7616 int nbNewBorders = curElemList.size();
7617 if ( nbNewBorders == 0 ) {
7618 // no free border furthermore
7619 return !needTheLast;
7621 else if ( nbNewBorders == 1 ) {
7622 // one more element found
7624 nStart = nStartList.front();
7625 curElem = curElemList.front();
7626 theFaces.push_back( curElem );
7627 theNodes.push_back( nStart );
7630 // several continuations found
7631 list< const SMDS_MeshElement* >::iterator curElemIt;
7632 list< const SMDS_MeshNode* >::iterator nStartIt;
7633 // check if one of them reached the last node
7634 if ( needTheLast ) {
7635 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7636 curElemIt!= curElemList.end();
7637 curElemIt++, nStartIt++ )
7638 if ( *nStartIt == theLastNode ) {
7639 theFaces.push_back( *curElemIt );
7640 theNodes.push_back( *nStartIt );
7644 // find the best free border by the continuations
7645 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7646 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7647 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7648 curElemIt!= curElemList.end();
7649 curElemIt++, nStartIt++ )
7651 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7652 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7653 // find one more free border
7654 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7658 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7659 // choice: clear a worse one
7660 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7661 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7662 contNodes[ iWorse ].clear();
7663 contFaces[ iWorse ].clear();
7666 if ( contNodes[0].empty() && contNodes[1].empty() )
7669 // append the best free border
7670 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7671 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7672 theNodes.pop_back(); // remove nIgnore
7673 theNodes.pop_back(); // remove nStart
7674 theFaces.pop_back(); // remove curElem
7675 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7676 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7677 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7678 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7681 } // several continuations found
7682 } // while ( nStart != theLastNode )
7687 //=======================================================================
7688 //function : CheckFreeBorderNodes
7689 //purpose : Return true if the tree nodes are on a free border
7690 //=======================================================================
7692 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7693 const SMDS_MeshNode* theNode2,
7694 const SMDS_MeshNode* theNode3)
7696 list< const SMDS_MeshNode* > nodes;
7697 list< const SMDS_MeshElement* > faces;
7698 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7701 //=======================================================================
7702 //function : SewFreeBorder
7704 //=======================================================================
7706 SMESH_MeshEditor::Sew_Error
7707 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7708 const SMDS_MeshNode* theBordSecondNode,
7709 const SMDS_MeshNode* theBordLastNode,
7710 const SMDS_MeshNode* theSideFirstNode,
7711 const SMDS_MeshNode* theSideSecondNode,
7712 const SMDS_MeshNode* theSideThirdNode,
7713 const bool theSideIsFreeBorder,
7714 const bool toCreatePolygons,
7715 const bool toCreatePolyedrs)
7717 myLastCreatedElems.Clear();
7718 myLastCreatedNodes.Clear();
7720 MESSAGE("::SewFreeBorder()");
7721 Sew_Error aResult = SEW_OK;
7723 // ====================================
7724 // find side nodes and elements
7725 // ====================================
7727 list< const SMDS_MeshNode* > nSide[ 2 ];
7728 list< const SMDS_MeshElement* > eSide[ 2 ];
7729 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7730 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7734 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7735 nSide[0], eSide[0])) {
7736 MESSAGE(" Free Border 1 not found " );
7737 aResult = SEW_BORDER1_NOT_FOUND;
7739 if (theSideIsFreeBorder) {
7742 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7743 nSide[1], eSide[1])) {
7744 MESSAGE(" Free Border 2 not found " );
7745 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7748 if ( aResult != SEW_OK )
7751 if (!theSideIsFreeBorder) {
7755 // -------------------------------------------------------------------------
7757 // 1. If nodes to merge are not coincident, move nodes of the free border
7758 // from the coord sys defined by the direction from the first to last
7759 // nodes of the border to the correspondent sys of the side 2
7760 // 2. On the side 2, find the links most co-directed with the correspondent
7761 // links of the free border
7762 // -------------------------------------------------------------------------
7764 // 1. Since sewing may break if there are volumes to split on the side 2,
7765 // we wont move nodes but just compute new coordinates for them
7766 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7767 TNodeXYZMap nBordXYZ;
7768 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7769 list< const SMDS_MeshNode* >::iterator nBordIt;
7771 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7772 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7773 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7774 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7775 double tol2 = 1.e-8;
7776 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7777 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7778 // Need node movement.
7780 // find X and Z axes to create trsf
7781 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7783 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7785 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7788 gp_Ax3 toBordAx( Pb1, Zb, X );
7789 gp_Ax3 fromSideAx( Ps1, Zs, X );
7790 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7792 gp_Trsf toBordSys, fromSide2Sys;
7793 toBordSys.SetTransformation( toBordAx );
7794 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7795 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7798 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7799 const SMDS_MeshNode* n = *nBordIt;
7800 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7801 toBordSys.Transforms( xyz );
7802 fromSide2Sys.Transforms( xyz );
7803 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7807 // just insert nodes XYZ in the nBordXYZ map
7808 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7809 const SMDS_MeshNode* n = *nBordIt;
7810 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7814 // 2. On the side 2, find the links most co-directed with the correspondent
7815 // links of the free border
7817 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7818 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7819 sideNodes.push_back( theSideFirstNode );
7821 bool hasVolumes = false;
7822 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7823 set<long> foundSideLinkIDs, checkedLinkIDs;
7824 SMDS_VolumeTool volume;
7825 //const SMDS_MeshNode* faceNodes[ 4 ];
7827 const SMDS_MeshNode* sideNode;
7828 const SMDS_MeshElement* sideElem;
7829 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7830 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7831 nBordIt = bordNodes.begin();
7833 // border node position and border link direction to compare with
7834 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7835 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7836 // choose next side node by link direction or by closeness to
7837 // the current border node:
7838 bool searchByDir = ( *nBordIt != theBordLastNode );
7840 // find the next node on the Side 2
7842 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7844 checkedLinkIDs.clear();
7845 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7847 // loop on inverse elements of current node (prevSideNode) on the Side 2
7848 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7849 while ( invElemIt->more() )
7851 const SMDS_MeshElement* elem = invElemIt->next();
7852 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7853 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7854 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7855 bool isVolume = volume.Set( elem );
7856 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7857 if ( isVolume ) // --volume
7859 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7860 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7861 if(elem->IsQuadratic()) {
7862 const SMDS_VtkFace* F =
7863 dynamic_cast<const SMDS_VtkFace*>(elem);
7864 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7865 // use special nodes iterator
7866 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7867 while( anIter->more() ) {
7868 nodes[ iNode ] = cast2Node(anIter->next());
7869 if ( nodes[ iNode++ ] == prevSideNode )
7870 iPrevNode = iNode - 1;
7874 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7875 while ( nIt->more() ) {
7876 nodes[ iNode ] = cast2Node( nIt->next() );
7877 if ( nodes[ iNode++ ] == prevSideNode )
7878 iPrevNode = iNode - 1;
7881 // there are 2 links to check
7886 // loop on links, to be precise, on the second node of links
7887 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7888 const SMDS_MeshNode* n = nodes[ iNode ];
7890 if ( !volume.IsLinked( n, prevSideNode ))
7894 if ( iNode ) // a node before prevSideNode
7895 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7896 else // a node after prevSideNode
7897 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7899 // check if this link was already used
7900 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7901 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7902 if (!isJustChecked &&
7903 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7905 // test a link geometrically
7906 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7907 bool linkIsBetter = false;
7908 double dot = 0.0, dist = 0.0;
7909 if ( searchByDir ) { // choose most co-directed link
7910 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7911 linkIsBetter = ( dot > maxDot );
7913 else { // choose link with the node closest to bordPos
7914 dist = ( nextXYZ - bordPos ).SquareModulus();
7915 linkIsBetter = ( dist < minDist );
7917 if ( linkIsBetter ) {
7926 } // loop on inverse elements of prevSideNode
7929 MESSAGE(" Cant find path by links of the Side 2 ");
7930 return SEW_BAD_SIDE_NODES;
7932 sideNodes.push_back( sideNode );
7933 sideElems.push_back( sideElem );
7934 foundSideLinkIDs.insert ( linkID );
7935 prevSideNode = sideNode;
7937 if ( *nBordIt == theBordLastNode )
7938 searchByDir = false;
7940 // find the next border link to compare with
7941 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7942 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7943 // move to next border node if sideNode is before forward border node (bordPos)
7944 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7945 prevBordNode = *nBordIt;
7947 bordPos = nBordXYZ[ *nBordIt ];
7948 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7949 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7953 while ( sideNode != theSideSecondNode );
7955 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7956 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7957 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7959 } // end nodes search on the side 2
7961 // ============================
7962 // sew the border to the side 2
7963 // ============================
7965 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7966 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7968 TListOfListOfNodes nodeGroupsToMerge;
7969 if ( nbNodes[0] == nbNodes[1] ||
7970 ( theSideIsFreeBorder && !theSideThirdNode)) {
7972 // all nodes are to be merged
7974 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7975 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7976 nIt[0]++, nIt[1]++ )
7978 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7979 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7980 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7985 // insert new nodes into the border and the side to get equal nb of segments
7987 // get normalized parameters of nodes on the borders
7988 //double param[ 2 ][ maxNbNodes ];
7990 param[0] = new double [ maxNbNodes ];
7991 param[1] = new double [ maxNbNodes ];
7993 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7994 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7995 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7996 const SMDS_MeshNode* nPrev = *nIt;
7997 double bordLength = 0;
7998 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7999 const SMDS_MeshNode* nCur = *nIt;
8000 gp_XYZ segment (nCur->X() - nPrev->X(),
8001 nCur->Y() - nPrev->Y(),
8002 nCur->Z() - nPrev->Z());
8003 double segmentLen = segment.Modulus();
8004 bordLength += segmentLen;
8005 param[ iBord ][ iNode ] = bordLength;
8008 // normalize within [0,1]
8009 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8010 param[ iBord ][ iNode ] /= bordLength;
8014 // loop on border segments
8015 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8016 int i[ 2 ] = { 0, 0 };
8017 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8018 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8020 TElemOfNodeListMap insertMap;
8021 TElemOfNodeListMap::iterator insertMapIt;
8023 // key: elem to insert nodes into
8024 // value: 2 nodes to insert between + nodes to be inserted
8026 bool next[ 2 ] = { false, false };
8028 // find min adjacent segment length after sewing
8029 double nextParam = 10., prevParam = 0;
8030 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8031 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8032 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8033 if ( i[ iBord ] > 0 )
8034 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8036 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8037 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8038 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8040 // choose to insert or to merge nodes
8041 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8042 if ( Abs( du ) <= minSegLen * 0.2 ) {
8045 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8046 const SMDS_MeshNode* n0 = *nIt[0];
8047 const SMDS_MeshNode* n1 = *nIt[1];
8048 nodeGroupsToMerge.back().push_back( n1 );
8049 nodeGroupsToMerge.back().push_back( n0 );
8050 // position of node of the border changes due to merge
8051 param[ 0 ][ i[0] ] += du;
8052 // move n1 for the sake of elem shape evaluation during insertion.
8053 // n1 will be removed by MergeNodes() anyway
8054 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8055 next[0] = next[1] = true;
8060 int intoBord = ( du < 0 ) ? 0 : 1;
8061 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8062 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8063 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8064 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8065 if ( intoBord == 1 ) {
8066 // move node of the border to be on a link of elem of the side
8067 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8068 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8069 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8070 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8071 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8073 insertMapIt = insertMap.find( elem );
8074 bool notFound = ( insertMapIt == insertMap.end() );
8075 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8077 // insert into another link of the same element:
8078 // 1. perform insertion into the other link of the elem
8079 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8080 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8081 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8082 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8083 // 2. perform insertion into the link of adjacent faces
8085 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8087 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8091 if (toCreatePolyedrs) {
8092 // perform insertion into the links of adjacent volumes
8093 UpdateVolumes(n12, n22, nodeList);
8095 // 3. find an element appeared on n1 and n2 after the insertion
8096 insertMap.erase( elem );
8097 elem = findAdjacentFace( n1, n2, 0 );
8099 if ( notFound || otherLink ) {
8100 // add element and nodes of the side into the insertMap
8101 insertMapIt = insertMap.insert
8102 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8103 (*insertMapIt).second.push_back( n1 );
8104 (*insertMapIt).second.push_back( n2 );
8106 // add node to be inserted into elem
8107 (*insertMapIt).second.push_back( nIns );
8108 next[ 1 - intoBord ] = true;
8111 // go to the next segment
8112 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8113 if ( next[ iBord ] ) {
8114 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8116 nPrev[ iBord ] = *nIt[ iBord ];
8117 nIt[ iBord ]++; i[ iBord ]++;
8121 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8123 // perform insertion of nodes into elements
8125 for (insertMapIt = insertMap.begin();
8126 insertMapIt != insertMap.end();
8129 const SMDS_MeshElement* elem = (*insertMapIt).first;
8130 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8131 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8132 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8134 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8136 if ( !theSideIsFreeBorder ) {
8137 // look for and insert nodes into the faces adjacent to elem
8139 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8141 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8146 if (toCreatePolyedrs) {
8147 // perform insertion into the links of adjacent volumes
8148 UpdateVolumes(n1, n2, nodeList);
8154 } // end: insert new nodes
8156 MergeNodes ( nodeGroupsToMerge );
8161 //=======================================================================
8162 //function : InsertNodesIntoLink
8163 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8164 // and theBetweenNode2 and split theElement
8165 //=======================================================================
8167 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8168 const SMDS_MeshNode* theBetweenNode1,
8169 const SMDS_MeshNode* theBetweenNode2,
8170 list<const SMDS_MeshNode*>& theNodesToInsert,
8171 const bool toCreatePoly)
8173 if ( theFace->GetType() != SMDSAbs_Face ) return;
8175 // find indices of 2 link nodes and of the rest nodes
8176 int iNode = 0, il1, il2, i3, i4;
8177 il1 = il2 = i3 = i4 = -1;
8178 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8179 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8181 if(theFace->IsQuadratic()) {
8182 const SMDS_VtkFace* F =
8183 dynamic_cast<const SMDS_VtkFace*>(theFace);
8184 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8185 // use special nodes iterator
8186 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8187 while( anIter->more() ) {
8188 const SMDS_MeshNode* n = cast2Node(anIter->next());
8189 if ( n == theBetweenNode1 )
8191 else if ( n == theBetweenNode2 )
8197 nodes[ iNode++ ] = n;
8201 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8202 while ( nodeIt->more() ) {
8203 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8204 if ( n == theBetweenNode1 )
8206 else if ( n == theBetweenNode2 )
8212 nodes[ iNode++ ] = n;
8215 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8218 // arrange link nodes to go one after another regarding the face orientation
8219 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8220 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8225 aNodesToInsert.reverse();
8227 // check that not link nodes of a quadrangles are in good order
8228 int nbFaceNodes = theFace->NbNodes();
8229 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8235 if (toCreatePoly || theFace->IsPoly()) {
8238 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8240 // add nodes of face up to first node of link
8243 if(theFace->IsQuadratic()) {
8244 const SMDS_VtkFace* F =
8245 dynamic_cast<const SMDS_VtkFace*>(theFace);
8246 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8247 // use special nodes iterator
8248 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8249 while( anIter->more() && !isFLN ) {
8250 const SMDS_MeshNode* n = cast2Node(anIter->next());
8251 poly_nodes[iNode++] = n;
8252 if (n == nodes[il1]) {
8256 // add nodes to insert
8257 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8258 for (; nIt != aNodesToInsert.end(); nIt++) {
8259 poly_nodes[iNode++] = *nIt;
8261 // add nodes of face starting from last node of link
8262 while ( anIter->more() ) {
8263 poly_nodes[iNode++] = cast2Node(anIter->next());
8267 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8268 while ( nodeIt->more() && !isFLN ) {
8269 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8270 poly_nodes[iNode++] = n;
8271 if (n == nodes[il1]) {
8275 // add nodes to insert
8276 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8277 for (; nIt != aNodesToInsert.end(); nIt++) {
8278 poly_nodes[iNode++] = *nIt;
8280 // add nodes of face starting from last node of link
8281 while ( nodeIt->more() ) {
8282 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8283 poly_nodes[iNode++] = n;
8287 // edit or replace the face
8288 SMESHDS_Mesh *aMesh = GetMeshDS();
8290 if (theFace->IsPoly()) {
8291 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8294 int aShapeId = FindShape( theFace );
8296 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8297 myLastCreatedElems.Append(newElem);
8298 if ( aShapeId && newElem )
8299 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8301 aMesh->RemoveElement(theFace);
8306 SMESHDS_Mesh *aMesh = GetMeshDS();
8307 if( !theFace->IsQuadratic() ) {
8309 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8310 int nbLinkNodes = 2 + aNodesToInsert.size();
8311 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8312 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8313 linkNodes[ 0 ] = nodes[ il1 ];
8314 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8315 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8316 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8317 linkNodes[ iNode++ ] = *nIt;
8319 // decide how to split a quadrangle: compare possible variants
8320 // and choose which of splits to be a quadrangle
8321 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8322 if ( nbFaceNodes == 3 ) {
8323 iBestQuad = nbSplits;
8326 else if ( nbFaceNodes == 4 ) {
8327 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8328 double aBestRate = DBL_MAX;
8329 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8331 double aBadRate = 0;
8332 // evaluate elements quality
8333 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8334 if ( iSplit == iQuad ) {
8335 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8339 aBadRate += getBadRate( &quad, aCrit );
8342 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8344 nodes[ iSplit < iQuad ? i4 : i3 ]);
8345 aBadRate += getBadRate( &tria, aCrit );
8349 if ( aBadRate < aBestRate ) {
8351 aBestRate = aBadRate;
8356 // create new elements
8357 int aShapeId = FindShape( theFace );
8360 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8361 SMDS_MeshElement* newElem = 0;
8362 if ( iSplit == iBestQuad )
8363 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8368 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8370 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8371 myLastCreatedElems.Append(newElem);
8372 if ( aShapeId && newElem )
8373 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8376 // change nodes of theFace
8377 const SMDS_MeshNode* newNodes[ 4 ];
8378 newNodes[ 0 ] = linkNodes[ i1 ];
8379 newNodes[ 1 ] = linkNodes[ i2 ];
8380 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8381 newNodes[ 3 ] = nodes[ i4 ];
8382 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8383 const SMDS_MeshElement* newElem = 0;
8384 if (iSplit == iBestQuad)
8385 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8387 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8388 myLastCreatedElems.Append(newElem);
8389 if ( aShapeId && newElem )
8390 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8391 } // end if(!theFace->IsQuadratic())
8392 else { // theFace is quadratic
8393 // we have to split theFace on simple triangles and one simple quadrangle
8395 int nbshift = tmp*2;
8396 // shift nodes in nodes[] by nbshift
8398 for(i=0; i<nbshift; i++) {
8399 const SMDS_MeshNode* n = nodes[0];
8400 for(j=0; j<nbFaceNodes-1; j++) {
8401 nodes[j] = nodes[j+1];
8403 nodes[nbFaceNodes-1] = n;
8405 il1 = il1 - nbshift;
8406 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8407 // n0 n1 n2 n0 n1 n2
8408 // +-----+-----+ +-----+-----+
8417 // create new elements
8418 int aShapeId = FindShape( theFace );
8421 if(nbFaceNodes==6) { // quadratic triangle
8422 SMDS_MeshElement* newElem =
8423 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8424 myLastCreatedElems.Append(newElem);
8425 if ( aShapeId && newElem )
8426 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8427 if(theFace->IsMediumNode(nodes[il1])) {
8428 // create quadrangle
8429 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8430 myLastCreatedElems.Append(newElem);
8431 if ( aShapeId && newElem )
8432 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8438 // create quadrangle
8439 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8440 myLastCreatedElems.Append(newElem);
8441 if ( aShapeId && newElem )
8442 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8448 else { // nbFaceNodes==8 - quadratic quadrangle
8449 SMDS_MeshElement* newElem =
8450 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8451 myLastCreatedElems.Append(newElem);
8452 if ( aShapeId && newElem )
8453 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8454 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8455 myLastCreatedElems.Append(newElem);
8456 if ( aShapeId && newElem )
8457 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8458 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8459 myLastCreatedElems.Append(newElem);
8460 if ( aShapeId && newElem )
8461 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8462 if(theFace->IsMediumNode(nodes[il1])) {
8463 // create quadrangle
8464 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8465 myLastCreatedElems.Append(newElem);
8466 if ( aShapeId && newElem )
8467 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8473 // create quadrangle
8474 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8475 myLastCreatedElems.Append(newElem);
8476 if ( aShapeId && newElem )
8477 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8483 // create needed triangles using n1,n2,n3 and inserted nodes
8484 int nbn = 2 + aNodesToInsert.size();
8485 //const SMDS_MeshNode* aNodes[nbn];
8486 vector<const SMDS_MeshNode*> aNodes(nbn);
8487 aNodes[0] = nodes[n1];
8488 aNodes[nbn-1] = nodes[n2];
8489 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8490 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8491 aNodes[iNode++] = *nIt;
8493 for(i=1; i<nbn; i++) {
8494 SMDS_MeshElement* newElem =
8495 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8496 myLastCreatedElems.Append(newElem);
8497 if ( aShapeId && newElem )
8498 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8502 aMesh->RemoveElement(theFace);
8505 //=======================================================================
8506 //function : UpdateVolumes
8508 //=======================================================================
8509 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8510 const SMDS_MeshNode* theBetweenNode2,
8511 list<const SMDS_MeshNode*>& theNodesToInsert)
8513 myLastCreatedElems.Clear();
8514 myLastCreatedNodes.Clear();
8516 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8517 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8518 const SMDS_MeshElement* elem = invElemIt->next();
8520 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8521 SMDS_VolumeTool aVolume (elem);
8522 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8525 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8526 int iface, nbFaces = aVolume.NbFaces();
8527 vector<const SMDS_MeshNode *> poly_nodes;
8528 vector<int> quantities (nbFaces);
8530 for (iface = 0; iface < nbFaces; iface++) {
8531 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8532 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8533 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8535 for (int inode = 0; inode < nbFaceNodes; inode++) {
8536 poly_nodes.push_back(faceNodes[inode]);
8538 if (nbInserted == 0) {
8539 if (faceNodes[inode] == theBetweenNode1) {
8540 if (faceNodes[inode + 1] == theBetweenNode2) {
8541 nbInserted = theNodesToInsert.size();
8543 // add nodes to insert
8544 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8545 for (; nIt != theNodesToInsert.end(); nIt++) {
8546 poly_nodes.push_back(*nIt);
8550 else if (faceNodes[inode] == theBetweenNode2) {
8551 if (faceNodes[inode + 1] == theBetweenNode1) {
8552 nbInserted = theNodesToInsert.size();
8554 // add nodes to insert in reversed order
8555 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8557 for (; nIt != theNodesToInsert.begin(); nIt--) {
8558 poly_nodes.push_back(*nIt);
8560 poly_nodes.push_back(*nIt);
8567 quantities[iface] = nbFaceNodes + nbInserted;
8570 // Replace or update the volume
8571 SMESHDS_Mesh *aMesh = GetMeshDS();
8573 if (elem->IsPoly()) {
8574 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8578 int aShapeId = FindShape( elem );
8580 SMDS_MeshElement* newElem =
8581 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8582 myLastCreatedElems.Append(newElem);
8583 if (aShapeId && newElem)
8584 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8586 aMesh->RemoveElement(elem);
8593 //================================================================================
8595 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8597 //================================================================================
8599 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8600 vector<const SMDS_MeshNode *> & nodes,
8601 vector<int> & nbNodeInFaces )
8604 nbNodeInFaces.clear();
8605 SMDS_VolumeTool vTool ( elem );
8606 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8608 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8609 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8610 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8615 //=======================================================================
8617 * \brief Convert elements contained in a submesh to quadratic
8618 * \return int - nb of checked elements
8620 //=======================================================================
8622 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8623 SMESH_MesherHelper& theHelper,
8624 const bool theForce3d)
8627 if( !theSm ) return nbElem;
8629 vector<int> nbNodeInFaces;
8630 vector<const SMDS_MeshNode *> nodes;
8631 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8632 while(ElemItr->more())
8635 const SMDS_MeshElement* elem = ElemItr->next();
8636 if( !elem ) continue;
8638 // analyse a necessity of conversion
8639 const SMDSAbs_ElementType aType = elem->GetType();
8640 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8642 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8643 bool hasCentralNodes = false;
8644 if ( elem->IsQuadratic() )
8647 switch ( aGeomType ) {
8648 case SMDSEntity_Quad_Triangle:
8649 case SMDSEntity_Quad_Quadrangle:
8650 case SMDSEntity_Quad_Hexa:
8651 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8653 case SMDSEntity_BiQuad_Triangle:
8654 case SMDSEntity_BiQuad_Quadrangle:
8655 case SMDSEntity_TriQuad_Hexa:
8656 alreadyOK = theHelper.GetIsBiQuadratic();
8657 hasCentralNodes = true;
8662 // take into account already present modium nodes
8664 case SMDSAbs_Volume:
8665 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8667 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8669 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8675 // get elem data needed to re-create it
8677 const int id = elem->GetID();
8678 const int nbNodes = elem->NbCornerNodes();
8679 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8680 if ( aGeomType == SMDSEntity_Polyhedra )
8681 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8682 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8683 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8685 // remove a linear element
8686 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8688 // remove central nodes of biquadratic elements (biquad->quad convertion)
8689 if ( hasCentralNodes )
8690 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8691 if ( nodes[i]->NbInverseElements() == 0 )
8692 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8694 const SMDS_MeshElement* NewElem = 0;
8700 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8708 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8711 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8714 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8718 case SMDSAbs_Volume :
8722 case SMDSEntity_Tetra:
8723 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8725 case SMDSEntity_Pyramid:
8726 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8728 case SMDSEntity_Penta:
8729 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8731 case SMDSEntity_Hexa:
8732 case SMDSEntity_Quad_Hexa:
8733 case SMDSEntity_TriQuad_Hexa:
8734 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8735 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8737 case SMDSEntity_Hexagonal_Prism:
8739 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8746 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8747 if( NewElem && NewElem->getshapeId() < 1 )
8748 theSm->AddElement( NewElem );
8752 //=======================================================================
8753 //function : ConvertToQuadratic
8755 //=======================================================================
8757 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8759 SMESHDS_Mesh* meshDS = GetMeshDS();
8761 SMESH_MesherHelper aHelper(*myMesh);
8763 aHelper.SetIsQuadratic( true );
8764 aHelper.SetIsBiQuadratic( theToBiQuad );
8765 aHelper.SetElementsOnShape(true);
8766 aHelper.ToFixNodeParameters( true );
8768 // convert elements assigned to sub-meshes
8769 int nbCheckedElems = 0;
8770 if ( myMesh->HasShapeToMesh() )
8772 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8774 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8775 while ( smIt->more() ) {
8776 SMESH_subMesh* sm = smIt->next();
8777 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8778 aHelper.SetSubShape( sm->GetSubShape() );
8779 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8785 // convert elements NOT assigned to sub-meshes
8786 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8787 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8789 aHelper.SetElementsOnShape(false);
8790 SMESHDS_SubMesh *smDS = 0;
8793 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8794 while( aEdgeItr->more() )
8796 const SMDS_MeshEdge* edge = aEdgeItr->next();
8797 if ( !edge->IsQuadratic() )
8799 int id = edge->GetID();
8800 const SMDS_MeshNode* n1 = edge->GetNode(0);
8801 const SMDS_MeshNode* n2 = edge->GetNode(1);
8803 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8805 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8806 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8810 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8815 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8816 while( aFaceItr->more() )
8818 const SMDS_MeshFace* face = aFaceItr->next();
8819 if ( !face ) continue;
8821 const SMDSAbs_EntityType type = face->GetEntityType();
8825 case SMDSEntity_Quad_Triangle:
8826 case SMDSEntity_Quad_Quadrangle:
8827 alreadyOK = !theToBiQuad;
8828 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8830 case SMDSEntity_BiQuad_Triangle:
8831 case SMDSEntity_BiQuad_Quadrangle:
8832 alreadyOK = theToBiQuad;
8833 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8835 default: alreadyOK = false;
8840 const int id = face->GetID();
8841 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8843 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8845 SMDS_MeshFace * NewFace = 0;
8848 case SMDSEntity_Triangle:
8849 case SMDSEntity_Quad_Triangle:
8850 case SMDSEntity_BiQuad_Triangle:
8851 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8852 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8853 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8856 case SMDSEntity_Quadrangle:
8857 case SMDSEntity_Quad_Quadrangle:
8858 case SMDSEntity_BiQuad_Quadrangle:
8859 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8860 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8861 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8865 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8867 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8871 vector<int> nbNodeInFaces;
8872 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8873 while(aVolumeItr->more())
8875 const SMDS_MeshVolume* volume = aVolumeItr->next();
8876 if ( !volume ) continue;
8878 const SMDSAbs_EntityType type = volume->GetEntityType();
8879 if ( volume->IsQuadratic() )
8884 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8885 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8886 default: alreadyOK = true;
8890 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8894 const int id = volume->GetID();
8895 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8896 if ( type == SMDSEntity_Polyhedra )
8897 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8898 else if ( type == SMDSEntity_Hexagonal_Prism )
8899 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8901 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8903 SMDS_MeshVolume * NewVolume = 0;
8906 case SMDSEntity_Tetra:
8907 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8909 case SMDSEntity_Hexa:
8910 case SMDSEntity_Quad_Hexa:
8911 case SMDSEntity_TriQuad_Hexa:
8912 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8913 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8914 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8915 if ( nodes[i]->NbInverseElements() == 0 )
8916 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8918 case SMDSEntity_Pyramid:
8919 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8920 nodes[3], nodes[4], id, theForce3d);
8922 case SMDSEntity_Penta:
8923 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8924 nodes[3], nodes[4], nodes[5], id, theForce3d);
8926 case SMDSEntity_Hexagonal_Prism:
8928 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8930 ReplaceElemInGroups(volume, NewVolume, meshDS);
8935 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8936 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8937 // aHelper.FixQuadraticElements(myError);
8938 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8942 //================================================================================
8944 * \brief Makes given elements quadratic
8945 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8946 * \param theElements - elements to make quadratic
8948 //================================================================================
8950 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8951 TIDSortedElemSet& theElements,
8952 const bool theToBiQuad)
8954 if ( theElements.empty() ) return;
8956 // we believe that all theElements are of the same type
8957 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8959 // get all nodes shared by theElements
8960 TIDSortedNodeSet allNodes;
8961 TIDSortedElemSet::iterator eIt = theElements.begin();
8962 for ( ; eIt != theElements.end(); ++eIt )
8963 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8965 // complete theElements with elements of lower dim whose all nodes are in allNodes
8967 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8968 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8969 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8970 for ( ; nIt != allNodes.end(); ++nIt )
8972 const SMDS_MeshNode* n = *nIt;
8973 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8974 while ( invIt->more() )
8976 const SMDS_MeshElement* e = invIt->next();
8977 const SMDSAbs_ElementType type = e->GetType();
8978 if ( e->IsQuadratic() )
8980 quadAdjacentElems[ type ].insert( e );
8983 switch ( e->GetEntityType() ) {
8984 case SMDSEntity_Quad_Triangle:
8985 case SMDSEntity_Quad_Quadrangle:
8986 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8987 case SMDSEntity_BiQuad_Triangle:
8988 case SMDSEntity_BiQuad_Quadrangle:
8989 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8990 default: alreadyOK = true;
8995 if ( type >= elemType )
8996 continue; // same type or more complex linear element
8998 if ( !checkedAdjacentElems[ type ].insert( e ).second )
8999 continue; // e is already checked
9003 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9004 while ( nodeIt->more() && allIn )
9005 allIn = allNodes.count( nodeIt->next() );
9007 theElements.insert(e );
9011 SMESH_MesherHelper helper(*myMesh);
9012 helper.SetIsQuadratic( true );
9013 helper.SetIsBiQuadratic( theToBiQuad );
9015 // add links of quadratic adjacent elements to the helper
9017 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9018 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9019 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9021 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9023 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9024 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9025 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9027 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9029 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9030 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9031 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9033 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9036 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9038 SMESHDS_Mesh* meshDS = GetMeshDS();
9039 SMESHDS_SubMesh* smDS = 0;
9040 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9042 const SMDS_MeshElement* elem = *eIt;
9045 int nbCentralNodes = 0;
9046 switch ( elem->GetEntityType() ) {
9047 // linear convertible
9048 case SMDSEntity_Edge:
9049 case SMDSEntity_Triangle:
9050 case SMDSEntity_Quadrangle:
9051 case SMDSEntity_Tetra:
9052 case SMDSEntity_Pyramid:
9053 case SMDSEntity_Hexa:
9054 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9055 // quadratic that can become bi-quadratic
9056 case SMDSEntity_Quad_Triangle:
9057 case SMDSEntity_Quad_Quadrangle:
9058 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9060 case SMDSEntity_BiQuad_Triangle:
9061 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9062 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9064 default: alreadyOK = true;
9066 if ( alreadyOK ) continue;
9068 const SMDSAbs_ElementType type = elem->GetType();
9069 const int id = elem->GetID();
9070 const int nbNodes = elem->NbCornerNodes();
9071 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9073 helper.SetSubShape( elem->getshapeId() );
9075 if ( !smDS || !smDS->Contains( elem ))
9076 smDS = meshDS->MeshElements( elem->getshapeId() );
9077 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9079 SMDS_MeshElement * newElem = 0;
9082 case 4: // cases for most frequently used element types go first (for optimization)
9083 if ( type == SMDSAbs_Volume )
9084 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9086 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9089 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9090 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9093 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9096 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9099 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9100 nodes[4], id, theForce3d);
9103 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9104 nodes[4], nodes[5], id, theForce3d);
9108 ReplaceElemInGroups( elem, newElem, meshDS);
9109 if( newElem && smDS )
9110 smDS->AddElement( newElem );
9112 // remove central nodes
9113 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9114 if ( nodes[i]->NbInverseElements() == 0 )
9115 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9117 } // loop on theElements
9120 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9121 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9122 // helper.FixQuadraticElements( myError );
9123 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9127 //=======================================================================
9129 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9130 * \return int - nb of checked elements
9132 //=======================================================================
9134 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9135 SMDS_ElemIteratorPtr theItr,
9136 const int theShapeID)
9139 SMESHDS_Mesh* meshDS = GetMeshDS();
9141 while( theItr->more() )
9143 const SMDS_MeshElement* elem = theItr->next();
9145 if( elem && elem->IsQuadratic())
9147 int id = elem->GetID();
9148 int nbCornerNodes = elem->NbCornerNodes();
9149 SMDSAbs_ElementType aType = elem->GetType();
9151 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9153 //remove a quadratic element
9154 if ( !theSm || !theSm->Contains( elem ))
9155 theSm = meshDS->MeshElements( elem->getshapeId() );
9156 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9158 // remove medium nodes
9159 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9160 if ( nodes[i]->NbInverseElements() == 0 )
9161 meshDS->RemoveFreeNode( nodes[i], theSm );
9163 // add a linear element
9164 nodes.resize( nbCornerNodes );
9165 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9166 ReplaceElemInGroups(elem, newElem, meshDS);
9167 if( theSm && newElem )
9168 theSm->AddElement( newElem );
9174 //=======================================================================
9175 //function : ConvertFromQuadratic
9177 //=======================================================================
9179 bool SMESH_MeshEditor::ConvertFromQuadratic()
9181 int nbCheckedElems = 0;
9182 if ( myMesh->HasShapeToMesh() )
9184 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9186 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9187 while ( smIt->more() ) {
9188 SMESH_subMesh* sm = smIt->next();
9189 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9190 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9196 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9197 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9199 SMESHDS_SubMesh *aSM = 0;
9200 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9208 //================================================================================
9210 * \brief Return true if all medium nodes of the element are in the node set
9212 //================================================================================
9214 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9216 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9217 if ( !nodeSet.count( elem->GetNode(i) ))
9223 //================================================================================
9225 * \brief Makes given elements linear
9227 //================================================================================
9229 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9231 if ( theElements.empty() ) return;
9233 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9234 set<int> mediumNodeIDs;
9235 TIDSortedElemSet::iterator eIt = theElements.begin();
9236 for ( ; eIt != theElements.end(); ++eIt )
9238 const SMDS_MeshElement* e = *eIt;
9239 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9240 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9243 // replace given elements by linear ones
9244 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9245 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9247 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9248 // except those elements sharing medium nodes of quadratic element whose medium nodes
9249 // are not all in mediumNodeIDs
9251 // get remaining medium nodes
9252 TIDSortedNodeSet mediumNodes;
9253 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9254 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9255 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9256 mediumNodes.insert( mediumNodes.end(), n );
9258 // find more quadratic elements to convert
9259 TIDSortedElemSet moreElemsToConvert;
9260 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9261 for ( ; nIt != mediumNodes.end(); ++nIt )
9263 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9264 while ( invIt->more() )
9266 const SMDS_MeshElement* e = invIt->next();
9267 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9269 // find a more complex element including e and
9270 // whose medium nodes are not in mediumNodes
9271 bool complexFound = false;
9272 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9274 SMDS_ElemIteratorPtr invIt2 =
9275 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9276 while ( invIt2->more() )
9278 const SMDS_MeshElement* eComplex = invIt2->next();
9279 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9281 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9282 if ( nbCommonNodes == e->NbNodes())
9284 complexFound = true;
9285 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9291 if ( !complexFound )
9292 moreElemsToConvert.insert( e );
9296 elemIt = elemSetIterator( moreElemsToConvert );
9297 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9300 //=======================================================================
9301 //function : SewSideElements
9303 //=======================================================================
9305 SMESH_MeshEditor::Sew_Error
9306 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9307 TIDSortedElemSet& theSide2,
9308 const SMDS_MeshNode* theFirstNode1,
9309 const SMDS_MeshNode* theFirstNode2,
9310 const SMDS_MeshNode* theSecondNode1,
9311 const SMDS_MeshNode* theSecondNode2)
9313 myLastCreatedElems.Clear();
9314 myLastCreatedNodes.Clear();
9316 MESSAGE ("::::SewSideElements()");
9317 if ( theSide1.size() != theSide2.size() )
9318 return SEW_DIFF_NB_OF_ELEMENTS;
9320 Sew_Error aResult = SEW_OK;
9322 // 1. Build set of faces representing each side
9323 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9324 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9326 // =======================================================================
9327 // 1. Build set of faces representing each side:
9328 // =======================================================================
9329 // a. build set of nodes belonging to faces
9330 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9331 // c. create temporary faces representing side of volumes if correspondent
9332 // face does not exist
9334 SMESHDS_Mesh* aMesh = GetMeshDS();
9335 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9336 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9337 TIDSortedElemSet faceSet1, faceSet2;
9338 set<const SMDS_MeshElement*> volSet1, volSet2;
9339 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9340 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9341 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9342 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9343 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9344 int iSide, iFace, iNode;
9346 list<const SMDS_MeshElement* > tempFaceList;
9347 for ( iSide = 0; iSide < 2; iSide++ ) {
9348 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9349 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9350 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9351 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9352 set<const SMDS_MeshElement*>::iterator vIt;
9353 TIDSortedElemSet::iterator eIt;
9354 set<const SMDS_MeshNode*>::iterator nIt;
9356 // check that given nodes belong to given elements
9357 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9358 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9359 int firstIndex = -1, secondIndex = -1;
9360 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9361 const SMDS_MeshElement* elem = *eIt;
9362 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9363 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9364 if ( firstIndex > -1 && secondIndex > -1 ) break;
9366 if ( firstIndex < 0 || secondIndex < 0 ) {
9367 // we can simply return until temporary faces created
9368 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9371 // -----------------------------------------------------------
9372 // 1a. Collect nodes of existing faces
9373 // and build set of face nodes in order to detect missing
9374 // faces corresponding to sides of volumes
9375 // -----------------------------------------------------------
9377 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9379 // loop on the given element of a side
9380 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9381 //const SMDS_MeshElement* elem = *eIt;
9382 const SMDS_MeshElement* elem = *eIt;
9383 if ( elem->GetType() == SMDSAbs_Face ) {
9384 faceSet->insert( elem );
9385 set <const SMDS_MeshNode*> faceNodeSet;
9386 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9387 while ( nodeIt->more() ) {
9388 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9389 nodeSet->insert( n );
9390 faceNodeSet.insert( n );
9392 setOfFaceNodeSet.insert( faceNodeSet );
9394 else if ( elem->GetType() == SMDSAbs_Volume )
9395 volSet->insert( elem );
9397 // ------------------------------------------------------------------------------
9398 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9399 // ------------------------------------------------------------------------------
9401 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9402 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9403 while ( fIt->more() ) { // loop on faces sharing a node
9404 const SMDS_MeshElement* f = fIt->next();
9405 if ( faceSet->find( f ) == faceSet->end() ) {
9406 // check if all nodes are in nodeSet and
9407 // complete setOfFaceNodeSet if they are
9408 set <const SMDS_MeshNode*> faceNodeSet;
9409 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9410 bool allInSet = true;
9411 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9412 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9413 if ( nodeSet->find( n ) == nodeSet->end() )
9416 faceNodeSet.insert( n );
9419 faceSet->insert( f );
9420 setOfFaceNodeSet.insert( faceNodeSet );
9426 // -------------------------------------------------------------------------
9427 // 1c. Create temporary faces representing sides of volumes if correspondent
9428 // face does not exist
9429 // -------------------------------------------------------------------------
9431 if ( !volSet->empty() ) {
9432 //int nodeSetSize = nodeSet->size();
9434 // loop on given volumes
9435 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9436 SMDS_VolumeTool vol (*vIt);
9437 // loop on volume faces: find free faces
9438 // --------------------------------------
9439 list<const SMDS_MeshElement* > freeFaceList;
9440 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9441 if ( !vol.IsFreeFace( iFace ))
9443 // check if there is already a face with same nodes in a face set
9444 const SMDS_MeshElement* aFreeFace = 0;
9445 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9446 int nbNodes = vol.NbFaceNodes( iFace );
9447 set <const SMDS_MeshNode*> faceNodeSet;
9448 vol.GetFaceNodes( iFace, faceNodeSet );
9449 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9451 // no such a face is given but it still can exist, check it
9452 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9453 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9456 // create a temporary face
9457 if ( nbNodes == 3 ) {
9458 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9459 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9461 else if ( nbNodes == 4 ) {
9462 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9463 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9466 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9467 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9468 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9471 tempFaceList.push_back( aFreeFace );
9475 freeFaceList.push_back( aFreeFace );
9477 } // loop on faces of a volume
9479 // choose one of several free faces of a volume
9480 // --------------------------------------------
9481 if ( freeFaceList.size() > 1 ) {
9482 // choose a face having max nb of nodes shared by other elems of a side
9483 int maxNbNodes = -1;
9484 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9485 while ( fIt != freeFaceList.end() ) { // loop on free faces
9486 int nbSharedNodes = 0;
9487 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9488 while ( nodeIt->more() ) { // loop on free face nodes
9489 const SMDS_MeshNode* n =
9490 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9491 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9492 while ( invElemIt->more() ) {
9493 const SMDS_MeshElement* e = invElemIt->next();
9494 nbSharedNodes += faceSet->count( e );
9495 nbSharedNodes += elemSet->count( e );
9498 if ( nbSharedNodes > maxNbNodes ) {
9499 maxNbNodes = nbSharedNodes;
9500 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9502 else if ( nbSharedNodes == maxNbNodes ) {
9506 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9509 if ( freeFaceList.size() > 1 )
9511 // could not choose one face, use another way
9512 // choose a face most close to the bary center of the opposite side
9513 gp_XYZ aBC( 0., 0., 0. );
9514 set <const SMDS_MeshNode*> addedNodes;
9515 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9516 eIt = elemSet2->begin();
9517 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9518 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9519 while ( nodeIt->more() ) { // loop on free face nodes
9520 const SMDS_MeshNode* n =
9521 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9522 if ( addedNodes.insert( n ).second )
9523 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9526 aBC /= addedNodes.size();
9527 double minDist = DBL_MAX;
9528 fIt = freeFaceList.begin();
9529 while ( fIt != freeFaceList.end() ) { // loop on free faces
9531 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9532 while ( nodeIt->more() ) { // loop on free face nodes
9533 const SMDS_MeshNode* n =
9534 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9535 gp_XYZ p( n->X(),n->Y(),n->Z() );
9536 dist += ( aBC - p ).SquareModulus();
9538 if ( dist < minDist ) {
9540 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9543 fIt = freeFaceList.erase( fIt++ );
9546 } // choose one of several free faces of a volume
9548 if ( freeFaceList.size() == 1 ) {
9549 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9550 faceSet->insert( aFreeFace );
9551 // complete a node set with nodes of a found free face
9552 // for ( iNode = 0; iNode < ; iNode++ )
9553 // nodeSet->insert( fNodes[ iNode ] );
9556 } // loop on volumes of a side
9558 // // complete a set of faces if new nodes in a nodeSet appeared
9559 // // ----------------------------------------------------------
9560 // if ( nodeSetSize != nodeSet->size() ) {
9561 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9562 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9563 // while ( fIt->more() ) { // loop on faces sharing a node
9564 // const SMDS_MeshElement* f = fIt->next();
9565 // if ( faceSet->find( f ) == faceSet->end() ) {
9566 // // check if all nodes are in nodeSet and
9567 // // complete setOfFaceNodeSet if they are
9568 // set <const SMDS_MeshNode*> faceNodeSet;
9569 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9570 // bool allInSet = true;
9571 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9572 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9573 // if ( nodeSet->find( n ) == nodeSet->end() )
9574 // allInSet = false;
9576 // faceNodeSet.insert( n );
9578 // if ( allInSet ) {
9579 // faceSet->insert( f );
9580 // setOfFaceNodeSet.insert( faceNodeSet );
9586 } // Create temporary faces, if there are volumes given
9589 if ( faceSet1.size() != faceSet2.size() ) {
9590 // delete temporary faces: they are in reverseElements of actual nodes
9591 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9592 // while ( tmpFaceIt->more() )
9593 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9594 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9595 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9596 // aMesh->RemoveElement(*tmpFaceIt);
9597 MESSAGE("Diff nb of faces");
9598 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9601 // ============================================================
9602 // 2. Find nodes to merge:
9603 // bind a node to remove to a node to put instead
9604 // ============================================================
9606 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9607 if ( theFirstNode1 != theFirstNode2 )
9608 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9609 if ( theSecondNode1 != theSecondNode2 )
9610 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9612 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9613 set< long > linkIdSet; // links to process
9614 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9616 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9617 list< NLink > linkList[2];
9618 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9619 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9620 // loop on links in linkList; find faces by links and append links
9621 // of the found faces to linkList
9622 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9623 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9625 NLink link[] = { *linkIt[0], *linkIt[1] };
9626 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9627 if ( !linkIdSet.count( linkID ) )
9630 // by links, find faces in the face sets,
9631 // and find indices of link nodes in the found faces;
9632 // in a face set, there is only one or no face sharing a link
9633 // ---------------------------------------------------------------
9635 const SMDS_MeshElement* face[] = { 0, 0 };
9636 vector<const SMDS_MeshNode*> fnodes[2];
9637 int iLinkNode[2][2];
9638 TIDSortedElemSet avoidSet;
9639 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9640 const SMDS_MeshNode* n1 = link[iSide].first;
9641 const SMDS_MeshNode* n2 = link[iSide].second;
9642 //cout << "Side " << iSide << " ";
9643 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9644 // find a face by two link nodes
9645 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9646 *faceSetPtr[ iSide ], avoidSet,
9647 &iLinkNode[iSide][0],
9648 &iLinkNode[iSide][1] );
9651 //cout << " F " << face[ iSide]->GetID() <<endl;
9652 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9653 // put face nodes to fnodes
9654 if ( face[ iSide ]->IsQuadratic() )
9656 // use interlaced nodes iterator
9657 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9658 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9659 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9660 while ( nIter->more() )
9661 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9665 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9666 face[ iSide ]->end_nodes() );
9668 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9672 // check similarity of elements of the sides
9673 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9674 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9675 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9676 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9679 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9681 break; // do not return because it's necessary to remove tmp faces
9684 // set nodes to merge
9685 // -------------------
9687 if ( face[0] && face[1] ) {
9688 const int nbNodes = face[0]->NbNodes();
9689 if ( nbNodes != face[1]->NbNodes() ) {
9690 MESSAGE("Diff nb of face nodes");
9691 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9692 break; // do not return because it s necessary to remove tmp faces
9694 bool reverse[] = { false, false }; // order of nodes in the link
9695 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9696 // analyse link orientation in faces
9697 int i1 = iLinkNode[ iSide ][ 0 ];
9698 int i2 = iLinkNode[ iSide ][ 1 ];
9699 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9701 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9702 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9703 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9705 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9706 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9709 // add other links of the faces to linkList
9710 // -----------------------------------------
9712 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9713 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9714 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9715 if ( !iter_isnew.second ) { // already in a set: no need to process
9716 linkIdSet.erase( iter_isnew.first );
9718 else // new in set == encountered for the first time: add
9720 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9721 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9722 linkList[0].push_back ( NLink( n1, n2 ));
9723 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9728 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9731 } // loop on link lists
9733 if ( aResult == SEW_OK &&
9734 ( //linkIt[0] != linkList[0].end() ||
9735 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9736 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9737 " " << (faceSetPtr[1]->empty()));
9738 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9741 // ====================================================================
9742 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9743 // ====================================================================
9745 // delete temporary faces
9746 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9747 // while ( tmpFaceIt->more() )
9748 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9749 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9750 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9751 aMesh->RemoveElement(*tmpFaceIt);
9753 if ( aResult != SEW_OK)
9756 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9757 // loop on nodes replacement map
9758 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9759 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9760 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9761 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9762 nodeIDsToRemove.push_back( nToRemove->GetID() );
9763 // loop on elements sharing nToRemove
9764 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9765 while ( invElemIt->more() ) {
9766 const SMDS_MeshElement* e = invElemIt->next();
9767 // get a new suite of nodes: make replacement
9768 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9769 vector< const SMDS_MeshNode*> nodes( nbNodes );
9770 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9771 while ( nIt->more() ) {
9772 const SMDS_MeshNode* n =
9773 static_cast<const SMDS_MeshNode*>( nIt->next() );
9774 nnIt = nReplaceMap.find( n );
9775 if ( nnIt != nReplaceMap.end() ) {
9781 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9782 // elemIDsToRemove.push_back( e->GetID() );
9786 SMDSAbs_ElementType etyp = e->GetType();
9787 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9790 myLastCreatedElems.Append(newElem);
9791 AddToSameGroups(newElem, e, aMesh);
9792 int aShapeId = e->getshapeId();
9795 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9798 aMesh->RemoveElement(e);
9803 Remove( nodeIDsToRemove, true );
9808 //================================================================================
9810 * \brief Find corresponding nodes in two sets of faces
9811 * \param theSide1 - first face set
9812 * \param theSide2 - second first face
9813 * \param theFirstNode1 - a boundary node of set 1
9814 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9815 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9816 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9817 * \param nReplaceMap - output map of corresponding nodes
9818 * \return bool - is a success or not
9820 //================================================================================
9823 //#define DEBUG_MATCHING_NODES
9826 SMESH_MeshEditor::Sew_Error
9827 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9828 set<const SMDS_MeshElement*>& theSide2,
9829 const SMDS_MeshNode* theFirstNode1,
9830 const SMDS_MeshNode* theFirstNode2,
9831 const SMDS_MeshNode* theSecondNode1,
9832 const SMDS_MeshNode* theSecondNode2,
9833 TNodeNodeMap & nReplaceMap)
9835 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9837 nReplaceMap.clear();
9838 if ( theFirstNode1 != theFirstNode2 )
9839 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9840 if ( theSecondNode1 != theSecondNode2 )
9841 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9843 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9844 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9846 list< NLink > linkList[2];
9847 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9848 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9850 // loop on links in linkList; find faces by links and append links
9851 // of the found faces to linkList
9852 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9853 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9854 NLink link[] = { *linkIt[0], *linkIt[1] };
9855 if ( linkSet.find( link[0] ) == linkSet.end() )
9858 // by links, find faces in the face sets,
9859 // and find indices of link nodes in the found faces;
9860 // in a face set, there is only one or no face sharing a link
9861 // ---------------------------------------------------------------
9863 const SMDS_MeshElement* face[] = { 0, 0 };
9864 list<const SMDS_MeshNode*> notLinkNodes[2];
9865 //bool reverse[] = { false, false }; // order of notLinkNodes
9867 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9869 const SMDS_MeshNode* n1 = link[iSide].first;
9870 const SMDS_MeshNode* n2 = link[iSide].second;
9871 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9872 set< const SMDS_MeshElement* > facesOfNode1;
9873 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9875 // during a loop of the first node, we find all faces around n1,
9876 // during a loop of the second node, we find one face sharing both n1 and n2
9877 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9878 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9879 while ( fIt->more() ) { // loop on faces sharing a node
9880 const SMDS_MeshElement* f = fIt->next();
9881 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9882 ! facesOfNode1.insert( f ).second ) // f encounters twice
9884 if ( face[ iSide ] ) {
9885 MESSAGE( "2 faces per link " );
9886 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9889 faceSet->erase( f );
9891 // get not link nodes
9892 int nbN = f->NbNodes();
9893 if ( f->IsQuadratic() )
9895 nbNodes[ iSide ] = nbN;
9896 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9897 int i1 = f->GetNodeIndex( n1 );
9898 int i2 = f->GetNodeIndex( n2 );
9899 int iEnd = nbN, iBeg = -1, iDelta = 1;
9900 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9902 std::swap( iEnd, iBeg ); iDelta = -1;
9907 if ( i == iEnd ) i = iBeg + iDelta;
9908 if ( i == i1 ) break;
9909 nodes.push_back ( f->GetNode( i ) );
9915 // check similarity of elements of the sides
9916 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9917 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9918 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9919 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9922 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9926 // set nodes to merge
9927 // -------------------
9929 if ( face[0] && face[1] ) {
9930 if ( nbNodes[0] != nbNodes[1] ) {
9931 MESSAGE("Diff nb of face nodes");
9932 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9934 #ifdef DEBUG_MATCHING_NODES
9935 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9936 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9937 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9939 int nbN = nbNodes[0];
9941 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9942 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9943 for ( int i = 0 ; i < nbN - 2; ++i ) {
9944 #ifdef DEBUG_MATCHING_NODES
9945 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9947 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9951 // add other links of the face 1 to linkList
9952 // -----------------------------------------
9954 const SMDS_MeshElement* f0 = face[0];
9955 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9956 for ( int i = 0; i < nbN; i++ )
9958 const SMDS_MeshNode* n2 = f0->GetNode( i );
9959 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9960 linkSet.insert( SMESH_TLink( n1, n2 ));
9961 if ( !iter_isnew.second ) { // already in a set: no need to process
9962 linkSet.erase( iter_isnew.first );
9964 else // new in set == encountered for the first time: add
9966 #ifdef DEBUG_MATCHING_NODES
9967 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9968 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9970 linkList[0].push_back ( NLink( n1, n2 ));
9971 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9976 } // loop on link lists
9981 //================================================================================
9983 * \brief Create elements equal (on same nodes) to given ones
9984 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
9985 * elements of the uppest dimension are duplicated.
9987 //================================================================================
9989 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9992 SMESHDS_Mesh* mesh = GetMeshDS();
9994 // get an element type and an iterator over elements
9996 SMDSAbs_ElementType type;
9997 SMDS_ElemIteratorPtr elemIt;
9998 vector< const SMDS_MeshElement* > allElems;
9999 if ( theElements.empty() )
10001 if ( mesh->NbNodes() == 0 )
10003 // get most complex type
10004 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10005 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10006 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10008 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10009 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10014 // put all elements in the vector <allElems>
10015 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10016 elemIt = mesh->elementsIterator( type );
10017 while ( elemIt->more() )
10018 allElems.push_back( elemIt->next());
10019 elemIt = elemSetIterator( allElems );
10023 type = (*theElements.begin())->GetType();
10024 elemIt = elemSetIterator( theElements );
10027 // duplicate elements
10029 if ( type == SMDSAbs_Ball )
10031 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10032 while ( elemIt->more() )
10034 const SMDS_MeshElement* elem = elemIt->next();
10035 if ( elem->GetType() != SMDSAbs_Ball )
10037 if (( elem = mesh->AddBall( elem->GetNode(0),
10038 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10039 myLastCreatedElems.Append( elem );
10044 vector< const SMDS_MeshNode* > nodes;
10045 while ( elemIt->more() )
10047 const SMDS_MeshElement* elem = elemIt->next();
10048 if ( elem->GetType() != type )
10051 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10053 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10055 std::vector<int> quantities =
10056 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10057 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10061 AddElement( nodes, type, elem->IsPoly() );
10062 elem = 0; // myLastCreatedElems is already filled
10065 myLastCreatedElems.Append( elem );
10070 //================================================================================
10072 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10073 \param theElems - the list of elements (edges or faces) to be replicated
10074 The nodes for duplication could be found from these elements
10075 \param theNodesNot - list of nodes to NOT replicate
10076 \param theAffectedElems - the list of elements (cells and edges) to which the
10077 replicated nodes should be associated to.
10078 \return TRUE if operation has been completed successfully, FALSE otherwise
10080 //================================================================================
10082 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10083 const TIDSortedElemSet& theNodesNot,
10084 const TIDSortedElemSet& theAffectedElems )
10086 myLastCreatedElems.Clear();
10087 myLastCreatedNodes.Clear();
10089 if ( theElems.size() == 0 )
10092 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10097 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10098 // duplicate elements and nodes
10099 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10100 // replce nodes by duplications
10101 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10105 //================================================================================
10107 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10108 \param theMeshDS - mesh instance
10109 \param theElems - the elements replicated or modified (nodes should be changed)
10110 \param theNodesNot - nodes to NOT replicate
10111 \param theNodeNodeMap - relation of old node to new created node
10112 \param theIsDoubleElem - flag os to replicate element or modify
10113 \return TRUE if operation has been completed successfully, FALSE otherwise
10115 //================================================================================
10117 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10118 const TIDSortedElemSet& theElems,
10119 const TIDSortedElemSet& theNodesNot,
10120 std::map< const SMDS_MeshNode*,
10121 const SMDS_MeshNode* >& theNodeNodeMap,
10122 const bool theIsDoubleElem )
10124 MESSAGE("doubleNodes");
10125 // iterate on through element and duplicate them (by nodes duplication)
10127 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10128 for ( ; elemItr != theElems.end(); ++elemItr )
10130 const SMDS_MeshElement* anElem = *elemItr;
10134 bool isDuplicate = false;
10135 // duplicate nodes to duplicate element
10136 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10137 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10139 while ( anIter->more() )
10142 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10143 SMDS_MeshNode* aNewNode = aCurrNode;
10144 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10145 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10146 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10149 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10150 theNodeNodeMap[ aCurrNode ] = aNewNode;
10151 myLastCreatedNodes.Append( aNewNode );
10153 isDuplicate |= (aCurrNode != aNewNode);
10154 newNodes[ ind++ ] = aNewNode;
10156 if ( !isDuplicate )
10159 if ( theIsDoubleElem )
10160 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10163 MESSAGE("ChangeElementNodes");
10164 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10171 //================================================================================
10173 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10174 \param theNodes - identifiers of nodes to be doubled
10175 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10176 nodes. If list of element identifiers is empty then nodes are doubled but
10177 they not assigned to elements
10178 \return TRUE if operation has been completed successfully, FALSE otherwise
10180 //================================================================================
10182 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10183 const std::list< int >& theListOfModifiedElems )
10185 MESSAGE("DoubleNodes");
10186 myLastCreatedElems.Clear();
10187 myLastCreatedNodes.Clear();
10189 if ( theListOfNodes.size() == 0 )
10192 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10196 // iterate through nodes and duplicate them
10198 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10200 std::list< int >::const_iterator aNodeIter;
10201 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10203 int aCurr = *aNodeIter;
10204 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10210 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10213 anOldNodeToNewNode[ aNode ] = aNewNode;
10214 myLastCreatedNodes.Append( aNewNode );
10218 // Create map of new nodes for modified elements
10220 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10222 std::list< int >::const_iterator anElemIter;
10223 for ( anElemIter = theListOfModifiedElems.begin();
10224 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10226 int aCurr = *anElemIter;
10227 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10231 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10233 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10235 while ( anIter->more() )
10237 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10238 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10240 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10241 aNodeArr[ ind++ ] = aNewNode;
10244 aNodeArr[ ind++ ] = aCurrNode;
10246 anElemToNodes[ anElem ] = aNodeArr;
10249 // Change nodes of elements
10251 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10252 anElemToNodesIter = anElemToNodes.begin();
10253 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10255 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10256 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10259 MESSAGE("ChangeElementNodes");
10260 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10269 //================================================================================
10271 \brief Check if element located inside shape
10272 \return TRUE if IN or ON shape, FALSE otherwise
10274 //================================================================================
10276 template<class Classifier>
10277 bool isInside(const SMDS_MeshElement* theElem,
10278 Classifier& theClassifier,
10279 const double theTol)
10281 gp_XYZ centerXYZ (0, 0, 0);
10282 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10283 while (aNodeItr->more())
10284 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10286 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10287 theClassifier.Perform(aPnt, theTol);
10288 TopAbs_State aState = theClassifier.State();
10289 return (aState == TopAbs_IN || aState == TopAbs_ON );
10292 //================================================================================
10294 * \brief Classifier of the 3D point on the TopoDS_Face
10295 * with interaface suitable for isInside()
10297 //================================================================================
10299 struct _FaceClassifier
10301 Extrema_ExtPS _extremum;
10302 BRepAdaptor_Surface _surface;
10303 TopAbs_State _state;
10305 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10307 _extremum.Initialize( _surface,
10308 _surface.FirstUParameter(), _surface.LastUParameter(),
10309 _surface.FirstVParameter(), _surface.LastVParameter(),
10310 _surface.Tolerance(), _surface.Tolerance() );
10312 void Perform(const gp_Pnt& aPnt, double theTol)
10314 _state = TopAbs_OUT;
10315 _extremum.Perform(aPnt);
10316 if ( _extremum.IsDone() )
10317 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10318 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10319 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10321 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10324 TopAbs_State State() const
10331 //================================================================================
10333 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10334 This method is the first step of DoubleNodeElemGroupsInRegion.
10335 \param theElems - list of groups of elements (edges or faces) to be replicated
10336 \param theNodesNot - list of groups of nodes not to replicated
10337 \param theShape - shape to detect affected elements (element which geometric center
10338 located on or inside shape). If the shape is null, detection is done on faces orientations
10339 (select elements with a gravity center on the side given by faces normals).
10340 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10341 The replicated nodes should be associated to affected elements.
10342 \return groups of affected elements
10343 \sa DoubleNodeElemGroupsInRegion()
10345 //================================================================================
10347 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10348 const TIDSortedElemSet& theNodesNot,
10349 const TopoDS_Shape& theShape,
10350 TIDSortedElemSet& theAffectedElems)
10352 if ( theShape.IsNull() )
10354 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10355 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10356 std::set<const SMDS_MeshElement*> edgesToCheck;
10357 alreadyCheckedNodes.clear();
10358 alreadyCheckedElems.clear();
10359 edgesToCheck.clear();
10361 // --- iterates on elements to be replicated and get elements by back references from their nodes
10363 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10365 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10367 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10368 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10371 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10372 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10373 std::set<const SMDS_MeshNode*> nodesElem;
10375 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10376 while ( nodeItr->more() )
10378 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10379 nodesElem.insert(aNode);
10381 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10382 for (; nodit != nodesElem.end(); nodit++)
10384 MESSAGE(" noeud ");
10385 const SMDS_MeshNode* aNode = *nodit;
10386 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10388 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10390 alreadyCheckedNodes.insert(aNode);
10391 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10392 while ( backElemItr->more() )
10394 MESSAGE(" backelem ");
10395 const SMDS_MeshElement* curElem = backElemItr->next();
10396 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10398 if (theElems.find(curElem) != theElems.end())
10400 alreadyCheckedElems.insert(curElem);
10401 double x=0, y=0, z=0;
10403 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10404 while ( nodeItr2->more() )
10406 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10407 x += anotherNode->X();
10408 y += anotherNode->Y();
10409 z += anotherNode->Z();
10413 p.SetCoord( x/nb -aNode->X(),
10415 z/nb -aNode->Z() );
10416 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10419 MESSAGE(" --- inserted")
10420 theAffectedElems.insert( curElem );
10422 else if (curElem->GetType() == SMDSAbs_Edge)
10423 edgesToCheck.insert(curElem);
10427 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10428 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10429 for( ; eit != edgesToCheck.end(); eit++)
10431 bool onside = true;
10432 const SMDS_MeshElement* anEdge = *eit;
10433 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10434 while ( nodeItr->more() )
10436 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10437 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10445 MESSAGE(" --- edge onside inserted")
10446 theAffectedElems.insert(anEdge);
10452 const double aTol = Precision::Confusion();
10453 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10454 auto_ptr<_FaceClassifier> aFaceClassifier;
10455 if ( theShape.ShapeType() == TopAbs_SOLID )
10457 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10458 bsc3d->PerformInfinitePoint(aTol);
10460 else if (theShape.ShapeType() == TopAbs_FACE )
10462 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10465 // iterates on indicated elements and get elements by back references from their nodes
10466 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10468 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10470 MESSAGE("element " << ielem++);
10471 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10474 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10475 while ( nodeItr->more() )
10477 MESSAGE(" noeud ");
10478 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10479 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10481 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10482 while ( backElemItr->more() )
10484 MESSAGE(" backelem ");
10485 const SMDS_MeshElement* curElem = backElemItr->next();
10486 if ( curElem && theElems.find(curElem) == theElems.end() &&
10488 isInside( curElem, *bsc3d, aTol ) :
10489 isInside( curElem, *aFaceClassifier, aTol )))
10490 theAffectedElems.insert( curElem );
10498 //================================================================================
10500 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10501 \param theElems - group of of elements (edges or faces) to be replicated
10502 \param theNodesNot - group of nodes not to replicate
10503 \param theShape - shape to detect affected elements (element which geometric center
10504 located on or inside shape).
10505 The replicated nodes should be associated to affected elements.
10506 \return TRUE if operation has been completed successfully, FALSE otherwise
10508 //================================================================================
10510 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10511 const TIDSortedElemSet& theNodesNot,
10512 const TopoDS_Shape& theShape )
10514 if ( theShape.IsNull() )
10517 const double aTol = Precision::Confusion();
10518 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10519 auto_ptr<_FaceClassifier> aFaceClassifier;
10520 if ( theShape.ShapeType() == TopAbs_SOLID )
10522 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10523 bsc3d->PerformInfinitePoint(aTol);
10525 else if (theShape.ShapeType() == TopAbs_FACE )
10527 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10530 // iterates on indicated elements and get elements by back references from their nodes
10531 TIDSortedElemSet anAffected;
10532 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10533 for ( ; elemItr != theElems.end(); ++elemItr )
10535 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10539 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10540 while ( nodeItr->more() )
10542 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10543 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10545 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10546 while ( backElemItr->more() )
10548 const SMDS_MeshElement* curElem = backElemItr->next();
10549 if ( curElem && theElems.find(curElem) == theElems.end() &&
10551 isInside( curElem, *bsc3d, aTol ) :
10552 isInside( curElem, *aFaceClassifier, aTol )))
10553 anAffected.insert( curElem );
10557 return DoubleNodes( theElems, theNodesNot, anAffected );
10561 * \brief compute an oriented angle between two planes defined by four points.
10562 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10563 * @param p0 base of the rotation axe
10564 * @param p1 extremity of the rotation axe
10565 * @param g1 belongs to the first plane
10566 * @param g2 belongs to the second plane
10568 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10570 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10571 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10572 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10573 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10574 gp_Vec vref(p0, p1);
10577 gp_Vec n1 = vref.Crossed(v1);
10578 gp_Vec n2 = vref.Crossed(v2);
10580 return n2.AngleWithRef(n1, vref);
10582 catch ( Standard_Failure ) {
10584 return Max( v1.Magnitude(), v2.Magnitude() );
10588 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10589 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10590 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10591 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10592 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10593 * 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.
10594 * 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.
10595 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10596 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10597 * \param theElems - list of groups of volumes, where a group of volume is a set of
10598 * SMDS_MeshElements sorted by Id.
10599 * \param createJointElems - if TRUE, create the elements
10600 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10601 * the boundary between \a theDomains and the rest mesh
10602 * \return TRUE if operation has been completed successfully, FALSE otherwise
10604 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10605 bool createJointElems,
10606 bool onAllBoundaries)
10608 MESSAGE("----------------------------------------------");
10609 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10610 MESSAGE("----------------------------------------------");
10612 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10613 meshDS->BuildDownWardConnectivity(true);
10615 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10617 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10618 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10619 // build the list of nodes shared by 2 or more domains, with their domain indexes
10621 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10622 std::map<int,int>celldom; // cell vtkId --> domain
10623 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10624 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10625 faceDomains.clear();
10627 cellDomains.clear();
10628 nodeDomains.clear();
10629 std::map<int,int> emptyMap;
10630 std::set<int> emptySet;
10633 MESSAGE(".. Number of domains :"<<theElems.size());
10635 TIDSortedElemSet theRestDomElems;
10636 const int iRestDom = -1;
10637 const int idom0 = onAllBoundaries ? iRestDom : 0;
10638 const int nbDomains = theElems.size();
10640 // Check if the domains do not share an element
10641 for (int idom = 0; idom < nbDomains-1; idom++)
10643 // MESSAGE("... Check of domain #" << idom);
10644 const TIDSortedElemSet& domain = theElems[idom];
10645 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10646 for (; elemItr != domain.end(); ++elemItr)
10648 const SMDS_MeshElement* anElem = *elemItr;
10649 int idombisdeb = idom + 1 ;
10650 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10652 const TIDSortedElemSet& domainbis = theElems[idombis];
10653 if ( domainbis.count(anElem) )
10655 MESSAGE(".... Domain #" << idom);
10656 MESSAGE(".... Domain #" << idombis);
10657 throw SALOME_Exception("The domains are not disjoint.");
10664 for (int idom = 0; idom < nbDomains; idom++)
10667 // --- build a map (face to duplicate --> volume to modify)
10668 // with all the faces shared by 2 domains (group of elements)
10669 // and corresponding volume of this domain, for each shared face.
10670 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10672 MESSAGE("... Neighbors of domain #" << idom);
10673 const TIDSortedElemSet& domain = theElems[idom];
10674 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10675 for (; elemItr != domain.end(); ++elemItr)
10677 const SMDS_MeshElement* anElem = *elemItr;
10680 int vtkId = anElem->getVtkId();
10681 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10682 int neighborsVtkIds[NBMAXNEIGHBORS];
10683 int downIds[NBMAXNEIGHBORS];
10684 unsigned char downTypes[NBMAXNEIGHBORS];
10685 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10686 for (int n = 0; n < nbNeighbors; n++)
10688 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10689 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10690 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10693 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10695 // MESSAGE("Domain " << idombis);
10696 const TIDSortedElemSet& domainbis = theElems[idombis];
10697 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10699 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10701 DownIdType face(downIds[n], downTypes[n]);
10702 if (!faceDomains[face].count(idom))
10704 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10705 celldom[vtkId] = idom;
10706 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10710 theRestDomElems.insert( elem );
10711 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10712 celldom[neighborsVtkIds[n]] = iRestDom;
10720 //MESSAGE("Number of shared faces " << faceDomains.size());
10721 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10723 // --- explore the shared faces domain by domain,
10724 // explore the nodes of the face and see if they belong to a cell in the domain,
10725 // which has only a node or an edge on the border (not a shared face)
10727 for (int idomain = idom0; idomain < nbDomains; idomain++)
10729 //MESSAGE("Domain " << idomain);
10730 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10731 itface = faceDomains.begin();
10732 for (; itface != faceDomains.end(); ++itface)
10734 const std::map<int, int>& domvol = itface->second;
10735 if (!domvol.count(idomain))
10737 DownIdType face = itface->first;
10738 //MESSAGE(" --- face " << face.cellId);
10739 std::set<int> oldNodes;
10741 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10742 std::set<int>::iterator itn = oldNodes.begin();
10743 for (; itn != oldNodes.end(); ++itn)
10746 //MESSAGE(" node " << oldId);
10747 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10748 for (int i=0; i<l.ncells; i++)
10750 int vtkId = l.cells[i];
10751 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10752 if (!domain.count(anElem))
10754 int vtkType = grid->GetCellType(vtkId);
10755 int downId = grid->CellIdToDownId(vtkId);
10758 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10759 continue; // not OK at this stage of the algorithm:
10760 //no cells created after BuildDownWardConnectivity
10762 DownIdType aCell(downId, vtkType);
10763 cellDomains[aCell][idomain] = vtkId;
10764 celldom[vtkId] = idomain;
10765 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10771 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10772 // for each shared face, get the nodes
10773 // for each node, for each domain of the face, create a clone of the node
10775 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10776 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10777 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10779 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10780 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10781 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10783 MESSAGE(".. Duplication of the nodes");
10784 for (int idomain = idom0; idomain < nbDomains; idomain++)
10786 itface = faceDomains.begin();
10787 for (; itface != faceDomains.end(); ++itface)
10789 const std::map<int, int>& domvol = itface->second;
10790 if (!domvol.count(idomain))
10792 DownIdType face = itface->first;
10793 //MESSAGE(" --- face " << face.cellId);
10794 std::set<int> oldNodes;
10796 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10797 std::set<int>::iterator itn = oldNodes.begin();
10798 for (; itn != oldNodes.end(); ++itn)
10801 if (nodeDomains[oldId].empty())
10803 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10804 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10806 std::map<int, int>::const_iterator itdom = domvol.begin();
10807 for (; itdom != domvol.end(); ++itdom)
10809 int idom = itdom->first;
10810 //MESSAGE(" domain " << idom);
10811 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10813 if (nodeDomains[oldId].size() >= 2) // a multiple node
10815 vector<int> orderedDoms;
10816 //MESSAGE("multiple node " << oldId);
10817 if (mutipleNodes.count(oldId))
10818 orderedDoms = mutipleNodes[oldId];
10821 map<int,int>::iterator it = nodeDomains[oldId].begin();
10822 for (; it != nodeDomains[oldId].end(); ++it)
10823 orderedDoms.push_back(it->first);
10825 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10826 //stringstream txt;
10827 //for (int i=0; i<orderedDoms.size(); i++)
10828 // txt << orderedDoms[i] << " ";
10829 //MESSAGE("orderedDoms " << txt.str());
10830 mutipleNodes[oldId] = orderedDoms;
10832 double *coords = grid->GetPoint(oldId);
10833 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10834 int newId = newNode->getVtkId();
10835 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10836 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10843 MESSAGE(".. Creation of elements");
10844 for (int idomain = idom0; idomain < nbDomains; idomain++)
10846 itface = faceDomains.begin();
10847 for (; itface != faceDomains.end(); ++itface)
10849 std::map<int, int> domvol = itface->second;
10850 if (!domvol.count(idomain))
10852 DownIdType face = itface->first;
10853 //MESSAGE(" --- face " << face.cellId);
10854 std::set<int> oldNodes;
10856 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10857 int nbMultipleNodes = 0;
10858 std::set<int>::iterator itn = oldNodes.begin();
10859 for (; itn != oldNodes.end(); ++itn)
10862 if (mutipleNodes.count(oldId))
10865 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10867 //MESSAGE("multiple Nodes detected on a shared face");
10868 int downId = itface->first.cellId;
10869 unsigned char cellType = itface->first.cellType;
10870 // --- shared edge or shared face ?
10871 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10874 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10875 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10876 if (mutipleNodes.count(nodes[i]))
10877 if (!mutipleNodesToFace.count(nodes[i]))
10878 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10880 else // shared face (between two volumes)
10882 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10883 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10884 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10885 for (int ie =0; ie < nbEdges; ie++)
10888 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10889 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10891 vector<int> vn0 = mutipleNodes[nodes[0]];
10892 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10894 for (int i0 = 0; i0 < vn0.size(); i0++)
10895 for (int i1 = 0; i1 < vn1.size(); i1++)
10896 if (vn0[i0] == vn1[i1])
10897 doms.push_back(vn0[i0]);
10898 if (doms.size() >2)
10900 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10901 double *coords = grid->GetPoint(nodes[0]);
10902 gp_Pnt p0(coords[0], coords[1], coords[2]);
10903 coords = grid->GetPoint(nodes[nbNodes - 1]);
10904 gp_Pnt p1(coords[0], coords[1], coords[2]);
10906 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10907 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10908 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10909 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10910 for (int id=0; id < doms.size(); id++)
10912 int idom = doms[id];
10913 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10914 for (int ivol=0; ivol<nbvol; ivol++)
10916 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10917 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10918 if (domain.count(elem))
10920 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10921 domvol[idom] = svol;
10922 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10924 vtkIdType npts = 0;
10925 vtkIdType* pts = 0;
10926 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10927 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10930 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10931 angleDom[idom] = 0;
10935 gp_Pnt g(values[0], values[1], values[2]);
10936 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10937 //MESSAGE(" angle=" << angleDom[idom]);
10943 map<double, int> sortedDom; // sort domains by angle
10944 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10945 sortedDom[ia->second] = ia->first;
10946 vector<int> vnodes;
10948 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10950 vdom.push_back(ib->second);
10951 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10953 for (int ino = 0; ino < nbNodes; ino++)
10954 vnodes.push_back(nodes[ino]);
10955 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10964 // --- iterate on shared faces (volumes to modify, face to extrude)
10965 // get node id's of the face (id SMDS = id VTK)
10966 // create flat element with old and new nodes if requested
10968 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10969 // (domain1 X domain2) = domain1 + MAXINT*domain2
10971 std::map<int, std::map<long,int> > nodeQuadDomains;
10972 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10974 MESSAGE(".. Creation of elements: simple junction");
10975 if (createJointElems)
10978 string joints2DName = "joints2D";
10979 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10980 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10981 string joints3DName = "joints3D";
10982 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10983 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10985 itface = faceDomains.begin();
10986 for (; itface != faceDomains.end(); ++itface)
10988 DownIdType face = itface->first;
10989 std::set<int> oldNodes;
10990 std::set<int>::iterator itn;
10992 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10994 std::map<int, int> domvol = itface->second;
10995 std::map<int, int>::iterator itdom = domvol.begin();
10996 int dom1 = itdom->first;
10997 int vtkVolId = itdom->second;
10999 int dom2 = itdom->first;
11000 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11002 stringstream grpname;
11005 grpname << dom1 << "_" << dom2;
11007 grpname << dom2 << "_" << dom1;
11008 string namegrp = grpname.str();
11009 if (!mapOfJunctionGroups.count(namegrp))
11010 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11011 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11013 sgrp->Add(vol->GetID());
11014 if (vol->GetType() == SMDSAbs_Volume)
11015 joints3DGrp->Add(vol->GetID());
11016 else if (vol->GetType() == SMDSAbs_Face)
11017 joints2DGrp->Add(vol->GetID());
11021 // --- create volumes on multiple domain intersection if requested
11022 // iterate on mutipleNodesToFace
11023 // iterate on edgesMultiDomains
11025 MESSAGE(".. Creation of elements: multiple junction");
11026 if (createJointElems)
11028 // --- iterate on mutipleNodesToFace
11030 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11031 for (; itn != mutipleNodesToFace.end(); ++itn)
11033 int node = itn->first;
11034 vector<int> orderDom = itn->second;
11035 vector<vtkIdType> orderedNodes;
11036 for (int idom = 0; idom <orderDom.size(); idom++)
11037 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11038 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11040 stringstream grpname;
11042 grpname << 0 << "_" << 0;
11044 string namegrp = grpname.str();
11045 if (!mapOfJunctionGroups.count(namegrp))
11046 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11047 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11049 sgrp->Add(face->GetID());
11052 // --- iterate on edgesMultiDomains
11054 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11055 for (; ite != edgesMultiDomains.end(); ++ite)
11057 vector<int> nodes = ite->first;
11058 vector<int> orderDom = ite->second;
11059 vector<vtkIdType> orderedNodes;
11060 if (nodes.size() == 2)
11062 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11063 for (int ino=0; ino < nodes.size(); ino++)
11064 if (orderDom.size() == 3)
11065 for (int idom = 0; idom <orderDom.size(); idom++)
11066 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11068 for (int idom = orderDom.size()-1; idom >=0; idom--)
11069 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11070 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11073 string namegrp = "jointsMultiples";
11074 if (!mapOfJunctionGroups.count(namegrp))
11075 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11076 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11078 sgrp->Add(vol->GetID());
11082 INFOS("Quadratic multiple joints not implemented");
11083 // TODO quadratic nodes
11088 // --- list the explicit faces and edges of the mesh that need to be modified,
11089 // i.e. faces and edges built with one or more duplicated nodes.
11090 // associate these faces or edges to their corresponding domain.
11091 // only the first domain found is kept when a face or edge is shared
11093 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11094 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11095 faceOrEdgeDom.clear();
11098 MESSAGE(".. Modification of elements");
11099 for (int idomain = idom0; idomain < nbDomains; idomain++)
11101 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11102 for (; itnod != nodeDomains.end(); ++itnod)
11104 int oldId = itnod->first;
11105 //MESSAGE(" node " << oldId);
11106 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11107 for (int i = 0; i < l.ncells; i++)
11109 int vtkId = l.cells[i];
11110 int vtkType = grid->GetCellType(vtkId);
11111 int downId = grid->CellIdToDownId(vtkId);
11113 continue; // new cells: not to be modified
11114 DownIdType aCell(downId, vtkType);
11115 int volParents[1000];
11116 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11117 for (int j = 0; j < nbvol; j++)
11118 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11119 if (!feDom.count(vtkId))
11121 feDom[vtkId] = idomain;
11122 faceOrEdgeDom[aCell] = emptyMap;
11123 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11124 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11125 // << " type " << vtkType << " downId " << downId);
11131 // --- iterate on shared faces (volumes to modify, face to extrude)
11132 // get node id's of the face
11133 // replace old nodes by new nodes in volumes, and update inverse connectivity
11135 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11136 for (int m=0; m<3; m++)
11138 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11139 itface = (*amap).begin();
11140 for (; itface != (*amap).end(); ++itface)
11142 DownIdType face = itface->first;
11143 std::set<int> oldNodes;
11144 std::set<int>::iterator itn;
11146 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11147 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11148 std::map<int, int> localClonedNodeIds;
11150 std::map<int, int> domvol = itface->second;
11151 std::map<int, int>::iterator itdom = domvol.begin();
11152 for (; itdom != domvol.end(); ++itdom)
11154 int idom = itdom->first;
11155 int vtkVolId = itdom->second;
11156 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11157 localClonedNodeIds.clear();
11158 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11161 if (nodeDomains[oldId].count(idom))
11163 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11164 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11167 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11172 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11173 grid->BuildLinks();
11181 * \brief Double nodes on some external faces and create flat elements.
11182 * Flat elements are mainly used by some types of mechanic calculations.
11184 * Each group of the list must be constituted of faces.
11185 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11186 * @param theElems - list of groups of faces, where a group of faces is a set of
11187 * SMDS_MeshElements sorted by Id.
11188 * @return TRUE if operation has been completed successfully, FALSE otherwise
11190 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11192 MESSAGE("-------------------------------------------------");
11193 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11194 MESSAGE("-------------------------------------------------");
11196 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11198 // --- For each group of faces
11199 // duplicate the nodes, create a flat element based on the face
11200 // replace the nodes of the faces by their clones
11202 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11203 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11204 clonedNodes.clear();
11205 intermediateNodes.clear();
11206 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11207 mapOfJunctionGroups.clear();
11209 for (int idom = 0; idom < theElems.size(); idom++)
11211 const TIDSortedElemSet& domain = theElems[idom];
11212 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11213 for (; elemItr != domain.end(); ++elemItr)
11215 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11216 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11219 // MESSAGE("aFace=" << aFace->GetID());
11220 bool isQuad = aFace->IsQuadratic();
11221 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11223 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11225 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11226 while (nodeIt->more())
11228 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11229 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11231 ln2.push_back(node);
11233 ln0.push_back(node);
11235 const SMDS_MeshNode* clone = 0;
11236 if (!clonedNodes.count(node))
11238 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11239 clonedNodes[node] = clone;
11242 clone = clonedNodes[node];
11245 ln3.push_back(clone);
11247 ln1.push_back(clone);
11249 const SMDS_MeshNode* inter = 0;
11250 if (isQuad && (!isMedium))
11252 if (!intermediateNodes.count(node))
11254 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11255 intermediateNodes[node] = inter;
11258 inter = intermediateNodes[node];
11259 ln4.push_back(inter);
11263 // --- extrude the face
11265 vector<const SMDS_MeshNode*> ln;
11266 SMDS_MeshVolume* vol = 0;
11267 vtkIdType aType = aFace->GetVtkType();
11271 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11272 // MESSAGE("vol prism " << vol->GetID());
11273 ln.push_back(ln1[0]);
11274 ln.push_back(ln1[1]);
11275 ln.push_back(ln1[2]);
11278 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11279 // MESSAGE("vol hexa " << vol->GetID());
11280 ln.push_back(ln1[0]);
11281 ln.push_back(ln1[1]);
11282 ln.push_back(ln1[2]);
11283 ln.push_back(ln1[3]);
11285 case VTK_QUADRATIC_TRIANGLE:
11286 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11287 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11288 // MESSAGE("vol quad prism " << vol->GetID());
11289 ln.push_back(ln1[0]);
11290 ln.push_back(ln1[1]);
11291 ln.push_back(ln1[2]);
11292 ln.push_back(ln3[0]);
11293 ln.push_back(ln3[1]);
11294 ln.push_back(ln3[2]);
11296 case VTK_QUADRATIC_QUAD:
11297 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11298 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11299 // ln4[0], ln4[1], ln4[2], ln4[3]);
11300 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11301 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11302 ln4[0], ln4[1], ln4[2], ln4[3]);
11303 // MESSAGE("vol quad hexa " << vol->GetID());
11304 ln.push_back(ln1[0]);
11305 ln.push_back(ln1[1]);
11306 ln.push_back(ln1[2]);
11307 ln.push_back(ln1[3]);
11308 ln.push_back(ln3[0]);
11309 ln.push_back(ln3[1]);
11310 ln.push_back(ln3[2]);
11311 ln.push_back(ln3[3]);
11321 stringstream grpname;
11325 string namegrp = grpname.str();
11326 if (!mapOfJunctionGroups.count(namegrp))
11327 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11328 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11330 sgrp->Add(vol->GetID());
11333 // --- modify the face
11335 aFace->ChangeNodes(&ln[0], ln.size());
11342 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11343 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11344 * groups of faces to remove inside the object, (idem edges).
11345 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11347 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11348 const TopoDS_Shape& theShape,
11349 SMESH_NodeSearcher* theNodeSearcher,
11350 const char* groupName,
11351 std::vector<double>& nodesCoords,
11352 std::vector<std::vector<int> >& listOfListOfNodes)
11354 MESSAGE("--------------------------------");
11355 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11356 MESSAGE("--------------------------------");
11358 // --- zone of volumes to remove is given :
11359 // 1 either by a geom shape (one or more vertices) and a radius,
11360 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11361 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11362 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11363 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11364 // defined by it's name.
11366 SMESHDS_GroupBase* groupDS = 0;
11367 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11368 while ( groupIt->more() )
11371 SMESH_Group * group = groupIt->next();
11372 if ( !group ) continue;
11373 groupDS = group->GetGroupDS();
11374 if ( !groupDS || groupDS->IsEmpty() ) continue;
11375 std::string grpName = group->GetName();
11376 //MESSAGE("grpName=" << grpName);
11377 if (grpName == groupName)
11383 bool isNodeGroup = false;
11384 bool isNodeCoords = false;
11387 if (groupDS->GetType() != SMDSAbs_Node)
11389 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11392 if (nodesCoords.size() > 0)
11393 isNodeCoords = true; // a list o nodes given by their coordinates
11394 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11396 // --- define groups to build
11398 int idg; // --- group of SMDS volumes
11399 string grpvName = groupName;
11400 grpvName += "_vol";
11401 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11404 MESSAGE("group not created " << grpvName);
11407 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11409 int idgs; // --- group of SMDS faces on the skin
11410 string grpsName = groupName;
11411 grpsName += "_skin";
11412 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11415 MESSAGE("group not created " << grpsName);
11418 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11420 int idgi; // --- group of SMDS faces internal (several shapes)
11421 string grpiName = groupName;
11422 grpiName += "_internalFaces";
11423 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11426 MESSAGE("group not created " << grpiName);
11429 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11431 int idgei; // --- group of SMDS faces internal (several shapes)
11432 string grpeiName = groupName;
11433 grpeiName += "_internalEdges";
11434 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11437 MESSAGE("group not created " << grpeiName);
11440 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11442 // --- build downward connectivity
11444 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11445 meshDS->BuildDownWardConnectivity(true);
11446 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11448 // --- set of volumes detected inside
11450 std::set<int> setOfInsideVol;
11451 std::set<int> setOfVolToCheck;
11453 std::vector<gp_Pnt> gpnts;
11456 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11458 MESSAGE("group of nodes provided");
11459 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11460 while ( elemIt->more() )
11462 const SMDS_MeshElement* elem = elemIt->next();
11465 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11468 SMDS_MeshElement* vol = 0;
11469 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11470 while (volItr->more())
11472 vol = (SMDS_MeshElement*)volItr->next();
11473 setOfInsideVol.insert(vol->getVtkId());
11474 sgrp->Add(vol->GetID());
11478 else if (isNodeCoords)
11480 MESSAGE("list of nodes coordinates provided");
11483 while (i < nodesCoords.size()-2)
11485 double x = nodesCoords[i++];
11486 double y = nodesCoords[i++];
11487 double z = nodesCoords[i++];
11488 gp_Pnt p = gp_Pnt(x, y ,z);
11489 gpnts.push_back(p);
11490 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11494 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11496 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11497 TopTools_IndexedMapOfShape vertexMap;
11498 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11499 gp_Pnt p = gp_Pnt(0,0,0);
11500 if (vertexMap.Extent() < 1)
11503 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11505 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11506 p = BRep_Tool::Pnt(vertex);
11507 gpnts.push_back(p);
11508 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11512 if (gpnts.size() > 0)
11515 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11517 nodeId = startNode->GetID();
11518 MESSAGE("nodeId " << nodeId);
11520 double radius2 = radius*radius;
11521 MESSAGE("radius2 " << radius2);
11523 // --- volumes on start node
11525 setOfVolToCheck.clear();
11526 SMDS_MeshElement* startVol = 0;
11527 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11528 while (volItr->more())
11530 startVol = (SMDS_MeshElement*)volItr->next();
11531 setOfVolToCheck.insert(startVol->getVtkId());
11533 if (setOfVolToCheck.empty())
11535 MESSAGE("No volumes found");
11539 // --- starting with central volumes then their neighbors, check if they are inside
11540 // or outside the domain, until no more new neighbor volume is inside.
11541 // Fill the group of inside volumes
11543 std::map<int, double> mapOfNodeDistance2;
11544 mapOfNodeDistance2.clear();
11545 std::set<int> setOfOutsideVol;
11546 while (!setOfVolToCheck.empty())
11548 std::set<int>::iterator it = setOfVolToCheck.begin();
11550 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11551 bool volInside = false;
11552 vtkIdType npts = 0;
11553 vtkIdType* pts = 0;
11554 grid->GetCellPoints(vtkId, npts, pts);
11555 for (int i=0; i<npts; i++)
11557 double distance2 = 0;
11558 if (mapOfNodeDistance2.count(pts[i]))
11560 distance2 = mapOfNodeDistance2[pts[i]];
11561 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11565 double *coords = grid->GetPoint(pts[i]);
11566 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11568 for (int j=0; j<gpnts.size(); j++)
11570 double d2 = aPoint.SquareDistance(gpnts[j]);
11571 if (d2 < distance2)
11574 if (distance2 < radius2)
11578 mapOfNodeDistance2[pts[i]] = distance2;
11579 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11581 if (distance2 < radius2)
11583 volInside = true; // one or more nodes inside the domain
11584 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11590 setOfInsideVol.insert(vtkId);
11591 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11592 int neighborsVtkIds[NBMAXNEIGHBORS];
11593 int downIds[NBMAXNEIGHBORS];
11594 unsigned char downTypes[NBMAXNEIGHBORS];
11595 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11596 for (int n = 0; n < nbNeighbors; n++)
11597 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11598 setOfVolToCheck.insert(neighborsVtkIds[n]);
11602 setOfOutsideVol.insert(vtkId);
11603 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11605 setOfVolToCheck.erase(vtkId);
11609 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11610 // If yes, add the volume to the inside set
11612 bool addedInside = true;
11613 std::set<int> setOfVolToReCheck;
11614 while (addedInside)
11616 MESSAGE(" --------------------------- re check");
11617 addedInside = false;
11618 std::set<int>::iterator itv = setOfInsideVol.begin();
11619 for (; itv != setOfInsideVol.end(); ++itv)
11622 int neighborsVtkIds[NBMAXNEIGHBORS];
11623 int downIds[NBMAXNEIGHBORS];
11624 unsigned char downTypes[NBMAXNEIGHBORS];
11625 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11626 for (int n = 0; n < nbNeighbors; n++)
11627 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11628 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11630 setOfVolToCheck = setOfVolToReCheck;
11631 setOfVolToReCheck.clear();
11632 while (!setOfVolToCheck.empty())
11634 std::set<int>::iterator it = setOfVolToCheck.begin();
11636 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11638 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11639 int countInside = 0;
11640 int neighborsVtkIds[NBMAXNEIGHBORS];
11641 int downIds[NBMAXNEIGHBORS];
11642 unsigned char downTypes[NBMAXNEIGHBORS];
11643 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11644 for (int n = 0; n < nbNeighbors; n++)
11645 if (setOfInsideVol.count(neighborsVtkIds[n]))
11647 MESSAGE("countInside " << countInside);
11648 if (countInside > 1)
11650 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11651 setOfInsideVol.insert(vtkId);
11652 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11653 addedInside = true;
11656 setOfVolToReCheck.insert(vtkId);
11658 setOfVolToCheck.erase(vtkId);
11662 // --- map of Downward faces at the boundary, inside the global volume
11663 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11664 // fill group of SMDS faces inside the volume (when several volume shapes)
11665 // fill group of SMDS faces on the skin of the global volume (if skin)
11667 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11668 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11669 std::set<int>::iterator it = setOfInsideVol.begin();
11670 for (; it != setOfInsideVol.end(); ++it)
11673 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11674 int neighborsVtkIds[NBMAXNEIGHBORS];
11675 int downIds[NBMAXNEIGHBORS];
11676 unsigned char downTypes[NBMAXNEIGHBORS];
11677 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11678 for (int n = 0; n < nbNeighbors; n++)
11680 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11681 if (neighborDim == 3)
11683 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11685 DownIdType face(downIds[n], downTypes[n]);
11686 boundaryFaces[face] = vtkId;
11688 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11689 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11690 if (vtkFaceId >= 0)
11692 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11693 // find also the smds edges on this face
11694 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11695 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11696 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11697 for (int i = 0; i < nbEdges; i++)
11699 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11700 if (vtkEdgeId >= 0)
11701 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11705 else if (neighborDim == 2) // skin of the volume
11707 DownIdType face(downIds[n], downTypes[n]);
11708 skinFaces[face] = vtkId;
11709 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11710 if (vtkFaceId >= 0)
11711 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11716 // --- identify the edges constituting the wire of each subshape on the skin
11717 // define polylines with the nodes of edges, equivalent to wires
11718 // project polylines on subshapes, and partition, to get geom faces
11720 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11721 std::set<int> emptySet;
11723 std::set<int> shapeIds;
11725 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11726 while (itelem->more())
11728 const SMDS_MeshElement *elem = itelem->next();
11729 int shapeId = elem->getshapeId();
11730 int vtkId = elem->getVtkId();
11731 if (!shapeIdToVtkIdSet.count(shapeId))
11733 shapeIdToVtkIdSet[shapeId] = emptySet;
11734 shapeIds.insert(shapeId);
11736 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11739 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11740 std::set<DownIdType, DownIdCompare> emptyEdges;
11741 emptyEdges.clear();
11743 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11744 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11746 int shapeId = itShape->first;
11747 MESSAGE(" --- Shape ID --- "<< shapeId);
11748 shapeIdToEdges[shapeId] = emptyEdges;
11750 std::vector<int> nodesEdges;
11752 std::set<int>::iterator its = itShape->second.begin();
11753 for (; its != itShape->second.end(); ++its)
11756 MESSAGE(" " << vtkId);
11757 int neighborsVtkIds[NBMAXNEIGHBORS];
11758 int downIds[NBMAXNEIGHBORS];
11759 unsigned char downTypes[NBMAXNEIGHBORS];
11760 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11761 for (int n = 0; n < nbNeighbors; n++)
11763 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11765 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11766 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11767 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11769 DownIdType edge(downIds[n], downTypes[n]);
11770 if (!shapeIdToEdges[shapeId].count(edge))
11772 shapeIdToEdges[shapeId].insert(edge);
11774 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11775 nodesEdges.push_back(vtkNodeId[0]);
11776 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11777 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11783 std::list<int> order;
11785 if (nodesEdges.size() > 0)
11787 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11788 nodesEdges[0] = -1;
11789 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11790 nodesEdges[1] = -1; // do not reuse this edge
11794 int nodeTofind = order.back(); // try first to push back
11796 for (i = 0; i<nodesEdges.size(); i++)
11797 if (nodesEdges[i] == nodeTofind)
11799 if (i == nodesEdges.size())
11800 found = false; // no follower found on back
11803 if (i%2) // odd ==> use the previous one
11804 if (nodesEdges[i-1] < 0)
11808 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11809 nodesEdges[i-1] = -1;
11811 else // even ==> use the next one
11812 if (nodesEdges[i+1] < 0)
11816 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11817 nodesEdges[i+1] = -1;
11822 // try to push front
11824 nodeTofind = order.front(); // try to push front
11825 for (i = 0; i<nodesEdges.size(); i++)
11826 if (nodesEdges[i] == nodeTofind)
11828 if (i == nodesEdges.size())
11830 found = false; // no predecessor found on front
11833 if (i%2) // odd ==> use the previous one
11834 if (nodesEdges[i-1] < 0)
11838 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11839 nodesEdges[i-1] = -1;
11841 else // even ==> use the next one
11842 if (nodesEdges[i+1] < 0)
11846 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11847 nodesEdges[i+1] = -1;
11853 std::vector<int> nodes;
11854 nodes.push_back(shapeId);
11855 std::list<int>::iterator itl = order.begin();
11856 for (; itl != order.end(); itl++)
11858 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11859 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11861 listOfListOfNodes.push_back(nodes);
11864 // partition geom faces with blocFissure
11865 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11866 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11872 //================================================================================
11874 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11875 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11876 * \return TRUE if operation has been completed successfully, FALSE otherwise
11878 //================================================================================
11880 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11882 // iterates on volume elements and detect all free faces on them
11883 SMESHDS_Mesh* aMesh = GetMeshDS();
11886 //bool res = false;
11887 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11888 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11891 const SMDS_MeshVolume* volume = vIt->next();
11892 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11893 vTool.SetExternalNormal();
11894 //const bool isPoly = volume->IsPoly();
11895 const int iQuad = volume->IsQuadratic();
11896 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11898 if (!vTool.IsFreeFace(iface))
11901 vector<const SMDS_MeshNode *> nodes;
11902 int nbFaceNodes = vTool.NbFaceNodes(iface);
11903 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11905 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11906 nodes.push_back(faceNodes[inode]);
11907 if (iQuad) { // add medium nodes
11908 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11909 nodes.push_back(faceNodes[inode]);
11910 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11911 nodes.push_back(faceNodes[8]);
11913 // add new face based on volume nodes
11914 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11916 continue; // face already exsist
11918 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11922 return ( nbFree==(nbExisted+nbCreated) );
11927 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11929 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11931 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11934 //================================================================================
11936 * \brief Creates missing boundary elements
11937 * \param elements - elements whose boundary is to be checked
11938 * \param dimension - defines type of boundary elements to create
11939 * \param group - a group to store created boundary elements in
11940 * \param targetMesh - a mesh to store created boundary elements in
11941 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11942 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11943 * boundary elements will be copied into the targetMesh
11944 * \param toAddExistingBondary - if true, not only new but also pre-existing
11945 * boundary elements will be added into the new group
11946 * \param aroundElements - if true, elements will be created on boundary of given
11947 * elements else, on boundary of the whole mesh.
11948 * \return nb of added boundary elements
11950 //================================================================================
11952 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11953 Bnd_Dimension dimension,
11954 SMESH_Group* group/*=0*/,
11955 SMESH_Mesh* targetMesh/*=0*/,
11956 bool toCopyElements/*=false*/,
11957 bool toCopyExistingBoundary/*=false*/,
11958 bool toAddExistingBondary/*= false*/,
11959 bool aroundElements/*= false*/)
11961 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11962 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11963 // hope that all elements are of the same type, do not check them all
11964 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11965 throw SALOME_Exception(LOCALIZED("wrong element type"));
11968 toCopyElements = toCopyExistingBoundary = false;
11970 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11971 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11972 int nbAddedBnd = 0;
11974 // editor adding present bnd elements and optionally holding elements to add to the group
11975 SMESH_MeshEditor* presentEditor;
11976 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11977 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11979 SMESH_MesherHelper helper( *myMesh );
11980 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11981 SMDS_VolumeTool vTool;
11982 TIDSortedElemSet avoidSet;
11983 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11986 typedef vector<const SMDS_MeshNode*> TConnectivity;
11988 SMDS_ElemIteratorPtr eIt;
11989 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11990 else eIt = elemSetIterator( elements );
11992 while (eIt->more())
11994 const SMDS_MeshElement* elem = eIt->next();
11995 const int iQuad = elem->IsQuadratic();
11997 // ------------------------------------------------------------------------------------
11998 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11999 // ------------------------------------------------------------------------------------
12000 vector<const SMDS_MeshElement*> presentBndElems;
12001 vector<TConnectivity> missingBndElems;
12002 TConnectivity nodes, elemNodes;
12003 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12005 vTool.SetExternalNormal();
12006 const SMDS_MeshElement* otherVol = 0;
12007 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12009 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12010 ( !aroundElements || elements.count( otherVol )))
12012 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12013 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12014 if ( missType == SMDSAbs_Edge ) // boundary edges
12016 nodes.resize( 2+iQuad );
12017 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12019 for ( int j = 0; j < nodes.size(); ++j )
12021 if ( const SMDS_MeshElement* edge =
12022 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12023 presentBndElems.push_back( edge );
12025 missingBndElems.push_back( nodes );
12028 else // boundary face
12031 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12032 nodes.push_back( nn[inode] ); // add corner nodes
12034 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12035 nodes.push_back( nn[inode] ); // add medium nodes
12036 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12038 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12040 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12041 SMDSAbs_Face, /*noMedium=*/false ))
12042 presentBndElems.push_back( f );
12044 missingBndElems.push_back( nodes );
12046 if ( targetMesh != myMesh )
12048 // add 1D elements on face boundary to be added to a new mesh
12049 const SMDS_MeshElement* edge;
12050 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12053 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12055 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12056 if ( edge && avoidSet.insert( edge ).second )
12057 presentBndElems.push_back( edge );
12063 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12065 avoidSet.clear(), avoidSet.insert( elem );
12066 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12067 SMDS_MeshElement::iterator() );
12068 elemNodes.push_back( elemNodes[0] );
12069 nodes.resize( 2 + iQuad );
12070 const int nbLinks = elem->NbCornerNodes();
12071 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12073 nodes[0] = elemNodes[iN];
12074 nodes[1] = elemNodes[iN+1+iQuad];
12075 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12076 continue; // not free link
12078 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12079 if ( const SMDS_MeshElement* edge =
12080 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12081 presentBndElems.push_back( edge );
12083 missingBndElems.push_back( nodes );
12087 // ---------------------------------
12088 // 2. Add missing boundary elements
12089 // ---------------------------------
12090 if ( targetMesh != myMesh )
12091 // instead of making a map of nodes in this mesh and targetMesh,
12092 // we create nodes with same IDs.
12093 for ( int i = 0; i < missingBndElems.size(); ++i )
12095 TConnectivity& srcNodes = missingBndElems[i];
12096 TConnectivity nodes( srcNodes.size() );
12097 for ( inode = 0; inode < nodes.size(); ++inode )
12098 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12099 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12101 /*noMedium=*/false))
12103 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12107 for ( int i = 0; i < missingBndElems.size(); ++i )
12109 TConnectivity& nodes = missingBndElems[i];
12110 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12112 /*noMedium=*/false))
12114 SMDS_MeshElement* elem =
12115 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12118 // try to set a new element to a shape
12119 if ( myMesh->HasShapeToMesh() )
12122 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12123 const int nbN = nodes.size() / (iQuad+1 );
12124 for ( inode = 0; inode < nbN && ok; ++inode )
12126 pair<int, TopAbs_ShapeEnum> i_stype =
12127 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12128 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12129 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12131 if ( ok && mediumShapes.size() > 1 )
12133 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12134 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12135 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12137 if (( ok = ( stype_i->first != stype_i_0.first )))
12138 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12139 aMesh->IndexToShape( stype_i_0.second ));
12142 if ( ok && mediumShapes.begin()->first == missShapeType )
12143 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12147 // ----------------------------------
12148 // 3. Copy present boundary elements
12149 // ----------------------------------
12150 if ( toCopyExistingBoundary )
12151 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12153 const SMDS_MeshElement* e = presentBndElems[i];
12154 TConnectivity nodes( e->NbNodes() );
12155 for ( inode = 0; inode < nodes.size(); ++inode )
12156 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12157 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12159 else // store present elements to add them to a group
12160 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12162 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12165 } // loop on given elements
12167 // ---------------------------------------------
12168 // 4. Fill group with boundary elements
12169 // ---------------------------------------------
12172 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12173 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12174 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12176 tgtEditor.myLastCreatedElems.Clear();
12177 tgtEditor2.myLastCreatedElems.Clear();
12179 // -----------------------
12180 // 5. Copy given elements
12181 // -----------------------
12182 if ( toCopyElements && targetMesh != myMesh )
12184 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12185 else eIt = elemSetIterator( elements );
12186 while (eIt->more())
12188 const SMDS_MeshElement* elem = eIt->next();
12189 TConnectivity nodes( elem->NbNodes() );
12190 for ( inode = 0; inode < nodes.size(); ++inode )
12191 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12192 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12194 tgtEditor.myLastCreatedElems.Clear();