1 // Copyright (C) 2007-2013 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.
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;
106 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Clears myLastCreatedNodes and myLastCreatedElems
122 //================================================================================
124 void SMESH_MeshEditor::CrearLastCreated()
126 myLastCreatedNodes.Clear();
127 myLastCreatedElems.Clear();
131 //=======================================================================
135 //=======================================================================
138 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
139 const SMDSAbs_ElementType type,
142 const double ballDiameter)
144 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
145 SMDS_MeshElement* e = 0;
146 int nbnode = node.size();
147 SMESHDS_Mesh* mesh = GetMeshDS();
152 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
153 else e = mesh->AddFace (node[0], node[1], node[2] );
155 else if (nbnode == 4) {
156 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
157 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
159 else if (nbnode == 6) {
160 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
161 node[4], node[5], ID);
162 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
165 else if (nbnode == 7) {
166 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
167 node[4], node[5], node[6], ID);
168 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
169 node[4], node[5], node[6] );
171 else if (nbnode == 8) {
172 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
173 node[4], node[5], node[6], node[7], ID);
174 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
175 node[4], node[5], node[6], node[7] );
177 else if (nbnode == 9) {
178 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
179 node[4], node[5], node[6], node[7], node[8], ID);
180 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
181 node[4], node[5], node[6], node[7], node[8] );
184 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
185 else e = mesh->AddPolygonalFace (node );
192 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
193 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
195 else if (nbnode == 5) {
196 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
198 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
207 else if (nbnode == 8) {
208 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], node[7], ID);
210 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6], node[7] );
213 else if (nbnode == 10) {
214 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7],
216 node[8], node[9], ID);
217 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7],
221 else if (nbnode == 12) {
222 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7],
224 node[8], node[9], node[10], node[11], ID);
225 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7],
227 node[8], node[9], node[10], node[11] );
229 else if (nbnode == 13) {
230 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
231 node[4], node[5], node[6], node[7],
232 node[8], node[9], node[10],node[11],
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],
239 else if (nbnode == 15) {
240 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241 node[4], node[5], node[6], node[7],
242 node[8], node[9], node[10],node[11],
243 node[12],node[13],node[14],ID);
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
245 node[4], node[5], node[6], node[7],
246 node[8], node[9], node[10],node[11],
247 node[12],node[13],node[14] );
249 else if (nbnode == 20) {
250 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251 node[4], node[5], node[6], node[7],
252 node[8], node[9], node[10],node[11],
253 node[12],node[13],node[14],node[15],
254 node[16],node[17],node[18],node[19],ID);
255 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 node[4], node[5], node[6], node[7],
257 node[8], node[9], node[10],node[11],
258 node[12],node[13],node[14],node[15],
259 node[16],node[17],node[18],node[19] );
261 else if (nbnode == 27) {
262 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7],
264 node[8], node[9], node[10],node[11],
265 node[12],node[13],node[14],node[15],
266 node[16],node[17],node[18],node[19],
267 node[20],node[21],node[22],node[23],
268 node[24],node[25],node[26], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
271 node[8], node[9], node[10],node[11],
272 node[12],node[13],node[14],node[15],
273 node[16],node[17],node[18],node[19],
274 node[20],node[21],node[22],node[23],
275 node[24],node[25],node[26] );
282 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
283 else e = mesh->AddEdge (node[0], node[1] );
285 else if ( nbnode == 3 ) {
286 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
287 else e = mesh->AddEdge (node[0], node[1], node[2] );
291 case SMDSAbs_0DElement:
293 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
294 else e = mesh->Add0DElement (node[0] );
299 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
300 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
304 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
305 else e = mesh->AddBall (node[0], ballDiameter);
310 if ( e ) myLastCreatedElems.Append( e );
314 //=======================================================================
318 //=======================================================================
320 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
321 const SMDSAbs_ElementType type,
325 vector<const SMDS_MeshNode*> nodes;
326 nodes.reserve( nodeIDs.size() );
327 vector<int>::const_iterator id = nodeIDs.begin();
328 while ( id != nodeIDs.end() ) {
329 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
330 nodes.push_back( node );
334 return AddElement( nodes, type, isPoly, ID );
337 //=======================================================================
339 //purpose : Remove a node or an element.
340 // Modify a compute state of sub-meshes which become empty
341 //=======================================================================
343 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
346 myLastCreatedElems.Clear();
347 myLastCreatedNodes.Clear();
349 SMESHDS_Mesh* aMesh = GetMeshDS();
350 set< SMESH_subMesh *> smmap;
353 list<int>::const_iterator it = theIDs.begin();
354 for ( ; it != theIDs.end(); it++ ) {
355 const SMDS_MeshElement * elem;
357 elem = aMesh->FindNode( *it );
359 elem = aMesh->FindElement( *it );
363 // Notify VERTEX sub-meshes about modification
365 const SMDS_MeshNode* node = cast2Node( elem );
366 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
367 if ( int aShapeID = node->getshapeId() )
368 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
371 // Find sub-meshes to notify about modification
372 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
373 // while ( nodeIt->more() ) {
374 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
375 // const SMDS_PositionPtr& aPosition = node->GetPosition();
376 // if ( aPosition.get() ) {
377 // if ( int aShapeID = aPosition->GetShapeId() ) {
378 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
379 // smmap.insert( sm );
386 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
388 aMesh->RemoveElement( elem );
392 // Notify sub-meshes about modification
393 if ( !smmap.empty() ) {
394 set< SMESH_subMesh *>::iterator smIt;
395 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
396 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
399 // // Check if the whole mesh becomes empty
400 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
401 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
406 //================================================================================
408 * \brief Create 0D elements on all nodes of the given object except those
409 * nodes on which a 0D element already exists.
410 * \param elements - Elements on whose nodes to create 0D elements; if empty,
411 * the all mesh is treated
412 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
414 //================================================================================
416 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
417 TIDSortedElemSet& all0DElems )
419 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::const_iterator> TSetIterator;
420 SMDS_ElemIteratorPtr elemIt;
421 if ( elements.empty() )
422 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
424 elemIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
426 while ( elemIt->more() )
428 const SMDS_MeshElement* e = elemIt->next();
429 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
430 while ( nodeIt->more() )
432 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
433 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
435 all0DElems.insert( it0D->next() );
437 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
438 all0DElems.insert( myLastCreatedElems.Last() );
444 //=======================================================================
445 //function : FindShape
446 //purpose : Return an index of the shape theElem is on
447 // or zero if a shape not found
448 //=======================================================================
450 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
452 myLastCreatedElems.Clear();
453 myLastCreatedNodes.Clear();
455 SMESHDS_Mesh * aMesh = GetMeshDS();
456 if ( aMesh->ShapeToMesh().IsNull() )
459 int aShapeID = theElem->getshapeId();
463 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
464 if ( sm->Contains( theElem ))
467 if ( theElem->GetType() == SMDSAbs_Node ) {
468 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
471 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
474 TopoDS_Shape aShape; // the shape a node of theElem is on
475 if ( theElem->GetType() != SMDSAbs_Node )
477 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
478 while ( nodeIt->more() ) {
479 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
480 if ((aShapeID = node->getshapeId()) > 0) {
481 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
482 if ( sm->Contains( theElem ))
484 if ( aShape.IsNull() )
485 aShape = aMesh->IndexToShape( aShapeID );
491 // None of nodes is on a proper shape,
492 // find the shape among ancestors of aShape on which a node is
493 if ( !aShape.IsNull() ) {
494 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
495 for ( ; ancIt.More(); ancIt.Next() ) {
496 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
497 if ( sm && sm->Contains( theElem ))
498 return aMesh->ShapeToIndex( ancIt.Value() );
503 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
504 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
505 for ( ; id_sm != id2sm.end(); ++id_sm )
506 if ( id_sm->second->Contains( theElem ))
510 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
514 //=======================================================================
515 //function : IsMedium
517 //=======================================================================
519 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
520 const SMDSAbs_ElementType typeToCheck)
522 bool isMedium = false;
523 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
524 while (it->more() && !isMedium ) {
525 const SMDS_MeshElement* elem = it->next();
526 isMedium = elem->IsMediumNode(node);
531 //=======================================================================
532 //function : shiftNodesQuadTria
533 //purpose : Shift nodes in the array corresponded to quadratic triangle
534 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
535 //=======================================================================
537 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
539 const SMDS_MeshNode* nd1 = aNodes[0];
540 aNodes[0] = aNodes[1];
541 aNodes[1] = aNodes[2];
543 const SMDS_MeshNode* nd2 = aNodes[3];
544 aNodes[3] = aNodes[4];
545 aNodes[4] = aNodes[5];
549 //=======================================================================
550 //function : nbEdgeConnectivity
551 //purpose : return number of the edges connected with the theNode.
552 // if theEdges has connections with the other type of the
553 // elements, return -1
554 //=======================================================================
556 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
558 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
560 // while(elemIt->more()) {
565 return theNode->NbInverseElements();
568 //=======================================================================
569 //function : getNodesFromTwoTria
571 //=======================================================================
573 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
574 const SMDS_MeshElement * theTria2,
575 vector< const SMDS_MeshNode*>& N1,
576 vector< const SMDS_MeshNode*>& N2)
578 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
579 if ( N1.size() < 6 ) return false;
580 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
581 if ( N2.size() < 6 ) return false;
583 int sames[3] = {-1,-1,-1};
595 if(nbsames!=2) return false;
597 shiftNodesQuadTria(N1);
599 shiftNodesQuadTria(N1);
602 i = sames[0] + sames[1] + sames[2];
604 shiftNodesQuadTria(N2);
606 // now we receive following N1 and N2 (using numeration as in the image below)
607 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
608 // i.e. first nodes from both arrays form a new diagonal
612 //=======================================================================
613 //function : InverseDiag
614 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
615 // but having other common link.
616 // Return False if args are improper
617 //=======================================================================
619 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
620 const SMDS_MeshElement * theTria2 )
622 MESSAGE("InverseDiag");
623 myLastCreatedElems.Clear();
624 myLastCreatedNodes.Clear();
626 if (!theTria1 || !theTria2)
629 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
630 if (!F1) return false;
631 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
632 if (!F2) return false;
633 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
634 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
636 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
637 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
641 // put nodes in array and find out indices of the same ones
642 const SMDS_MeshNode* aNodes [6];
643 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
645 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
646 while ( it->more() ) {
647 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
649 if ( i > 2 ) // theTria2
650 // find same node of theTria1
651 for ( int j = 0; j < 3; j++ )
652 if ( aNodes[ i ] == aNodes[ j ]) {
661 return false; // theTria1 is not a triangle
662 it = theTria2->nodesIterator();
664 if ( i == 6 && it->more() )
665 return false; // theTria2 is not a triangle
668 // find indices of 1,2 and of A,B in theTria1
669 int iA = 0, iB = 0, i1 = 0, i2 = 0;
670 for ( i = 0; i < 6; i++ ) {
671 if ( sameInd [ i ] == 0 ) {
680 // nodes 1 and 2 should not be the same
681 if ( aNodes[ i1 ] == aNodes[ i2 ] )
685 aNodes[ iA ] = aNodes[ i2 ];
687 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
689 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
690 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
694 } // end if(F1 && F2)
696 // check case of quadratic faces
697 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
698 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
700 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
701 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
705 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
706 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
714 vector< const SMDS_MeshNode* > N1;
715 vector< const SMDS_MeshNode* > N2;
716 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
718 // now we receive following N1 and N2 (using numeration as above image)
719 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
720 // i.e. first nodes from both arrays determ new diagonal
722 vector< const SMDS_MeshNode*> N1new( N1.size() );
723 vector< const SMDS_MeshNode*> N2new( N2.size() );
724 N1new.back() = N1.back(); // central node of biquadratic
725 N2new.back() = N2.back();
726 N1new[0] = N1[0]; N2new[0] = N1[0];
727 N1new[1] = N2[0]; N2new[1] = N1[1];
728 N1new[2] = N2[1]; N2new[2] = N2[0];
729 N1new[3] = N1[4]; N2new[3] = N1[3];
730 N1new[4] = N2[3]; N2new[4] = N2[5];
731 N1new[5] = N1[5]; N2new[5] = N1[4];
732 // change nodes in faces
733 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
734 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
736 // move the central node of biquadratic triangle
737 SMESH_MesherHelper helper( *GetMesh() );
738 for ( int is2nd = 0; is2nd < 2; ++is2nd )
740 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
741 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
742 if ( nodes.size() < 7 )
744 helper.SetSubShape( tria->getshapeId() );
745 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
749 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
750 SMESH_TNodeXYZ( nodes[4] ) +
751 SMESH_TNodeXYZ( nodes[5] )) / 3.;
756 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
757 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
758 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
760 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
761 xyz = S->Value( uv.X(), uv.Y() );
762 xyz.Transform( loc );
763 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
764 nodes[6]->getshapeId() > 0 )
765 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
767 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
772 //=======================================================================
773 //function : findTriangles
774 //purpose : find triangles sharing theNode1-theNode2 link
775 //=======================================================================
777 static bool findTriangles(const SMDS_MeshNode * theNode1,
778 const SMDS_MeshNode * theNode2,
779 const SMDS_MeshElement*& theTria1,
780 const SMDS_MeshElement*& theTria2)
782 if ( !theNode1 || !theNode2 ) return false;
784 theTria1 = theTria2 = 0;
786 set< const SMDS_MeshElement* > emap;
787 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
789 const SMDS_MeshElement* elem = it->next();
790 if ( elem->NbCornerNodes() == 3 )
793 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
795 const SMDS_MeshElement* elem = it->next();
796 if ( emap.count( elem )) {
804 // theTria1 must be element with minimum ID
805 if ( theTria2->GetID() < theTria1->GetID() )
806 std::swap( theTria2, theTria1 );
814 //=======================================================================
815 //function : InverseDiag
816 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
817 // with ones built on the same 4 nodes but having other common link.
818 // Return false if proper faces not found
819 //=======================================================================
821 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
822 const SMDS_MeshNode * theNode2)
824 myLastCreatedElems.Clear();
825 myLastCreatedNodes.Clear();
827 MESSAGE( "::InverseDiag()" );
829 const SMDS_MeshElement *tr1, *tr2;
830 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
833 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
834 if (!F1) return false;
835 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
836 if (!F2) return false;
837 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
838 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
840 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
841 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
845 // put nodes in array
846 // and find indices of 1,2 and of A in tr1 and of B in tr2
847 int i, iA1 = 0, i1 = 0;
848 const SMDS_MeshNode* aNodes1 [3];
849 SMDS_ElemIteratorPtr it;
850 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
851 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
852 if ( aNodes1[ i ] == theNode1 )
853 iA1 = i; // node A in tr1
854 else if ( aNodes1[ i ] != theNode2 )
858 const SMDS_MeshNode* aNodes2 [3];
859 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
860 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
861 if ( aNodes2[ i ] == theNode2 )
862 iB2 = i; // node B in tr2
863 else if ( aNodes2[ i ] != theNode1 )
867 // nodes 1 and 2 should not be the same
868 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
872 aNodes1[ iA1 ] = aNodes2[ i2 ];
874 aNodes2[ iB2 ] = aNodes1[ i1 ];
876 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
877 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
882 // check case of quadratic faces
883 return InverseDiag(tr1,tr2);
886 //=======================================================================
887 //function : getQuadrangleNodes
888 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
889 // fusion of triangles tr1 and tr2 having shared link on
890 // theNode1 and theNode2
891 //=======================================================================
893 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
894 const SMDS_MeshNode * theNode1,
895 const SMDS_MeshNode * theNode2,
896 const SMDS_MeshElement * tr1,
897 const SMDS_MeshElement * tr2 )
899 if( tr1->NbNodes() != tr2->NbNodes() )
901 // find the 4-th node to insert into tr1
902 const SMDS_MeshNode* n4 = 0;
903 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
905 while ( !n4 && i<3 ) {
906 const SMDS_MeshNode * n = cast2Node( it->next() );
908 bool isDiag = ( n == theNode1 || n == theNode2 );
912 // Make an array of nodes to be in a quadrangle
913 int iNode = 0, iFirstDiag = -1;
914 it = tr1->nodesIterator();
917 const SMDS_MeshNode * n = cast2Node( it->next() );
919 bool isDiag = ( n == theNode1 || n == theNode2 );
921 if ( iFirstDiag < 0 )
923 else if ( iNode - iFirstDiag == 1 )
924 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
926 else if ( n == n4 ) {
927 return false; // tr1 and tr2 should not have all the same nodes
929 theQuadNodes[ iNode++ ] = n;
931 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
932 theQuadNodes[ iNode ] = n4;
937 //=======================================================================
938 //function : DeleteDiag
939 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
940 // with a quadrangle built on the same 4 nodes.
941 // Return false if proper faces not found
942 //=======================================================================
944 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
945 const SMDS_MeshNode * theNode2)
947 myLastCreatedElems.Clear();
948 myLastCreatedNodes.Clear();
950 MESSAGE( "::DeleteDiag()" );
952 const SMDS_MeshElement *tr1, *tr2;
953 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
956 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
957 if (!F1) return false;
958 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
959 if (!F2) return false;
960 SMESHDS_Mesh * aMesh = GetMeshDS();
962 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
963 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
965 const SMDS_MeshNode* aNodes [ 4 ];
966 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
969 const SMDS_MeshElement* newElem = 0;
970 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
971 myLastCreatedElems.Append(newElem);
972 AddToSameGroups( newElem, tr1, aMesh );
973 int aShapeId = tr1->getshapeId();
976 aMesh->SetMeshElementOnShape( newElem, aShapeId );
978 aMesh->RemoveElement( tr1 );
979 aMesh->RemoveElement( tr2 );
984 // check case of quadratic faces
985 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
987 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
991 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
992 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1000 vector< const SMDS_MeshNode* > N1;
1001 vector< const SMDS_MeshNode* > N2;
1002 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1004 // now we receive following N1 and N2 (using numeration as above image)
1005 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1006 // i.e. first nodes from both arrays determ new diagonal
1008 const SMDS_MeshNode* aNodes[8];
1018 const SMDS_MeshElement* newElem = 0;
1019 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1020 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1021 myLastCreatedElems.Append(newElem);
1022 AddToSameGroups( newElem, tr1, aMesh );
1023 int aShapeId = tr1->getshapeId();
1026 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1028 aMesh->RemoveElement( tr1 );
1029 aMesh->RemoveElement( tr2 );
1031 // remove middle node (9)
1032 GetMeshDS()->RemoveNode( N1[4] );
1037 //=======================================================================
1038 //function : Reorient
1039 //purpose : Reverse theElement orientation
1040 //=======================================================================
1042 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1044 MESSAGE("Reorient");
1045 myLastCreatedElems.Clear();
1046 myLastCreatedNodes.Clear();
1050 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1051 if ( !it || !it->more() )
1054 const SMDSAbs_ElementType type = theElem->GetType();
1055 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1058 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1059 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1061 const SMDS_VtkVolume* aPolyedre =
1062 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1064 MESSAGE("Warning: bad volumic element");
1067 const int nbFaces = aPolyedre->NbFaces();
1068 vector<const SMDS_MeshNode *> poly_nodes;
1069 vector<int> quantities (nbFaces);
1071 // reverse each face of the polyedre
1072 for (int iface = 1; iface <= nbFaces; iface++) {
1073 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1074 quantities[iface - 1] = nbFaceNodes;
1076 for (inode = nbFaceNodes; inode >= 1; inode--) {
1077 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1078 poly_nodes.push_back(curNode);
1081 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1083 else // other elements
1085 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1086 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1087 if ( interlace.empty() )
1089 std::reverse( nodes.begin(), nodes.end() ); // polygon
1091 else if ( interlace.size() > 1 )
1093 SMDS_MeshCell::applyInterlace( interlace, nodes );
1095 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1100 //================================================================================
1102 * \brief Reorient faces.
1103 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1104 * \param theDirection - desired direction of normal of \a theFace
1105 * \param theFace - one of \a theFaces that sould be oriented according to
1106 * \a theDirection and whose orientation defines orientation of other faces
1107 * \return number of reoriented faces.
1109 //================================================================================
1111 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1112 const gp_Dir& theDirection,
1113 const SMDS_MeshElement * theFace)
1116 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1118 if ( theFaces.empty() )
1120 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1121 while ( fIt->more() )
1122 theFaces.insert( theFaces.end(), fIt->next() );
1125 // orient theFace according to theDirection
1127 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1128 if ( normal * theDirection.XYZ() < 0 )
1129 nbReori += Reorient( theFace );
1131 // Orient other faces
1133 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1134 TIDSortedElemSet avoidSet;
1135 set< SMESH_TLink > checkedLinks;
1136 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1138 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1139 theFaces.erase( theFace );
1140 startFaces.insert( theFace );
1142 int nodeInd1, nodeInd2;
1143 const SMDS_MeshElement* otherFace;
1144 vector< const SMDS_MeshElement* > facesNearLink;
1145 vector< std::pair< int, int > > nodeIndsOfFace;
1147 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1148 while ( !startFaces.empty() )
1150 startFace = startFaces.begin();
1151 theFace = *startFace;
1152 startFaces.erase( startFace );
1153 if ( !visitedFaces.insert( theFace ).second )
1157 avoidSet.insert(theFace);
1159 NLink link( theFace->GetNode( 0 ), 0 );
1161 const int nbNodes = theFace->NbCornerNodes();
1162 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1164 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1165 linkIt_isNew = checkedLinks.insert( link );
1166 if ( !linkIt_isNew.second )
1168 // link has already been checked and won't be encountered more
1169 // if the group (theFaces) is manifold
1170 //checkedLinks.erase( linkIt_isNew.first );
1174 facesNearLink.clear();
1175 nodeIndsOfFace.clear();
1176 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1178 &nodeInd1, &nodeInd2 )))
1179 if ( otherFace != theFace)
1181 facesNearLink.push_back( otherFace );
1182 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1183 avoidSet.insert( otherFace );
1185 if ( facesNearLink.size() > 1 )
1187 // NON-MANIFOLD mesh shell !
1188 // select a face most co-directed with theFace,
1189 // other faces won't be visited this time
1191 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1192 double proj, maxProj = -1;
1193 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1194 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1195 if (( proj = Abs( NF * NOF )) > maxProj ) {
1197 otherFace = facesNearLink[i];
1198 nodeInd1 = nodeIndsOfFace[i].first;
1199 nodeInd2 = nodeIndsOfFace[i].second;
1202 // not to visit rejected faces
1203 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1204 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1205 visitedFaces.insert( facesNearLink[i] );
1207 else if ( facesNearLink.size() == 1 )
1209 otherFace = facesNearLink[0];
1210 nodeInd1 = nodeIndsOfFace.back().first;
1211 nodeInd2 = nodeIndsOfFace.back().second;
1213 if ( otherFace && otherFace != theFace)
1215 // link must be reverse in otherFace if orientation ot otherFace
1216 // is same as that of theFace
1217 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1219 nbReori += Reorient( otherFace );
1221 startFaces.insert( otherFace );
1224 std::swap( link.first, link.second ); // reverse the link
1230 //=======================================================================
1231 //function : getBadRate
1233 //=======================================================================
1235 static double getBadRate (const SMDS_MeshElement* theElem,
1236 SMESH::Controls::NumericalFunctorPtr& theCrit)
1238 SMESH::Controls::TSequenceOfXYZ P;
1239 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1241 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1242 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1245 //=======================================================================
1246 //function : QuadToTri
1247 //purpose : Cut quadrangles into triangles.
1248 // theCrit is used to select a diagonal to cut
1249 //=======================================================================
1251 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1252 SMESH::Controls::NumericalFunctorPtr theCrit)
1254 myLastCreatedElems.Clear();
1255 myLastCreatedNodes.Clear();
1257 MESSAGE( "::QuadToTri()" );
1259 if ( !theCrit.get() )
1262 SMESHDS_Mesh * aMesh = GetMeshDS();
1264 Handle(Geom_Surface) surface;
1265 SMESH_MesherHelper helper( *GetMesh() );
1267 TIDSortedElemSet::iterator itElem;
1268 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1270 const SMDS_MeshElement* elem = *itElem;
1271 if ( !elem || elem->GetType() != SMDSAbs_Face )
1273 if ( elem->NbCornerNodes() != 4 )
1276 // retrieve element nodes
1277 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1279 // compare two sets of possible triangles
1280 double aBadRate1, aBadRate2; // to what extent a set is bad
1281 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1282 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1283 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1285 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1286 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1287 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1289 const int aShapeId = FindShape( elem );
1290 const SMDS_MeshElement* newElem1 = 0;
1291 const SMDS_MeshElement* newElem2 = 0;
1293 if ( !elem->IsQuadratic() ) // split liner quadrangle
1295 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1296 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1297 if ( aBadRate1 <= aBadRate2 ) {
1298 // tr1 + tr2 is better
1299 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1300 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1303 // tr3 + tr4 is better
1304 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1305 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1308 else // split quadratic quadrangle
1310 helper.SetIsQuadratic( true );
1311 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1313 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1314 if ( aNodes.size() == 9 )
1316 helper.SetIsBiQuadratic( true );
1317 if ( aBadRate1 <= aBadRate2 )
1318 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1320 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1322 // create a new element
1323 if ( aBadRate1 <= aBadRate2 ) {
1324 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1325 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1328 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1329 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1333 // care of a new element
1335 myLastCreatedElems.Append(newElem1);
1336 myLastCreatedElems.Append(newElem2);
1337 AddToSameGroups( newElem1, elem, aMesh );
1338 AddToSameGroups( newElem2, elem, aMesh );
1340 // put a new triangle on the same shape
1342 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1343 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1345 aMesh->RemoveElement( elem );
1350 //=======================================================================
1351 //function : BestSplit
1352 //purpose : Find better diagonal for cutting.
1353 //=======================================================================
1355 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1356 SMESH::Controls::NumericalFunctorPtr theCrit)
1358 myLastCreatedElems.Clear();
1359 myLastCreatedNodes.Clear();
1364 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1367 if( theQuad->NbNodes()==4 ||
1368 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1370 // retrieve element nodes
1371 const SMDS_MeshNode* aNodes [4];
1372 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1374 //while (itN->more())
1376 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1378 // compare two sets of possible triangles
1379 double aBadRate1, aBadRate2; // to what extent a set is bad
1380 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1381 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1382 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1384 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1385 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1386 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1387 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1388 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1389 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1390 return 1; // diagonal 1-3
1392 return 2; // diagonal 2-4
1399 // Methods of splitting volumes into tetra
1401 const int theHexTo5_1[5*4+1] =
1403 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1405 const int theHexTo5_2[5*4+1] =
1407 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1409 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1411 const int theHexTo6_1[6*4+1] =
1413 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
1415 const int theHexTo6_2[6*4+1] =
1417 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
1419 const int theHexTo6_3[6*4+1] =
1421 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
1423 const int theHexTo6_4[6*4+1] =
1425 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
1427 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1429 const int thePyraTo2_1[2*4+1] =
1431 0, 1, 2, 4, 0, 2, 3, 4, -1
1433 const int thePyraTo2_2[2*4+1] =
1435 1, 2, 3, 4, 1, 3, 0, 4, -1
1437 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1439 const int thePentaTo3_1[3*4+1] =
1441 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1443 const int thePentaTo3_2[3*4+1] =
1445 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1447 const int thePentaTo3_3[3*4+1] =
1449 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1451 const int thePentaTo3_4[3*4+1] =
1453 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1455 const int thePentaTo3_5[3*4+1] =
1457 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1459 const int thePentaTo3_6[3*4+1] =
1461 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1463 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1464 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1466 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1469 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1470 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1471 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1476 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1477 bool _baryNode; //!< additional node is to be created at cell barycenter
1478 bool _ownConn; //!< to delete _connectivity in destructor
1479 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1481 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1482 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1483 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1484 bool hasFacet( const TTriangleFacet& facet ) const
1486 const int* tetConn = _connectivity;
1487 for ( ; tetConn[0] >= 0; tetConn += 4 )
1488 if (( facet.contains( tetConn[0] ) +
1489 facet.contains( tetConn[1] ) +
1490 facet.contains( tetConn[2] ) +
1491 facet.contains( tetConn[3] )) == 3 )
1497 //=======================================================================
1499 * \brief return TSplitMethod for the given element
1501 //=======================================================================
1503 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1505 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1507 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1508 // an edge and a face barycenter; tertaherdons are based on triangles and
1509 // a volume barycenter
1510 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1512 // Find out how adjacent volumes are split
1514 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1515 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1516 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1518 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1519 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1520 if ( nbNodes < 4 ) continue;
1522 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1523 const int* nInd = vol.GetFaceNodesIndices( iF );
1526 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1527 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1528 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1529 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1533 int iCom = 0; // common node of triangle faces to split into
1534 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1536 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1537 nInd[ iQ * ( (iCom+1)%nbNodes )],
1538 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1539 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1540 nInd[ iQ * ( (iCom+2)%nbNodes )],
1541 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1542 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1544 triaSplits.push_back( t012 );
1545 triaSplits.push_back( t023 );
1550 if ( !triaSplits.empty() )
1551 hasAdjacentSplits = true;
1554 // Among variants of split method select one compliant with adjacent volumes
1556 TSplitMethod method;
1557 if ( !vol.Element()->IsPoly() && !is24TetMode )
1559 int nbVariants = 2, nbTet = 0;
1560 const int** connVariants = 0;
1561 switch ( vol.Element()->GetEntityType() )
1563 case SMDSEntity_Hexa:
1564 case SMDSEntity_Quad_Hexa:
1565 case SMDSEntity_TriQuad_Hexa:
1566 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1567 connVariants = theHexTo5, nbTet = 5;
1569 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1571 case SMDSEntity_Pyramid:
1572 case SMDSEntity_Quad_Pyramid:
1573 connVariants = thePyraTo2; nbTet = 2;
1575 case SMDSEntity_Penta:
1576 case SMDSEntity_Quad_Penta:
1577 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1582 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1584 // check method compliancy with adjacent tetras,
1585 // all found splits must be among facets of tetras described by this method
1586 method = TSplitMethod( nbTet, connVariants[variant] );
1587 if ( hasAdjacentSplits && method._nbTetra > 0 )
1589 bool facetCreated = true;
1590 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1592 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1593 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1594 facetCreated = method.hasFacet( *facet );
1596 if ( !facetCreated )
1597 method = TSplitMethod(0); // incompatible method
1601 if ( method._nbTetra < 1 )
1603 // No standard method is applicable, use a generic solution:
1604 // each facet of a volume is split into triangles and
1605 // each of triangles and a volume barycenter form a tetrahedron.
1607 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1609 int* connectivity = new int[ maxTetConnSize + 1 ];
1610 method._connectivity = connectivity;
1611 method._ownConn = true;
1612 method._baryNode = !isHex27; // to create central node or not
1615 int baryCenInd = vol.NbNodes() - int( isHex27 );
1616 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1618 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1619 const int* nInd = vol.GetFaceNodesIndices( iF );
1620 // find common node of triangle facets of tetra to create
1621 int iCommon = 0; // index in linear numeration
1622 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1623 if ( !triaSplits.empty() )
1626 const TTriangleFacet* facet = &triaSplits.front();
1627 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1628 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1629 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1632 else if ( nbNodes > 3 && !is24TetMode )
1634 // find the best method of splitting into triangles by aspect ratio
1635 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1636 map< double, int > badness2iCommon;
1637 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1638 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1639 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1642 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1644 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1645 nodes[ iQ*((iLast-1)%nbNodes)],
1646 nodes[ iQ*((iLast )%nbNodes)]);
1647 badness += getBadRate( &tria, aspectRatio );
1649 badness2iCommon.insert( make_pair( badness, iCommon ));
1651 // use iCommon with lowest badness
1652 iCommon = badness2iCommon.begin()->second;
1654 if ( iCommon >= nbNodes )
1655 iCommon = 0; // something wrong
1657 // fill connectivity of tetrahedra based on a current face
1658 int nbTet = nbNodes - 2;
1659 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1664 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1665 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1669 method._faceBaryNode[ iF ] = 0;
1670 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1673 for ( int i = 0; i < nbTet; ++i )
1675 int i1 = i, i2 = (i+1) % nbNodes;
1676 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1677 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1678 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1679 connectivity[ connSize++ ] = faceBaryCenInd;
1680 connectivity[ connSize++ ] = baryCenInd;
1685 for ( int i = 0; i < nbTet; ++i )
1687 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1688 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1689 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1690 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1691 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1692 connectivity[ connSize++ ] = baryCenInd;
1695 method._nbTetra += nbTet;
1697 } // loop on volume faces
1699 connectivity[ connSize++ ] = -1;
1701 } // end of generic solution
1705 //================================================================================
1707 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1709 //================================================================================
1711 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1713 // find the tetrahedron including the three nodes of facet
1714 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1715 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1716 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1717 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1718 while ( volIt1->more() )
1720 const SMDS_MeshElement* v = volIt1->next();
1721 SMDSAbs_EntityType type = v->GetEntityType();
1722 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1724 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1725 continue; // medium node not allowed
1726 const int ind2 = v->GetNodeIndex( n2 );
1727 if ( ind2 < 0 || 3 < ind2 )
1729 const int ind3 = v->GetNodeIndex( n3 );
1730 if ( ind3 < 0 || 3 < ind3 )
1737 //=======================================================================
1739 * \brief A key of a face of volume
1741 //=======================================================================
1743 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1745 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1747 TIDSortedNodeSet sortedNodes;
1748 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1749 int nbNodes = vol.NbFaceNodes( iF );
1750 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1751 for ( int i = 0; i < nbNodes; i += iQ )
1752 sortedNodes.insert( fNodes[i] );
1753 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1754 first.first = (*(n++))->GetID();
1755 first.second = (*(n++))->GetID();
1756 second.first = (*(n++))->GetID();
1757 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1762 //=======================================================================
1763 //function : SplitVolumesIntoTetra
1764 //purpose : Split volume elements into tetrahedra.
1765 //=======================================================================
1767 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1768 const int theMethodFlags)
1770 // std-like iterator on coordinates of nodes of mesh element
1771 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1772 NXyzIterator xyzEnd;
1774 SMDS_VolumeTool volTool;
1775 SMESH_MesherHelper helper( *GetMesh());
1777 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1778 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1780 SMESH_SequenceOfElemPtr newNodes, newElems;
1782 // map face of volume to it's baricenrtic node
1783 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1786 TIDSortedElemSet::const_iterator elem = theElems.begin();
1787 for ( ; elem != theElems.end(); ++elem )
1789 if ( (*elem)->GetType() != SMDSAbs_Volume )
1791 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1792 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1795 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1797 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1798 if ( splitMethod._nbTetra < 1 ) continue;
1800 // find submesh to add new tetras to
1801 if ( !subMesh || !subMesh->Contains( *elem ))
1803 int shapeID = FindShape( *elem );
1804 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1805 subMesh = GetMeshDS()->MeshElements( shapeID );
1808 if ( (*elem)->IsQuadratic() )
1811 // add quadratic links to the helper
1812 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1814 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1815 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1816 for ( int iN = 0; iN < nbN; iN += iQ )
1817 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1819 helper.SetIsQuadratic( true );
1824 helper.SetIsQuadratic( false );
1826 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1827 helper.SetElementsOnShape( true );
1828 if ( splitMethod._baryNode )
1830 // make a node at barycenter
1831 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1832 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1833 nodes.push_back( gcNode );
1834 newNodes.Append( gcNode );
1836 if ( !splitMethod._faceBaryNode.empty() )
1838 // make or find baricentric nodes of faces
1839 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1840 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1842 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1843 volFace2BaryNode.insert
1844 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1847 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1848 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1850 nodes.push_back( iF_n->second = f_n->second );
1855 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1856 const int* tetConn = splitMethod._connectivity;
1857 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1858 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1859 nodes[ tetConn[1] ],
1860 nodes[ tetConn[2] ],
1861 nodes[ tetConn[3] ]));
1863 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1865 // Split faces on sides of the split volume
1867 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1868 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1870 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1871 if ( nbNodes < 4 ) continue;
1873 // find an existing face
1874 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1875 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1876 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1877 /*noMedium=*/false))
1880 helper.SetElementsOnShape( false );
1881 vector< const SMDS_MeshElement* > triangles;
1883 // find submesh to add new triangles in
1884 if ( !fSubMesh || !fSubMesh->Contains( face ))
1886 int shapeID = FindShape( face );
1887 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1889 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1890 if ( iF_n != splitMethod._faceBaryNode.end() )
1892 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1894 const SMDS_MeshNode* n1 = fNodes[iN];
1895 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1896 const SMDS_MeshNode *n3 = iF_n->second;
1897 if ( !volTool.IsFaceExternal( iF ))
1899 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1901 if ( fSubMesh && n3->getshapeId() < 1 )
1902 fSubMesh->AddNode( n3 );
1907 // among possible triangles create ones discribed by split method
1908 const int* nInd = volTool.GetFaceNodesIndices( iF );
1909 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1910 int iCom = 0; // common node of triangle faces to split into
1911 list< TTriangleFacet > facets;
1912 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1914 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1915 nInd[ iQ * ( (iCom+1)%nbNodes )],
1916 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1917 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1918 nInd[ iQ * ( (iCom+2)%nbNodes )],
1919 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1920 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1922 facets.push_back( t012 );
1923 facets.push_back( t023 );
1924 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1925 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1926 nInd[ iQ * ((iLast-1)%nbNodes )],
1927 nInd[ iQ * ((iLast )%nbNodes )]));
1931 list< TTriangleFacet >::iterator facet = facets.begin();
1932 for ( ; facet != facets.end(); ++facet )
1934 if ( !volTool.IsFaceExternal( iF ))
1935 swap( facet->_n2, facet->_n3 );
1936 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1937 volNodes[ facet->_n2 ],
1938 volNodes[ facet->_n3 ]));
1941 for ( int i = 0; i < triangles.size(); ++i )
1943 if ( !triangles[i] ) continue;
1945 fSubMesh->AddElement( triangles[i]);
1946 newElems.Append( triangles[i] );
1948 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1949 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1952 } // loop on volume faces to split them into triangles
1954 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1956 if ( geomType == SMDSEntity_TriQuad_Hexa )
1958 // remove medium nodes that could become free
1959 for ( int i = 20; i < volTool.NbNodes(); ++i )
1960 if ( volNodes[i]->NbInverseElements() == 0 )
1961 GetMeshDS()->RemoveNode( volNodes[i] );
1963 } // loop on volumes to split
1965 myLastCreatedNodes = newNodes;
1966 myLastCreatedElems = newElems;
1969 //=======================================================================
1970 //function : AddToSameGroups
1971 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1972 //=======================================================================
1974 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1975 const SMDS_MeshElement* elemInGroups,
1976 SMESHDS_Mesh * aMesh)
1978 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1979 if (!groups.empty()) {
1980 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1981 for ( ; grIt != groups.end(); grIt++ ) {
1982 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1983 if ( group && group->Contains( elemInGroups ))
1984 group->SMDSGroup().Add( elemToAdd );
1990 //=======================================================================
1991 //function : RemoveElemFromGroups
1992 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1993 //=======================================================================
1994 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1995 SMESHDS_Mesh * aMesh)
1997 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1998 if (!groups.empty())
2000 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2001 for (; GrIt != groups.end(); GrIt++)
2003 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2004 if (!grp || grp->IsEmpty()) continue;
2005 grp->SMDSGroup().Remove(removeelem);
2010 //================================================================================
2012 * \brief Replace elemToRm by elemToAdd in the all groups
2014 //================================================================================
2016 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2017 const SMDS_MeshElement* elemToAdd,
2018 SMESHDS_Mesh * aMesh)
2020 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2021 if (!groups.empty()) {
2022 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2023 for ( ; grIt != groups.end(); grIt++ ) {
2024 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2025 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2026 group->SMDSGroup().Add( elemToAdd );
2031 //================================================================================
2033 * \brief Replace elemToRm by elemToAdd in the all groups
2035 //================================================================================
2037 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2038 const vector<const SMDS_MeshElement*>& elemToAdd,
2039 SMESHDS_Mesh * aMesh)
2041 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2042 if (!groups.empty())
2044 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2045 for ( ; grIt != groups.end(); grIt++ ) {
2046 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2047 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2048 for ( int i = 0; i < elemToAdd.size(); ++i )
2049 group->SMDSGroup().Add( elemToAdd[ i ] );
2054 //=======================================================================
2055 //function : QuadToTri
2056 //purpose : Cut quadrangles into triangles.
2057 // theCrit is used to select a diagonal to cut
2058 //=======================================================================
2060 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2061 const bool the13Diag)
2063 myLastCreatedElems.Clear();
2064 myLastCreatedNodes.Clear();
2066 MESSAGE( "::QuadToTri()" );
2068 SMESHDS_Mesh * aMesh = GetMeshDS();
2070 Handle(Geom_Surface) surface;
2071 SMESH_MesherHelper helper( *GetMesh() );
2073 TIDSortedElemSet::iterator itElem;
2074 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2075 const SMDS_MeshElement* elem = *itElem;
2076 if ( !elem || elem->GetType() != SMDSAbs_Face )
2078 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2079 if(!isquad) continue;
2081 if(elem->NbNodes()==4) {
2082 // retrieve element nodes
2083 const SMDS_MeshNode* aNodes [4];
2084 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2086 while ( itN->more() )
2087 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2089 int aShapeId = FindShape( elem );
2090 const SMDS_MeshElement* newElem1 = 0;
2091 const SMDS_MeshElement* newElem2 = 0;
2093 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2094 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2097 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2098 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2100 myLastCreatedElems.Append(newElem1);
2101 myLastCreatedElems.Append(newElem2);
2102 // put a new triangle on the same shape and add to the same groups
2105 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2106 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2108 AddToSameGroups( newElem1, elem, aMesh );
2109 AddToSameGroups( newElem2, elem, aMesh );
2110 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2111 aMesh->RemoveElement( elem );
2114 // Quadratic quadrangle
2116 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2118 // get surface elem is on
2119 int aShapeId = FindShape( elem );
2120 if ( aShapeId != helper.GetSubShapeID() ) {
2124 shape = aMesh->IndexToShape( aShapeId );
2125 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2126 TopoDS_Face face = TopoDS::Face( shape );
2127 surface = BRep_Tool::Surface( face );
2128 if ( !surface.IsNull() )
2129 helper.SetSubShape( shape );
2133 const SMDS_MeshNode* aNodes [8];
2134 const SMDS_MeshNode* inFaceNode = 0;
2135 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2137 while ( itN->more() ) {
2138 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2139 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2140 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2142 inFaceNode = aNodes[ i-1 ];
2146 // find middle point for (0,1,2,3)
2147 // and create a node in this point;
2149 if ( surface.IsNull() ) {
2151 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2155 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2158 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2160 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2162 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2163 myLastCreatedNodes.Append(newN);
2165 // create a new element
2166 const SMDS_MeshElement* newElem1 = 0;
2167 const SMDS_MeshElement* newElem2 = 0;
2169 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2170 aNodes[6], aNodes[7], newN );
2171 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2172 newN, aNodes[4], aNodes[5] );
2175 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2176 aNodes[7], aNodes[4], newN );
2177 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2178 newN, aNodes[5], aNodes[6] );
2180 myLastCreatedElems.Append(newElem1);
2181 myLastCreatedElems.Append(newElem2);
2182 // put a new triangle on the same shape and add to the same groups
2185 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2186 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2188 AddToSameGroups( newElem1, elem, aMesh );
2189 AddToSameGroups( newElem2, elem, aMesh );
2190 aMesh->RemoveElement( elem );
2197 //=======================================================================
2198 //function : getAngle
2200 //=======================================================================
2202 double getAngle(const SMDS_MeshElement * tr1,
2203 const SMDS_MeshElement * tr2,
2204 const SMDS_MeshNode * n1,
2205 const SMDS_MeshNode * n2)
2207 double angle = 2. * M_PI; // bad angle
2210 SMESH::Controls::TSequenceOfXYZ P1, P2;
2211 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2212 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2215 if(!tr1->IsQuadratic())
2216 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2218 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2219 if ( N1.SquareMagnitude() <= gp::Resolution() )
2221 if(!tr2->IsQuadratic())
2222 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2224 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2225 if ( N2.SquareMagnitude() <= gp::Resolution() )
2228 // find the first diagonal node n1 in the triangles:
2229 // take in account a diagonal link orientation
2230 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2231 for ( int t = 0; t < 2; t++ ) {
2232 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2233 int i = 0, iDiag = -1;
2234 while ( it->more()) {
2235 const SMDS_MeshElement *n = it->next();
2236 if ( n == n1 || n == n2 ) {
2240 if ( i - iDiag == 1 )
2241 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2250 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2253 angle = N1.Angle( N2 );
2258 // =================================================
2259 // class generating a unique ID for a pair of nodes
2260 // and able to return nodes by that ID
2261 // =================================================
2265 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2266 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2269 long GetLinkID (const SMDS_MeshNode * n1,
2270 const SMDS_MeshNode * n2) const
2272 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2275 bool GetNodes (const long theLinkID,
2276 const SMDS_MeshNode* & theNode1,
2277 const SMDS_MeshNode* & theNode2) const
2279 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2280 if ( !theNode1 ) return false;
2281 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2282 if ( !theNode2 ) return false;
2288 const SMESHDS_Mesh* myMesh;
2293 //=======================================================================
2294 //function : TriToQuad
2295 //purpose : Fuse neighbour triangles into quadrangles.
2296 // theCrit is used to select a neighbour to fuse with.
2297 // theMaxAngle is a max angle between element normals at which
2298 // fusion is still performed.
2299 //=======================================================================
2301 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2302 SMESH::Controls::NumericalFunctorPtr theCrit,
2303 const double theMaxAngle)
2305 myLastCreatedElems.Clear();
2306 myLastCreatedNodes.Clear();
2308 MESSAGE( "::TriToQuad()" );
2310 if ( !theCrit.get() )
2313 SMESHDS_Mesh * aMesh = GetMeshDS();
2315 // Prepare data for algo: build
2316 // 1. map of elements with their linkIDs
2317 // 2. map of linkIDs with their elements
2319 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2320 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2321 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2322 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2324 TIDSortedElemSet::iterator itElem;
2325 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2327 const SMDS_MeshElement* elem = *itElem;
2328 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2329 bool IsTria = ( elem->NbCornerNodes()==3 );
2330 if (!IsTria) continue;
2332 // retrieve element nodes
2333 const SMDS_MeshNode* aNodes [4];
2334 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2337 aNodes[ i++ ] = itN->next();
2338 aNodes[ 3 ] = aNodes[ 0 ];
2341 for ( i = 0; i < 3; i++ ) {
2342 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2343 // check if elements sharing a link can be fused
2344 itLE = mapLi_listEl.find( link );
2345 if ( itLE != mapLi_listEl.end() ) {
2346 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2348 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2349 //if ( FindShape( elem ) != FindShape( elem2 ))
2350 // continue; // do not fuse triangles laying on different shapes
2351 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2352 continue; // avoid making badly shaped quads
2353 (*itLE).second.push_back( elem );
2356 mapLi_listEl[ link ].push_back( elem );
2358 mapEl_setLi [ elem ].insert( link );
2361 // Clean the maps from the links shared by a sole element, ie
2362 // links to which only one element is bound in mapLi_listEl
2364 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2365 int nbElems = (*itLE).second.size();
2366 if ( nbElems < 2 ) {
2367 const SMDS_MeshElement* elem = (*itLE).second.front();
2368 SMESH_TLink link = (*itLE).first;
2369 mapEl_setLi[ elem ].erase( link );
2370 if ( mapEl_setLi[ elem ].empty() )
2371 mapEl_setLi.erase( elem );
2375 // Algo: fuse triangles into quadrangles
2377 while ( ! mapEl_setLi.empty() ) {
2378 // Look for the start element:
2379 // the element having the least nb of shared links
2380 const SMDS_MeshElement* startElem = 0;
2382 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2383 int nbLinks = (*itEL).second.size();
2384 if ( nbLinks < minNbLinks ) {
2385 startElem = (*itEL).first;
2386 minNbLinks = nbLinks;
2387 if ( minNbLinks == 1 )
2392 // search elements to fuse starting from startElem or links of elements
2393 // fused earlyer - startLinks
2394 list< SMESH_TLink > startLinks;
2395 while ( startElem || !startLinks.empty() ) {
2396 while ( !startElem && !startLinks.empty() ) {
2397 // Get an element to start, by a link
2398 SMESH_TLink linkId = startLinks.front();
2399 startLinks.pop_front();
2400 itLE = mapLi_listEl.find( linkId );
2401 if ( itLE != mapLi_listEl.end() ) {
2402 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2403 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2404 for ( ; itE != listElem.end() ; itE++ )
2405 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2407 mapLi_listEl.erase( itLE );
2412 // Get candidates to be fused
2413 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2414 const SMESH_TLink *link12, *link13;
2416 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2417 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2418 ASSERT( !setLi.empty() );
2419 set< SMESH_TLink >::iterator itLi;
2420 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2422 const SMESH_TLink & link = (*itLi);
2423 itLE = mapLi_listEl.find( link );
2424 if ( itLE == mapLi_listEl.end() )
2427 const SMDS_MeshElement* elem = (*itLE).second.front();
2429 elem = (*itLE).second.back();
2430 mapLi_listEl.erase( itLE );
2431 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2442 // add other links of elem to list of links to re-start from
2443 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2444 set< SMESH_TLink >::iterator it;
2445 for ( it = links.begin(); it != links.end(); it++ ) {
2446 const SMESH_TLink& link2 = (*it);
2447 if ( link2 != link )
2448 startLinks.push_back( link2 );
2452 // Get nodes of possible quadrangles
2453 const SMDS_MeshNode *n12 [4], *n13 [4];
2454 bool Ok12 = false, Ok13 = false;
2455 const SMDS_MeshNode *linkNode1, *linkNode2;
2457 linkNode1 = link12->first;
2458 linkNode2 = link12->second;
2459 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2463 linkNode1 = link13->first;
2464 linkNode2 = link13->second;
2465 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2469 // Choose a pair to fuse
2470 if ( Ok12 && Ok13 ) {
2471 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2472 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2473 double aBadRate12 = getBadRate( &quad12, theCrit );
2474 double aBadRate13 = getBadRate( &quad13, theCrit );
2475 if ( aBadRate13 < aBadRate12 )
2482 // and remove fused elems and remove links from the maps
2483 mapEl_setLi.erase( tr1 );
2486 mapEl_setLi.erase( tr2 );
2487 mapLi_listEl.erase( *link12 );
2488 if ( tr1->NbNodes() == 3 )
2490 const SMDS_MeshElement* newElem = 0;
2491 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2492 myLastCreatedElems.Append(newElem);
2493 AddToSameGroups( newElem, tr1, aMesh );
2494 int aShapeId = tr1->getshapeId();
2496 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2497 aMesh->RemoveElement( tr1 );
2498 aMesh->RemoveElement( tr2 );
2501 vector< const SMDS_MeshNode* > N1;
2502 vector< const SMDS_MeshNode* > N2;
2503 getNodesFromTwoTria(tr1,tr2,N1,N2);
2504 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
2505 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2506 // i.e. first nodes from both arrays form a new diagonal
2507 const SMDS_MeshNode* aNodes[8];
2516 const SMDS_MeshElement* newElem = 0;
2517 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2518 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2519 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2521 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2522 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2523 myLastCreatedElems.Append(newElem);
2524 AddToSameGroups( newElem, tr1, aMesh );
2525 int aShapeId = tr1->getshapeId();
2527 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2528 aMesh->RemoveElement( tr1 );
2529 aMesh->RemoveElement( tr2 );
2530 // remove middle node (9)
2531 if ( N1[4]->NbInverseElements() == 0 )
2532 aMesh->RemoveNode( N1[4] );
2533 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2534 aMesh->RemoveNode( N1[6] );
2535 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2536 aMesh->RemoveNode( N2[6] );
2541 mapEl_setLi.erase( tr3 );
2542 mapLi_listEl.erase( *link13 );
2543 if ( tr1->NbNodes() == 3 ) {
2544 const SMDS_MeshElement* newElem = 0;
2545 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2546 myLastCreatedElems.Append(newElem);
2547 AddToSameGroups( newElem, tr1, aMesh );
2548 int aShapeId = tr1->getshapeId();
2550 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2551 aMesh->RemoveElement( tr1 );
2552 aMesh->RemoveElement( tr3 );
2555 vector< const SMDS_MeshNode* > N1;
2556 vector< const SMDS_MeshNode* > N2;
2557 getNodesFromTwoTria(tr1,tr3,N1,N2);
2558 // now we receive following N1 and N2 (using numeration as above image)
2559 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2560 // i.e. first nodes from both arrays form a new diagonal
2561 const SMDS_MeshNode* aNodes[8];
2570 const SMDS_MeshElement* newElem = 0;
2571 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2572 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2573 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2575 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2576 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2577 myLastCreatedElems.Append(newElem);
2578 AddToSameGroups( newElem, tr1, aMesh );
2579 int aShapeId = tr1->getshapeId();
2581 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2582 aMesh->RemoveElement( tr1 );
2583 aMesh->RemoveElement( tr3 );
2584 // remove middle node (9)
2585 if ( N1[4]->NbInverseElements() == 0 )
2586 aMesh->RemoveNode( N1[4] );
2587 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2588 aMesh->RemoveNode( N1[6] );
2589 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2590 aMesh->RemoveNode( N2[6] );
2594 // Next element to fuse: the rejected one
2596 startElem = Ok12 ? tr3 : tr2;
2598 } // if ( startElem )
2599 } // while ( startElem || !startLinks.empty() )
2600 } // while ( ! mapEl_setLi.empty() )
2606 /*#define DUMPSO(txt) \
2607 // cout << txt << endl;
2608 //=============================================================================
2612 //=============================================================================
2613 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2617 int tmp = idNodes[ i1 ];
2618 idNodes[ i1 ] = idNodes[ i2 ];
2619 idNodes[ i2 ] = tmp;
2620 gp_Pnt Ptmp = P[ i1 ];
2623 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2626 //=======================================================================
2627 //function : SortQuadNodes
2628 //purpose : Set 4 nodes of a quadrangle face in a good order.
2629 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2631 //=======================================================================
2633 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2638 for ( i = 0; i < 4; i++ ) {
2639 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2641 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2644 gp_Vec V1(P[0], P[1]);
2645 gp_Vec V2(P[0], P[2]);
2646 gp_Vec V3(P[0], P[3]);
2648 gp_Vec Cross1 = V1 ^ V2;
2649 gp_Vec Cross2 = V2 ^ V3;
2652 if (Cross1.Dot(Cross2) < 0)
2657 if (Cross1.Dot(Cross2) < 0)
2661 swap ( i, i + 1, idNodes, P );
2663 // for ( int ii = 0; ii < 4; ii++ ) {
2664 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2665 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2671 //=======================================================================
2672 //function : SortHexaNodes
2673 //purpose : Set 8 nodes of a hexahedron in a good order.
2674 // Return success status
2675 //=======================================================================
2677 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2682 DUMPSO( "INPUT: ========================================");
2683 for ( i = 0; i < 8; i++ ) {
2684 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2685 if ( !n ) return false;
2686 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2687 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2689 DUMPSO( "========================================");
2692 set<int> faceNodes; // ids of bottom face nodes, to be found
2693 set<int> checkedId1; // ids of tried 2-nd nodes
2694 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2695 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2696 int iMin, iLoop1 = 0;
2698 // Loop to try the 2-nd nodes
2700 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2702 // Find not checked 2-nd node
2703 for ( i = 1; i < 8; i++ )
2704 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2705 int id1 = idNodes[i];
2706 swap ( 1, i, idNodes, P );
2707 checkedId1.insert ( id1 );
2711 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2712 // ie that all but meybe one (id3 which is on the same face) nodes
2713 // lay on the same side from the triangle plane.
2715 bool manyInPlane = false; // more than 4 nodes lay in plane
2717 while ( ++iLoop2 < 6 ) {
2719 // get 1-2-3 plane coeffs
2720 Standard_Real A, B, C, D;
2721 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2722 if ( N.SquareMagnitude() > gp::Resolution() )
2724 gp_Pln pln ( P[0], N );
2725 pln.Coefficients( A, B, C, D );
2727 // find the node (iMin) closest to pln
2728 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2730 for ( i = 3; i < 8; i++ ) {
2731 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2732 if ( fabs( dist[i] ) < minDist ) {
2733 minDist = fabs( dist[i] );
2736 if ( fabs( dist[i] ) <= tol )
2737 idInPln.insert( idNodes[i] );
2740 // there should not be more than 4 nodes in bottom plane
2741 if ( idInPln.size() > 1 )
2743 DUMPSO( "### idInPln.size() = " << idInPln.size());
2744 // idInPlane does not contain the first 3 nodes
2745 if ( manyInPlane || idInPln.size() == 5)
2746 return false; // all nodes in one plane
2749 // set the 1-st node to be not in plane
2750 for ( i = 3; i < 8; i++ ) {
2751 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2752 DUMPSO( "### Reset 0-th node");
2753 swap( 0, i, idNodes, P );
2758 // reset to re-check second nodes
2759 leastDist = DBL_MAX;
2763 break; // from iLoop2;
2766 // check that the other 4 nodes are on the same side
2767 bool sameSide = true;
2768 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2769 for ( i = 3; sameSide && i < 8; i++ ) {
2771 sameSide = ( isNeg == dist[i] <= 0.);
2774 // keep best solution
2775 if ( sameSide && minDist < leastDist ) {
2776 leastDist = minDist;
2778 faceNodes.insert( idNodes[ 1 ] );
2779 faceNodes.insert( idNodes[ 2 ] );
2780 faceNodes.insert( idNodes[ iMin ] );
2781 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2782 << " leastDist = " << leastDist);
2783 if ( leastDist <= DBL_MIN )
2788 // set next 3-d node to check
2789 int iNext = 2 + iLoop2;
2791 DUMPSO( "Try 2-nd");
2792 swap ( 2, iNext, idNodes, P );
2794 } // while ( iLoop2 < 6 )
2797 if ( faceNodes.empty() ) return false;
2799 // Put the faceNodes in proper places
2800 for ( i = 4; i < 8; i++ ) {
2801 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2802 // find a place to put
2804 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2806 DUMPSO( "Set faceNodes");
2807 swap ( iTo, i, idNodes, P );
2812 // Set nodes of the found bottom face in good order
2813 DUMPSO( " Found bottom face: ");
2814 i = SortQuadNodes( theMesh, idNodes );
2816 gp_Pnt Ptmp = P[ i ];
2821 // for ( int ii = 0; ii < 4; ii++ ) {
2822 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2823 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2826 // Gravity center of the top and bottom faces
2827 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2828 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2830 // Get direction from the bottom to the top face
2831 gp_Vec upDir ( aGCb, aGCt );
2832 Standard_Real upDirSize = upDir.Magnitude();
2833 if ( upDirSize <= gp::Resolution() ) return false;
2836 // Assure that the bottom face normal points up
2837 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2838 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2839 if ( Nb.Dot( upDir ) < 0 ) {
2840 DUMPSO( "Reverse bottom face");
2841 swap( 1, 3, idNodes, P );
2844 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2845 Standard_Real minDist = DBL_MAX;
2846 for ( i = 4; i < 8; i++ ) {
2847 // projection of P[i] to the plane defined by P[0] and upDir
2848 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2849 Standard_Real sqDist = P[0].SquareDistance( Pp );
2850 if ( sqDist < minDist ) {
2855 DUMPSO( "Set 4-th");
2856 swap ( 4, iMin, idNodes, P );
2858 // Set nodes of the top face in good order
2859 DUMPSO( "Sort top face");
2860 i = SortQuadNodes( theMesh, &idNodes[4] );
2863 gp_Pnt Ptmp = P[ i ];
2868 // Assure that direction of the top face normal is from the bottom face
2869 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2870 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2871 if ( Nt.Dot( upDir ) < 0 ) {
2872 DUMPSO( "Reverse top face");
2873 swap( 5, 7, idNodes, P );
2876 // DUMPSO( "OUTPUT: ========================================");
2877 // for ( i = 0; i < 8; i++ ) {
2878 // float *p = ugrid->GetPoint(idNodes[i]);
2879 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2885 //================================================================================
2887 * \brief Return nodes linked to the given one
2888 * \param theNode - the node
2889 * \param linkedNodes - the found nodes
2890 * \param type - the type of elements to check
2892 * Medium nodes are ignored
2894 //================================================================================
2896 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2897 TIDSortedElemSet & linkedNodes,
2898 SMDSAbs_ElementType type )
2900 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2901 while ( elemIt->more() )
2903 const SMDS_MeshElement* elem = elemIt->next();
2904 if(elem->GetType() == SMDSAbs_0DElement)
2907 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2908 if ( elem->GetType() == SMDSAbs_Volume )
2910 SMDS_VolumeTool vol( elem );
2911 while ( nodeIt->more() ) {
2912 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2913 if ( theNode != n && vol.IsLinked( theNode, n ))
2914 linkedNodes.insert( n );
2919 for ( int i = 0; nodeIt->more(); ++i ) {
2920 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2921 if ( n == theNode ) {
2922 int iBefore = i - 1;
2924 if ( elem->IsQuadratic() ) {
2925 int nb = elem->NbNodes() / 2;
2926 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2927 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2929 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2930 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2937 //=======================================================================
2938 //function : laplacianSmooth
2939 //purpose : pulls theNode toward the center of surrounding nodes directly
2940 // connected to that node along an element edge
2941 //=======================================================================
2943 void laplacianSmooth(const SMDS_MeshNode* theNode,
2944 const Handle(Geom_Surface)& theSurface,
2945 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2947 // find surrounding nodes
2949 TIDSortedElemSet nodeSet;
2950 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2952 // compute new coodrs
2954 double coord[] = { 0., 0., 0. };
2955 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2956 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2957 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2958 if ( theSurface.IsNull() ) { // smooth in 3D
2959 coord[0] += node->X();
2960 coord[1] += node->Y();
2961 coord[2] += node->Z();
2963 else { // smooth in 2D
2964 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2965 gp_XY* uv = theUVMap[ node ];
2966 coord[0] += uv->X();
2967 coord[1] += uv->Y();
2970 int nbNodes = nodeSet.size();
2973 coord[0] /= nbNodes;
2974 coord[1] /= nbNodes;
2976 if ( !theSurface.IsNull() ) {
2977 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2978 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2979 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2985 coord[2] /= nbNodes;
2989 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2992 //=======================================================================
2993 //function : centroidalSmooth
2994 //purpose : pulls theNode toward the element-area-weighted centroid of the
2995 // surrounding elements
2996 //=======================================================================
2998 void centroidalSmooth(const SMDS_MeshNode* theNode,
2999 const Handle(Geom_Surface)& theSurface,
3000 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3002 gp_XYZ aNewXYZ(0.,0.,0.);
3003 SMESH::Controls::Area anAreaFunc;
3004 double totalArea = 0.;
3009 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3010 while ( elemIt->more() )
3012 const SMDS_MeshElement* elem = elemIt->next();
3015 gp_XYZ elemCenter(0.,0.,0.);
3016 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3017 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3018 int nn = elem->NbNodes();
3019 if(elem->IsQuadratic()) nn = nn/2;
3021 //while ( itN->more() ) {
3023 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3025 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3026 aNodePoints.push_back( aP );
3027 if ( !theSurface.IsNull() ) { // smooth in 2D
3028 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3029 gp_XY* uv = theUVMap[ aNode ];
3030 aP.SetCoord( uv->X(), uv->Y(), 0. );
3034 double elemArea = anAreaFunc.GetValue( aNodePoints );
3035 totalArea += elemArea;
3037 aNewXYZ += elemCenter * elemArea;
3039 aNewXYZ /= totalArea;
3040 if ( !theSurface.IsNull() ) {
3041 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3042 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3047 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3050 //=======================================================================
3051 //function : getClosestUV
3052 //purpose : return UV of closest projection
3053 //=======================================================================
3055 static bool getClosestUV (Extrema_GenExtPS& projector,
3056 const gp_Pnt& point,
3059 projector.Perform( point );
3060 if ( projector.IsDone() ) {
3061 double u, v, minVal = DBL_MAX;
3062 for ( int i = projector.NbExt(); i > 0; i-- )
3063 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3064 if ( projector.SquareDistance( i ) < minVal ) {
3065 minVal = projector.SquareDistance( i );
3067 if ( projector.Value( i ) < minVal ) {
3068 minVal = projector.Value( i );
3070 projector.Point( i ).Parameter( u, v );
3072 result.SetCoord( u, v );
3078 //=======================================================================
3080 //purpose : Smooth theElements during theNbIterations or until a worst
3081 // element has aspect ratio <= theTgtAspectRatio.
3082 // Aspect Ratio varies in range [1.0, inf].
3083 // If theElements is empty, the whole mesh is smoothed.
3084 // theFixedNodes contains additionally fixed nodes. Nodes built
3085 // on edges and boundary nodes are always fixed.
3086 //=======================================================================
3088 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3089 set<const SMDS_MeshNode*> & theFixedNodes,
3090 const SmoothMethod theSmoothMethod,
3091 const int theNbIterations,
3092 double theTgtAspectRatio,
3095 myLastCreatedElems.Clear();
3096 myLastCreatedNodes.Clear();
3098 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3100 if ( theTgtAspectRatio < 1.0 )
3101 theTgtAspectRatio = 1.0;
3103 const double disttol = 1.e-16;
3105 SMESH::Controls::AspectRatio aQualityFunc;
3107 SMESHDS_Mesh* aMesh = GetMeshDS();
3109 if ( theElems.empty() ) {
3110 // add all faces to theElems
3111 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3112 while ( fIt->more() ) {
3113 const SMDS_MeshElement* face = fIt->next();
3114 theElems.insert( theElems.end(), face );
3117 // get all face ids theElems are on
3118 set< int > faceIdSet;
3119 TIDSortedElemSet::iterator itElem;
3121 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3122 int fId = FindShape( *itElem );
3123 // check that corresponding submesh exists and a shape is face
3125 faceIdSet.find( fId ) == faceIdSet.end() &&
3126 aMesh->MeshElements( fId )) {
3127 TopoDS_Shape F = aMesh->IndexToShape( fId );
3128 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3129 faceIdSet.insert( fId );
3132 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3134 // ===============================================
3135 // smooth elements on each TopoDS_Face separately
3136 // ===============================================
3138 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3139 for ( ; fId != faceIdSet.rend(); ++fId ) {
3140 // get face surface and submesh
3141 Handle(Geom_Surface) surface;
3142 SMESHDS_SubMesh* faceSubMesh = 0;
3144 double fToler2 = 0, f,l;
3145 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3146 bool isUPeriodic = false, isVPeriodic = false;
3148 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3149 surface = BRep_Tool::Surface( face );
3150 faceSubMesh = aMesh->MeshElements( *fId );
3151 fToler2 = BRep_Tool::Tolerance( face );
3152 fToler2 *= fToler2 * 10.;
3153 isUPeriodic = surface->IsUPeriodic();
3156 isVPeriodic = surface->IsVPeriodic();
3159 surface->Bounds( u1, u2, v1, v2 );
3161 // ---------------------------------------------------------
3162 // for elements on a face, find movable and fixed nodes and
3163 // compute UV for them
3164 // ---------------------------------------------------------
3165 bool checkBoundaryNodes = false;
3166 bool isQuadratic = false;
3167 set<const SMDS_MeshNode*> setMovableNodes;
3168 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3169 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3170 list< const SMDS_MeshElement* > elemsOnFace;
3172 Extrema_GenExtPS projector;
3173 GeomAdaptor_Surface surfAdaptor;
3174 if ( !surface.IsNull() ) {
3175 surfAdaptor.Load( surface );
3176 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3178 int nbElemOnFace = 0;
3179 itElem = theElems.begin();
3180 // loop on not yet smoothed elements: look for elems on a face
3181 while ( itElem != theElems.end() ) {
3182 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3183 break; // all elements found
3185 const SMDS_MeshElement* elem = *itElem;
3186 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3187 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3191 elemsOnFace.push_back( elem );
3192 theElems.erase( itElem++ );
3196 isQuadratic = elem->IsQuadratic();
3198 // get movable nodes of elem
3199 const SMDS_MeshNode* node;
3200 SMDS_TypeOfPosition posType;
3201 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3202 int nn = 0, nbn = elem->NbNodes();
3203 if(elem->IsQuadratic())
3205 while ( nn++ < nbn ) {
3206 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3207 const SMDS_PositionPtr& pos = node->GetPosition();
3208 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3209 if (posType != SMDS_TOP_EDGE &&
3210 posType != SMDS_TOP_VERTEX &&
3211 theFixedNodes.find( node ) == theFixedNodes.end())
3213 // check if all faces around the node are on faceSubMesh
3214 // because a node on edge may be bound to face
3215 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3217 if ( faceSubMesh ) {
3218 while ( eIt->more() && all ) {
3219 const SMDS_MeshElement* e = eIt->next();
3220 all = faceSubMesh->Contains( e );
3224 setMovableNodes.insert( node );
3226 checkBoundaryNodes = true;
3228 if ( posType == SMDS_TOP_3DSPACE )
3229 checkBoundaryNodes = true;
3232 if ( surface.IsNull() )
3235 // get nodes to check UV
3236 list< const SMDS_MeshNode* > uvCheckNodes;
3237 itN = elem->nodesIterator();
3238 nn = 0; nbn = elem->NbNodes();
3239 if(elem->IsQuadratic())
3241 while ( nn++ < nbn ) {
3242 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3243 if ( uvMap.find( node ) == uvMap.end() )
3244 uvCheckNodes.push_back( node );
3245 // add nodes of elems sharing node
3246 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3247 // while ( eIt->more() ) {
3248 // const SMDS_MeshElement* e = eIt->next();
3249 // if ( e != elem ) {
3250 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3251 // while ( nIt->more() ) {
3252 // const SMDS_MeshNode* n =
3253 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3254 // if ( uvMap.find( n ) == uvMap.end() )
3255 // uvCheckNodes.push_back( n );
3261 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3262 for ( ; n != uvCheckNodes.end(); ++n ) {
3265 const SMDS_PositionPtr& pos = node->GetPosition();
3266 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3268 switch ( posType ) {
3269 case SMDS_TOP_FACE: {
3270 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3271 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3274 case SMDS_TOP_EDGE: {
3275 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3276 Handle(Geom2d_Curve) pcurve;
3277 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3278 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3279 if ( !pcurve.IsNull() ) {
3280 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3281 uv = pcurve->Value( u ).XY();
3285 case SMDS_TOP_VERTEX: {
3286 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3287 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3288 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3293 // check existing UV
3294 bool project = true;
3295 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3296 double dist1 = DBL_MAX, dist2 = 0;
3297 if ( posType != SMDS_TOP_3DSPACE ) {
3298 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3299 project = dist1 > fToler2;
3301 if ( project ) { // compute new UV
3303 if ( !getClosestUV( projector, pNode, newUV )) {
3304 MESSAGE("Node Projection Failed " << node);
3308 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3310 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3312 if ( posType != SMDS_TOP_3DSPACE )
3313 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3314 if ( dist2 < dist1 )
3318 // store UV in the map
3319 listUV.push_back( uv );
3320 uvMap.insert( make_pair( node, &listUV.back() ));
3322 } // loop on not yet smoothed elements
3324 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3325 checkBoundaryNodes = true;
3327 // fix nodes on mesh boundary
3329 if ( checkBoundaryNodes ) {
3330 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3331 map< SMESH_TLink, int >::iterator link_nb;
3332 // put all elements links to linkNbMap
3333 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3334 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3335 const SMDS_MeshElement* elem = (*elemIt);
3336 int nbn = elem->NbCornerNodes();
3337 // loop on elem links: insert them in linkNbMap
3338 for ( int iN = 0; iN < nbn; ++iN ) {
3339 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3340 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3341 SMESH_TLink link( n1, n2 );
3342 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3346 // remove nodes that are in links encountered only once from setMovableNodes
3347 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3348 if ( link_nb->second == 1 ) {
3349 setMovableNodes.erase( link_nb->first.node1() );
3350 setMovableNodes.erase( link_nb->first.node2() );
3355 // -----------------------------------------------------
3356 // for nodes on seam edge, compute one more UV ( uvMap2 );
3357 // find movable nodes linked to nodes on seam and which
3358 // are to be smoothed using the second UV ( uvMap2 )
3359 // -----------------------------------------------------
3361 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3362 if ( !surface.IsNull() ) {
3363 TopExp_Explorer eExp( face, TopAbs_EDGE );
3364 for ( ; eExp.More(); eExp.Next() ) {
3365 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3366 if ( !BRep_Tool::IsClosed( edge, face ))
3368 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3369 if ( !sm ) continue;
3370 // find out which parameter varies for a node on seam
3373 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3374 if ( pcurve.IsNull() ) continue;
3375 uv1 = pcurve->Value( f );
3377 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3378 if ( pcurve.IsNull() ) continue;
3379 uv2 = pcurve->Value( f );
3380 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3382 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3383 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3385 // get nodes on seam and its vertices
3386 list< const SMDS_MeshNode* > seamNodes;
3387 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3388 while ( nSeamIt->more() ) {
3389 const SMDS_MeshNode* node = nSeamIt->next();
3390 if ( !isQuadratic || !IsMedium( node ))
3391 seamNodes.push_back( node );
3393 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3394 for ( ; vExp.More(); vExp.Next() ) {
3395 sm = aMesh->MeshElements( vExp.Current() );
3397 nSeamIt = sm->GetNodes();
3398 while ( nSeamIt->more() )
3399 seamNodes.push_back( nSeamIt->next() );
3402 // loop on nodes on seam
3403 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3404 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3405 const SMDS_MeshNode* nSeam = *noSeIt;
3406 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3407 if ( n_uv == uvMap.end() )
3410 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3411 // set the second UV
3412 listUV.push_back( *n_uv->second );
3413 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3414 if ( uvMap2.empty() )
3415 uvMap2 = uvMap; // copy the uvMap contents
3416 uvMap2[ nSeam ] = &listUV.back();
3418 // collect movable nodes linked to ones on seam in nodesNearSeam
3419 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3420 while ( eIt->more() ) {
3421 const SMDS_MeshElement* e = eIt->next();
3422 int nbUseMap1 = 0, nbUseMap2 = 0;
3423 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3424 int nn = 0, nbn = e->NbNodes();
3425 if(e->IsQuadratic()) nbn = nbn/2;
3426 while ( nn++ < nbn )
3428 const SMDS_MeshNode* n =
3429 static_cast<const SMDS_MeshNode*>( nIt->next() );
3431 setMovableNodes.find( n ) == setMovableNodes.end() )
3433 // add only nodes being closer to uv2 than to uv1
3434 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3435 0.5 * ( n->Y() + nSeam->Y() ),
3436 0.5 * ( n->Z() + nSeam->Z() ));
3438 getClosestUV( projector, pMid, uv );
3439 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3440 nodesNearSeam.insert( n );
3446 // for centroidalSmooth all element nodes must
3447 // be on one side of a seam
3448 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3449 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3451 while ( nn++ < nbn ) {
3452 const SMDS_MeshNode* n =
3453 static_cast<const SMDS_MeshNode*>( nIt->next() );
3454 setMovableNodes.erase( n );
3458 } // loop on nodes on seam
3459 } // loop on edge of a face
3460 } // if ( !face.IsNull() )
3462 if ( setMovableNodes.empty() ) {
3463 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3464 continue; // goto next face
3472 double maxRatio = -1., maxDisplacement = -1.;
3473 set<const SMDS_MeshNode*>::iterator nodeToMove;
3474 for ( it = 0; it < theNbIterations; it++ ) {
3475 maxDisplacement = 0.;
3476 nodeToMove = setMovableNodes.begin();
3477 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3478 const SMDS_MeshNode* node = (*nodeToMove);
3479 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3482 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3483 if ( theSmoothMethod == LAPLACIAN )
3484 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3486 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3488 // node displacement
3489 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3490 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3491 if ( aDispl > maxDisplacement )
3492 maxDisplacement = aDispl;
3494 // no node movement => exit
3495 //if ( maxDisplacement < 1.e-16 ) {
3496 if ( maxDisplacement < disttol ) {
3497 MESSAGE("-- no node movement --");
3501 // check elements quality
3503 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3504 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3505 const SMDS_MeshElement* elem = (*elemIt);
3506 if ( !elem || elem->GetType() != SMDSAbs_Face )
3508 SMESH::Controls::TSequenceOfXYZ aPoints;
3509 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3510 double aValue = aQualityFunc.GetValue( aPoints );
3511 if ( aValue > maxRatio )
3515 if ( maxRatio <= theTgtAspectRatio ) {
3516 MESSAGE("-- quality achived --");
3519 if (it+1 == theNbIterations) {
3520 MESSAGE("-- Iteration limit exceeded --");
3522 } // smoothing iterations
3524 MESSAGE(" Face id: " << *fId <<
3525 " Nb iterstions: " << it <<
3526 " Displacement: " << maxDisplacement <<
3527 " Aspect Ratio " << maxRatio);
3529 // ---------------------------------------
3530 // new nodes positions are computed,
3531 // record movement in DS and set new UV
3532 // ---------------------------------------
3533 nodeToMove = setMovableNodes.begin();
3534 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3535 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3536 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3537 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3538 if ( node_uv != uvMap.end() ) {
3539 gp_XY* uv = node_uv->second;
3541 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3545 // move medium nodes of quadratic elements
3548 SMESH_MesherHelper helper( *GetMesh() );
3549 helper.SetSubShape( face );
3550 vector<const SMDS_MeshNode*> nodes;
3552 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3553 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
3555 const SMDS_MeshElement* QF = *elemIt;
3556 if ( QF->IsQuadratic() )
3558 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
3559 SMDS_MeshElement::iterator() );
3560 nodes.push_back( nodes[0] );
3562 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
3564 if ( !surface.IsNull() )
3566 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
3567 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
3568 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
3569 xyz = surface->Value( uv.X(), uv.Y() );
3572 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
3574 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
3575 // we have to move a medium node
3576 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
3582 } // loop on face ids
3586 //=======================================================================
3587 //function : isReverse
3588 //purpose : Return true if normal of prevNodes is not co-directied with
3589 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3590 // iNotSame is where prevNodes and nextNodes are different.
3591 // If result is true then future volume orientation is OK
3592 //=======================================================================
3594 static bool isReverse(const SMDS_MeshElement* face,
3595 const vector<const SMDS_MeshNode*>& prevNodes,
3596 const vector<const SMDS_MeshNode*>& nextNodes,
3600 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3601 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3602 gp_XYZ extrDir( pN - pP ), faceNorm;
3603 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
3605 return faceNorm * extrDir < 0.0;
3608 //=======================================================================
3610 * \brief Create elements by sweeping an element
3611 * \param elem - element to sweep
3612 * \param newNodesItVec - nodes generated from each node of the element
3613 * \param newElems - generated elements
3614 * \param nbSteps - number of sweeping steps
3615 * \param srcElements - to append elem for each generated element
3617 //=======================================================================
3619 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3620 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3621 list<const SMDS_MeshElement*>& newElems,
3623 SMESH_SequenceOfElemPtr& srcElements)
3625 //MESSAGE("sweepElement " << nbSteps);
3626 SMESHDS_Mesh* aMesh = GetMeshDS();
3628 const int nbNodes = elem->NbNodes();
3629 const int nbCorners = elem->NbCornerNodes();
3630 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3631 polyhedron creation !!! */
3632 // Loop on elem nodes:
3633 // find new nodes and detect same nodes indices
3634 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3635 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3636 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3637 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3639 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3640 vector<int> sames(nbNodes);
3641 vector<bool> isSingleNode(nbNodes);
3643 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3644 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3645 const SMDS_MeshNode* node = nnIt->first;
3646 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3647 if ( listNewNodes.empty() )
3650 itNN [ iNode ] = listNewNodes.begin();
3651 prevNod[ iNode ] = node;
3652 nextNod[ iNode ] = listNewNodes.front();
3654 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3655 corner node of linear */
3656 if ( prevNod[ iNode ] != nextNod [ iNode ])
3657 nbDouble += !isSingleNode[iNode];
3659 if( iNode < nbCorners ) { // check corners only
3660 if ( prevNod[ iNode ] == nextNod [ iNode ])
3661 sames[nbSame++] = iNode;
3663 iNotSameNode = iNode;
3667 if ( nbSame == nbNodes || nbSame > 2) {
3668 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3672 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3674 // fix nodes order to have bottom normal external
3675 if ( baseType == SMDSEntity_Polygon )
3677 std::reverse( itNN.begin(), itNN.end() );
3678 std::reverse( prevNod.begin(), prevNod.end() );
3679 std::reverse( midlNod.begin(), midlNod.end() );
3680 std::reverse( nextNod.begin(), nextNod.end() );
3681 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3685 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3686 SMDS_MeshCell::applyInterlace( ind, itNN );
3687 SMDS_MeshCell::applyInterlace( ind, prevNod );
3688 SMDS_MeshCell::applyInterlace( ind, nextNod );
3689 SMDS_MeshCell::applyInterlace( ind, midlNod );
3690 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3693 sames[nbSame] = iNotSameNode;
3694 for ( int j = 0; j <= nbSame; ++j )
3695 for ( size_t i = 0; i < ind.size(); ++i )
3696 if ( ind[i] == sames[j] )
3701 iNotSameNode = sames[nbSame];
3706 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3708 iSameNode = sames[ nbSame-1 ];
3709 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3710 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3711 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3714 // make new elements
3715 for (int iStep = 0; iStep < nbSteps; iStep++ )
3718 for ( iNode = 0; iNode < nbNodes; iNode++ )
3720 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3721 nextNod[ iNode ] = *itNN[ iNode ]++;
3724 SMDS_MeshElement* aNewElem = 0;
3725 /*if(!elem->IsPoly())*/ {
3726 switch ( baseType ) {
3728 case SMDSEntity_Node: { // sweep NODE
3729 if ( nbSame == 0 ) {
3730 if ( isSingleNode[0] )
3731 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3733 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3739 case SMDSEntity_Edge: { // sweep EDGE
3740 if ( nbDouble == 0 )
3742 if ( nbSame == 0 ) // ---> quadrangle
3743 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3744 nextNod[ 1 ], nextNod[ 0 ] );
3745 else // ---> triangle
3746 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3747 nextNod[ iNotSameNode ] );
3749 else // ---> polygon
3751 vector<const SMDS_MeshNode*> poly_nodes;
3752 poly_nodes.push_back( prevNod[0] );
3753 poly_nodes.push_back( prevNod[1] );
3754 if ( prevNod[1] != nextNod[1] )
3756 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3757 poly_nodes.push_back( nextNod[1] );
3759 if ( prevNod[0] != nextNod[0] )
3761 poly_nodes.push_back( nextNod[0] );
3762 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3764 switch ( poly_nodes.size() ) {
3766 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3769 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3770 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3773 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3778 case SMDSEntity_Triangle: // TRIANGLE --->
3780 if ( nbDouble > 0 ) break;
3781 if ( nbSame == 0 ) // ---> pentahedron
3782 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3783 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3785 else if ( nbSame == 1 ) // ---> pyramid
3786 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3787 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3788 nextNod[ iSameNode ]);
3790 else // 2 same nodes: ---> tetrahedron
3791 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3792 nextNod[ iNotSameNode ]);
3795 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3799 if ( nbDouble+nbSame == 2 )
3801 if(nbSame==0) { // ---> quadratic quadrangle
3802 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3803 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3805 else { //(nbSame==1) // ---> quadratic triangle
3807 return; // medium node on axis
3809 else if(sames[0]==0)
3810 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3811 nextNod[2], midlNod[1], prevNod[2]);
3813 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3814 midlNod[0], nextNod[2], prevNod[2]);
3817 else if ( nbDouble == 3 )
3819 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3820 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3821 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3828 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3829 if ( nbDouble > 0 ) break;
3831 if ( nbSame == 0 ) // ---> hexahedron
3832 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3833 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3835 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3836 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3837 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3838 nextNod[ iSameNode ]);
3839 newElems.push_back( aNewElem );
3840 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3841 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3842 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3844 else if ( nbSame == 2 ) { // ---> pentahedron
3845 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3846 // iBeforeSame is same too
3847 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3848 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3849 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3851 // iAfterSame is same too
3852 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3853 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3854 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3858 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
3859 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
3860 if ( nbDouble+nbSame != 3 ) break;
3862 // ---> pentahedron with 15 nodes
3863 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3864 nextNod[0], nextNod[1], nextNod[2],
3865 prevNod[3], prevNod[4], prevNod[5],
3866 nextNod[3], nextNod[4], nextNod[5],
3867 midlNod[0], midlNod[1], midlNod[2]);
3869 else if(nbSame==1) {
3870 // ---> 2d order pyramid of 13 nodes
3871 int apex = iSameNode;
3872 int i0 = ( apex + 1 ) % nbCorners;
3873 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3877 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3878 nextNod[i0], nextNod[i1], prevNod[apex],
3879 prevNod[i01], midlNod[i0],
3880 nextNod[i01], midlNod[i1],
3881 prevNod[i1a], prevNod[i0a],
3882 nextNod[i0a], nextNod[i1a]);
3884 else if(nbSame==2) {
3885 // ---> 2d order tetrahedron of 10 nodes
3886 int n1 = iNotSameNode;
3887 int n2 = ( n1 + 1 ) % nbCorners;
3888 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3892 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3893 prevNod[n12], prevNod[n23], prevNod[n31],
3894 midlNod[n1], nextNod[n12], nextNod[n31]);
3898 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3900 if ( nbDouble != 4 ) break;
3901 // ---> hexahedron with 20 nodes
3902 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3903 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3904 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3905 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3906 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3908 else if(nbSame==1) {
3909 // ---> pyramid + pentahedron - can not be created since it is needed
3910 // additional middle node at the center of face
3911 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3914 else if( nbSame == 2 ) {
3915 if ( nbDouble != 2 ) break;
3916 // ---> 2d order Pentahedron with 15 nodes
3918 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3919 // iBeforeSame is same too
3926 // iAfterSame is same too
3936 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3937 prevNod[n4], prevNod[n5], nextNod[n5],
3938 prevNod[n12], midlNod[n2], nextNod[n12],
3939 prevNod[n45], midlNod[n5], nextNod[n45],
3940 prevNod[n14], prevNod[n25], nextNod[n25]);
3944 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3946 if( nbSame == 0 && nbDouble == 9 ) {
3947 // ---> tri-quadratic hexahedron with 27 nodes
3948 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3949 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3950 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3951 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3952 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3953 prevNod[8], // bottom center
3954 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3955 nextNod[8], // top center
3956 midlNod[8]);// elem center
3964 case SMDSEntity_Polygon: { // sweep POLYGON
3966 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3967 // ---> hexagonal prism
3968 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3969 prevNod[3], prevNod[4], prevNod[5],
3970 nextNod[0], nextNod[1], nextNod[2],
3971 nextNod[3], nextNod[4], nextNod[5]);
3975 case SMDSEntity_Ball:
3983 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3985 if ( baseType != SMDSEntity_Polygon )
3987 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3988 SMDS_MeshCell::applyInterlace( ind, prevNod );
3989 SMDS_MeshCell::applyInterlace( ind, nextNod );
3990 SMDS_MeshCell::applyInterlace( ind, midlNod );
3991 SMDS_MeshCell::applyInterlace( ind, itNN );
3992 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3993 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3995 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3996 vector<int> quantities (nbNodes + 2);
3997 polyedre_nodes.clear();
4001 for (int inode = 0; inode < nbNodes; inode++)
4002 polyedre_nodes.push_back( prevNod[inode] );
4003 quantities.push_back( nbNodes );
4006 polyedre_nodes.push_back( nextNod[0] );
4007 for (int inode = nbNodes; inode-1; --inode )
4008 polyedre_nodes.push_back( nextNod[inode-1] );
4009 quantities.push_back( nbNodes );
4012 for (int iface = 0; iface < nbNodes; iface++)
4014 const int prevNbNodes = polyedre_nodes.size();
4015 int inextface = (iface+1) % nbNodes;
4016 polyedre_nodes.push_back( prevNod[inextface] );
4017 polyedre_nodes.push_back( prevNod[iface] );
4018 if ( prevNod[iface] != nextNod[iface] )
4020 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4021 polyedre_nodes.push_back( nextNod[iface] );
4023 if ( prevNod[inextface] != nextNod[inextface] )
4025 polyedre_nodes.push_back( nextNod[inextface] );
4026 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4028 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4029 if ( nbFaceNodes > 2 )
4030 quantities.push_back( nbFaceNodes );
4031 else // degenerated face
4032 polyedre_nodes.resize( prevNbNodes );
4034 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4038 newElems.push_back( aNewElem );
4039 myLastCreatedElems.Append(aNewElem);
4040 srcElements.Append( elem );
4043 // set new prev nodes
4044 for ( iNode = 0; iNode < nbNodes; iNode++ )
4045 prevNod[ iNode ] = nextNod[ iNode ];
4050 //=======================================================================
4052 * \brief Create 1D and 2D elements around swept elements
4053 * \param mapNewNodes - source nodes and ones generated from them
4054 * \param newElemsMap - source elements and ones generated from them
4055 * \param elemNewNodesMap - nodes generated from each node of each element
4056 * \param elemSet - all swept elements
4057 * \param nbSteps - number of sweeping steps
4058 * \param srcElements - to append elem for each generated element
4060 //=======================================================================
4062 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4063 TElemOfElemListMap & newElemsMap,
4064 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4065 TIDSortedElemSet& elemSet,
4067 SMESH_SequenceOfElemPtr& srcElements)
4069 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4070 SMESHDS_Mesh* aMesh = GetMeshDS();
4072 // Find nodes belonging to only one initial element - sweep them into edges.
4074 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4075 for ( ; nList != mapNewNodes.end(); nList++ )
4077 const SMDS_MeshNode* node =
4078 static_cast<const SMDS_MeshNode*>( nList->first );
4079 if ( newElemsMap.count( node ))
4080 continue; // node was extruded into edge
4081 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4082 int nbInitElems = 0;
4083 const SMDS_MeshElement* el = 0;
4084 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4085 while ( eIt->more() && nbInitElems < 2 ) {
4087 SMDSAbs_ElementType type = el->GetType();
4088 if ( type == SMDSAbs_Volume || type < highType ) continue;
4089 if ( type > highType ) {
4093 nbInitElems += elemSet.count(el);
4095 if ( nbInitElems < 2 ) {
4096 bool NotCreateEdge = el && el->IsMediumNode(node);
4097 if(!NotCreateEdge) {
4098 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4099 list<const SMDS_MeshElement*> newEdges;
4100 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4105 // Make a ceiling for each element ie an equal element of last new nodes.
4106 // Find free links of faces - make edges and sweep them into faces.
4108 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4109 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4110 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4112 const SMDS_MeshElement* elem = itElem->first;
4113 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4115 if(itElem->second.size()==0) continue;
4117 const bool isQuadratic = elem->IsQuadratic();
4119 if ( elem->GetType() == SMDSAbs_Edge ) {
4120 // create a ceiling edge
4121 if ( !isQuadratic ) {
4122 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4123 vecNewNodes[ 1 ]->second.back())) {
4124 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4125 vecNewNodes[ 1 ]->second.back()));
4126 srcElements.Append( elem );
4130 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4131 vecNewNodes[ 1 ]->second.back(),
4132 vecNewNodes[ 2 ]->second.back())) {
4133 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4134 vecNewNodes[ 1 ]->second.back(),
4135 vecNewNodes[ 2 ]->second.back()));
4136 srcElements.Append( elem );
4140 if ( elem->GetType() != SMDSAbs_Face )
4143 bool hasFreeLinks = false;
4145 TIDSortedElemSet avoidSet;
4146 avoidSet.insert( elem );
4148 set<const SMDS_MeshNode*> aFaceLastNodes;
4149 int iNode, nbNodes = vecNewNodes.size();
4150 if ( !isQuadratic ) {
4151 // loop on the face nodes
4152 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4153 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4154 // look for free links of the face
4155 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4156 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4157 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4158 // check if a link n1-n2 is free
4159 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4160 hasFreeLinks = true;
4161 // make a new edge and a ceiling for a new edge
4162 const SMDS_MeshElement* edge;
4163 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4164 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4165 srcElements.Append( myLastCreatedElems.Last() );
4167 n1 = vecNewNodes[ iNode ]->second.back();
4168 n2 = vecNewNodes[ iNext ]->second.back();
4169 if ( !aMesh->FindEdge( n1, n2 )) {
4170 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4171 srcElements.Append( edge );
4176 else { // elem is quadratic face
4177 int nbn = nbNodes/2;
4178 for ( iNode = 0; iNode < nbn; iNode++ ) {
4179 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4180 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4181 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4182 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4183 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4184 // check if a link is free
4185 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4186 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4187 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4188 hasFreeLinks = true;
4189 // make an edge and a ceiling for a new edge
4191 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4192 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4193 srcElements.Append( elem );
4195 n1 = vecNewNodes[ iNode ]->second.back();
4196 n2 = vecNewNodes[ iNext ]->second.back();
4197 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4198 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4199 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4200 srcElements.Append( elem );
4204 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4205 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4209 // sweep free links into faces
4211 if ( hasFreeLinks ) {
4212 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4213 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4215 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4216 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4217 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4218 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4219 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4221 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4222 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4223 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4225 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4226 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4227 std::advance( v, volNb );
4228 // find indices of free faces of a volume and their source edges
4229 list< int > freeInd;
4230 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4231 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4232 int iF, nbF = vTool.NbFaces();
4233 for ( iF = 0; iF < nbF; iF ++ ) {
4234 if (vTool.IsFreeFace( iF ) &&
4235 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4236 initNodeSet != faceNodeSet) // except an initial face
4238 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4240 if ( faceNodeSet == initNodeSetNoCenter )
4242 freeInd.push_back( iF );
4243 // find source edge of a free face iF
4244 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4245 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4246 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4247 initNodeSet.begin(), initNodeSet.end(),
4248 commonNodes.begin());
4249 if ( (*v)->IsQuadratic() )
4250 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4252 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4254 if ( !srcEdges.back() )
4256 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4257 << iF << " of volume #" << vTool.ID() << endl;
4262 if ( freeInd.empty() )
4265 // create faces for all steps;
4266 // if such a face has been already created by sweep of edge,
4267 // assure that its orientation is OK
4268 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4269 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4270 vTool.SetExternalNormal();
4271 const int nextShift = vTool.IsForward() ? +1 : -1;
4272 list< int >::iterator ind = freeInd.begin();
4273 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4274 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4276 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4277 int nbn = vTool.NbFaceNodes( *ind );
4278 const SMDS_MeshElement * f = 0;
4279 if ( nbn == 3 ) ///// triangle
4281 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4283 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4285 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4287 nodes[ 1 + nextShift ] };
4289 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4291 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4295 else if ( nbn == 4 ) ///// quadrangle
4297 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4299 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4301 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4302 nodes[ 2 ], nodes[ 2+nextShift ] };
4304 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4306 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4307 newOrder[ 2 ], newOrder[ 3 ]));
4310 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4312 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4314 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4316 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4318 nodes[2 + 2*nextShift],
4319 nodes[3 - 2*nextShift],
4321 nodes[3 + 2*nextShift]};
4323 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4325 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4333 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4335 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4336 nodes[1], nodes[3], nodes[5], nodes[7] );
4338 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4340 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4341 nodes[4 - 2*nextShift],
4343 nodes[4 + 2*nextShift],
4345 nodes[5 - 2*nextShift],
4347 nodes[5 + 2*nextShift] };
4349 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4351 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4352 newOrder[ 2 ], newOrder[ 3 ],
4353 newOrder[ 4 ], newOrder[ 5 ],
4354 newOrder[ 6 ], newOrder[ 7 ]));
4357 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4359 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4360 SMDSAbs_Face, /*noMedium=*/false);
4362 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4364 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4365 nodes[4 - 2*nextShift],
4367 nodes[4 + 2*nextShift],
4369 nodes[5 - 2*nextShift],
4371 nodes[5 + 2*nextShift],
4374 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4376 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4377 newOrder[ 2 ], newOrder[ 3 ],
4378 newOrder[ 4 ], newOrder[ 5 ],
4379 newOrder[ 6 ], newOrder[ 7 ],
4383 else //////// polygon
4385 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4386 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4388 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4390 if ( !vTool.IsForward() )
4391 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4393 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4395 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4399 while ( srcElements.Length() < myLastCreatedElems.Length() )
4400 srcElements.Append( *srcEdge );
4402 } // loop on free faces
4404 // go to the next volume
4406 while ( iVol++ < nbVolumesByStep ) v++;
4409 } // loop on volumes of one step
4410 } // sweep free links into faces
4412 // Make a ceiling face with a normal external to a volume
4414 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4415 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4416 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4418 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4419 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4420 iF = lastVol.GetFaceIndex( aFaceLastNodes );
4423 lastVol.SetExternalNormal();
4424 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4425 int nbn = lastVol.NbFaceNodes( iF );
4426 // we do not use this->AddElement() because nodes are interlaced
4427 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4428 if ( !hasFreeLinks ||
4429 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4432 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4434 else if ( nbn == 4 )
4435 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4437 else if ( nbn == 6 && isQuadratic )
4438 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4439 nodes[1], nodes[3], nodes[5]));
4440 else if ( nbn == 7 && isQuadratic )
4441 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4442 nodes[1], nodes[3], nodes[5], nodes[6]));
4443 else if ( nbn == 8 && isQuadratic )
4444 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4445 nodes[1], nodes[3], nodes[5], nodes[7]));
4446 else if ( nbn == 9 && isQuadratic )
4447 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4448 nodes[1], nodes[3], nodes[5], nodes[7],
4451 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
4453 while ( srcElements.Length() < myLastCreatedElems.Length() )
4454 srcElements.Append( elem );
4457 } // loop on swept elements
4460 //=======================================================================
4461 //function : RotationSweep
4463 //=======================================================================
4465 SMESH_MeshEditor::PGroupIDs
4466 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4467 const gp_Ax1& theAxis,
4468 const double theAngle,
4469 const int theNbSteps,
4470 const double theTol,
4471 const bool theMakeGroups,
4472 const bool theMakeWalls)
4474 myLastCreatedElems.Clear();
4475 myLastCreatedNodes.Clear();
4477 // source elements for each generated one
4478 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4480 MESSAGE( "RotationSweep()");
4482 aTrsf.SetRotation( theAxis, theAngle );
4484 aTrsf2.SetRotation( theAxis, theAngle/2. );
4486 gp_Lin aLine( theAxis );
4487 double aSqTol = theTol * theTol;
4489 SMESHDS_Mesh* aMesh = GetMeshDS();
4491 TNodeOfNodeListMap mapNewNodes;
4492 TElemOfVecOfNnlmiMap mapElemNewNodes;
4493 TElemOfElemListMap newElemsMap;
4495 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4496 myMesh->NbFaces(ORDER_QUADRATIC) +
4497 myMesh->NbVolumes(ORDER_QUADRATIC) );
4499 TIDSortedElemSet::iterator itElem;
4500 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4501 const SMDS_MeshElement* elem = *itElem;
4502 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4504 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4505 newNodesItVec.reserve( elem->NbNodes() );
4507 // loop on elem nodes
4508 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4509 while ( itN->more() )
4511 // check if a node has been already sweeped
4512 const SMDS_MeshNode* node = cast2Node( itN->next() );
4514 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4516 aXYZ.Coord( coord[0], coord[1], coord[2] );
4517 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4519 TNodeOfNodeListMapItr nIt =
4520 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4521 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4522 if ( listNewNodes.empty() )
4524 // check if we are to create medium nodes between corner ones
4525 bool needMediumNodes = false;
4526 if ( isQuadraticMesh )
4528 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4529 while (it->more() && !needMediumNodes )
4531 const SMDS_MeshElement* invElem = it->next();
4532 if ( invElem != elem && !theElems.count( invElem )) continue;
4533 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4534 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4535 needMediumNodes = true;
4540 const SMDS_MeshNode * newNode = node;
4541 for ( int i = 0; i < theNbSteps; i++ ) {
4543 if ( needMediumNodes ) // create a medium node
4545 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4546 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4547 myLastCreatedNodes.Append(newNode);
4548 srcNodes.Append( node );
4549 listNewNodes.push_back( newNode );
4550 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4553 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4555 // create a corner node
4556 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4557 myLastCreatedNodes.Append(newNode);
4558 srcNodes.Append( node );
4559 listNewNodes.push_back( newNode );
4562 listNewNodes.push_back( newNode );
4563 // if ( needMediumNodes )
4564 // listNewNodes.push_back( newNode );
4568 newNodesItVec.push_back( nIt );
4570 // make new elements
4571 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4575 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4577 PGroupIDs newGroupIDs;
4578 if ( theMakeGroups )
4579 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4585 //=======================================================================
4586 //function : CreateNode
4588 //=======================================================================
4589 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4592 const double tolnode,
4593 SMESH_SequenceOfNode& aNodes)
4595 // myLastCreatedElems.Clear();
4596 // myLastCreatedNodes.Clear();
4599 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4601 // try to search in sequence of existing nodes
4602 // if aNodes.Length()>0 we 'nave to use given sequence
4603 // else - use all nodes of mesh
4604 if(aNodes.Length()>0) {
4606 for(i=1; i<=aNodes.Length(); i++) {
4607 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4608 if(P1.Distance(P2)<tolnode)
4609 return aNodes.Value(i);
4613 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4614 while(itn->more()) {
4615 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4616 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4617 if(P1.Distance(P2)<tolnode)
4622 // create new node and return it
4623 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4624 //myLastCreatedNodes.Append(NewNode);
4629 //=======================================================================
4630 //function : ExtrusionSweep
4632 //=======================================================================
4634 SMESH_MeshEditor::PGroupIDs
4635 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4636 const gp_Vec& theStep,
4637 const int theNbSteps,
4638 TElemOfElemListMap& newElemsMap,
4639 const bool theMakeGroups,
4641 const double theTolerance)
4643 ExtrusParam aParams;
4644 aParams.myDir = gp_Dir(theStep);
4645 aParams.myNodes.Clear();
4646 aParams.mySteps = new TColStd_HSequenceOfReal;
4648 for(i=1; i<=theNbSteps; i++)
4649 aParams.mySteps->Append(theStep.Magnitude());
4652 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4656 //=======================================================================
4657 //function : ExtrusionSweep
4659 //=======================================================================
4661 SMESH_MeshEditor::PGroupIDs
4662 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4663 ExtrusParam& theParams,
4664 TElemOfElemListMap& newElemsMap,
4665 const bool theMakeGroups,
4667 const double theTolerance)
4669 myLastCreatedElems.Clear();
4670 myLastCreatedNodes.Clear();
4672 // source elements for each generated one
4673 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4675 SMESHDS_Mesh* aMesh = GetMeshDS();
4677 int nbsteps = theParams.mySteps->Length();
4679 TNodeOfNodeListMap mapNewNodes;
4680 //TNodeOfNodeVecMap mapNewNodes;
4681 TElemOfVecOfNnlmiMap mapElemNewNodes;
4682 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4684 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4685 myMesh->NbFaces(ORDER_QUADRATIC) +
4686 myMesh->NbVolumes(ORDER_QUADRATIC) );
4688 TIDSortedElemSet::iterator itElem;
4689 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4690 // check element type
4691 const SMDS_MeshElement* elem = *itElem;
4692 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4695 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4696 newNodesItVec.reserve( elem->NbNodes() );
4698 // loop on elem nodes
4699 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4700 while ( itN->more() )
4702 // check if a node has been already sweeped
4703 const SMDS_MeshNode* node = cast2Node( itN->next() );
4704 TNodeOfNodeListMap::iterator nIt =
4705 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4706 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4707 if ( listNewNodes.empty() )
4711 // check if we are to create medium nodes between corner ones
4712 bool needMediumNodes = false;
4713 if ( isQuadraticMesh )
4715 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4716 while (it->more() && !needMediumNodes )
4718 const SMDS_MeshElement* invElem = it->next();
4719 if ( invElem != elem && !theElems.count( invElem )) continue;
4720 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4721 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4722 needMediumNodes = true;
4726 double coord[] = { node->X(), node->Y(), node->Z() };
4727 for ( int i = 0; i < nbsteps; i++ )
4729 if ( needMediumNodes ) // create a medium node
4731 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4732 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4733 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4734 if( theFlags & EXTRUSION_FLAG_SEW ) {
4735 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4736 theTolerance, theParams.myNodes);
4737 listNewNodes.push_back( newNode );
4740 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4741 myLastCreatedNodes.Append(newNode);
4742 srcNodes.Append( node );
4743 listNewNodes.push_back( newNode );
4746 // create a corner node
4747 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4748 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4749 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4750 if( theFlags & EXTRUSION_FLAG_SEW ) {
4751 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4752 theTolerance, theParams.myNodes);
4753 listNewNodes.push_back( newNode );
4756 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4757 myLastCreatedNodes.Append(newNode);
4758 srcNodes.Append( node );
4759 listNewNodes.push_back( newNode );
4763 newNodesItVec.push_back( nIt );
4765 // make new elements
4766 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4769 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4770 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4772 PGroupIDs newGroupIDs;
4773 if ( theMakeGroups )
4774 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4779 //=======================================================================
4780 //function : ExtrusionAlongTrack
4782 //=======================================================================
4783 SMESH_MeshEditor::Extrusion_Error
4784 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4785 SMESH_subMesh* theTrack,
4786 const SMDS_MeshNode* theN1,
4787 const bool theHasAngles,
4788 list<double>& theAngles,
4789 const bool theLinearVariation,
4790 const bool theHasRefPoint,
4791 const gp_Pnt& theRefPoint,
4792 const bool theMakeGroups)
4794 MESSAGE("ExtrusionAlongTrack");
4795 myLastCreatedElems.Clear();
4796 myLastCreatedNodes.Clear();
4799 std::list<double> aPrms;
4800 TIDSortedElemSet::iterator itElem;
4803 TopoDS_Edge aTrackEdge;
4804 TopoDS_Vertex aV1, aV2;
4806 SMDS_ElemIteratorPtr aItE;
4807 SMDS_NodeIteratorPtr aItN;
4808 SMDSAbs_ElementType aTypeE;
4810 TNodeOfNodeListMap mapNewNodes;
4813 aNbE = theElements.size();
4816 return EXTR_NO_ELEMENTS;
4818 // 1.1 Track Pattern
4821 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4823 aItE = pSubMeshDS->GetElements();
4824 while ( aItE->more() ) {
4825 const SMDS_MeshElement* pE = aItE->next();
4826 aTypeE = pE->GetType();
4827 // Pattern must contain links only
4828 if ( aTypeE != SMDSAbs_Edge )
4829 return EXTR_PATH_NOT_EDGE;
4832 list<SMESH_MeshEditor_PathPoint> fullList;
4834 const TopoDS_Shape& aS = theTrack->GetSubShape();
4835 // Sub-shape for the Pattern must be an Edge or Wire
4836 if( aS.ShapeType() == TopAbs_EDGE ) {
4837 aTrackEdge = TopoDS::Edge( aS );
4838 // the Edge must not be degenerated
4839 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4840 return EXTR_BAD_PATH_SHAPE;
4841 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4842 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4843 const SMDS_MeshNode* aN1 = aItN->next();
4844 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4845 const SMDS_MeshNode* aN2 = aItN->next();
4846 // starting node must be aN1 or aN2
4847 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4848 return EXTR_BAD_STARTING_NODE;
4849 aItN = pSubMeshDS->GetNodes();
4850 while ( aItN->more() ) {
4851 const SMDS_MeshNode* pNode = aItN->next();
4852 const SMDS_EdgePosition* pEPos =
4853 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4854 double aT = pEPos->GetUParameter();
4855 aPrms.push_back( aT );
4857 //Extrusion_Error err =
4858 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4859 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4860 list< SMESH_subMesh* > LSM;
4861 TopTools_SequenceOfShape Edges;
4862 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4863 while(itSM->more()) {
4864 SMESH_subMesh* SM = itSM->next();
4866 const TopoDS_Shape& aS = SM->GetSubShape();
4869 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4870 int startNid = theN1->GetID();
4871 TColStd_MapOfInteger UsedNums;
4873 int NbEdges = Edges.Length();
4875 for(; i<=NbEdges; i++) {
4877 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4878 for(; itLSM!=LSM.end(); itLSM++) {
4880 if(UsedNums.Contains(k)) continue;
4881 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4882 SMESH_subMesh* locTrack = *itLSM;
4883 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4884 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4885 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4886 const SMDS_MeshNode* aN1 = aItN->next();
4887 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4888 const SMDS_MeshNode* aN2 = aItN->next();
4889 // starting node must be aN1 or aN2
4890 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4891 // 2. Collect parameters on the track edge
4893 aItN = locMeshDS->GetNodes();
4894 while ( aItN->more() ) {
4895 const SMDS_MeshNode* pNode = aItN->next();
4896 const SMDS_EdgePosition* pEPos =
4897 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4898 double aT = pEPos->GetUParameter();
4899 aPrms.push_back( aT );
4901 list<SMESH_MeshEditor_PathPoint> LPP;
4902 //Extrusion_Error err =
4903 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4904 LLPPs.push_back(LPP);
4906 // update startN for search following egde
4907 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4908 else startNid = aN1->GetID();
4912 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4913 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4914 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4915 for(; itPP!=firstList.end(); itPP++) {
4916 fullList.push_back( *itPP );
4918 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4919 fullList.pop_back();
4921 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4922 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4923 itPP = currList.begin();
4924 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4925 gp_Dir D1 = PP1.Tangent();
4926 gp_Dir D2 = PP2.Tangent();
4927 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4928 (D1.Z()+D2.Z())/2 ) );
4929 PP1.SetTangent(Dnew);
4930 fullList.push_back(PP1);
4932 for(; itPP!=firstList.end(); itPP++) {
4933 fullList.push_back( *itPP );
4935 PP1 = fullList.back();
4936 fullList.pop_back();
4938 // if wire not closed
4939 fullList.push_back(PP1);
4943 return EXTR_BAD_PATH_SHAPE;
4946 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4947 theHasRefPoint, theRefPoint, theMakeGroups);
4951 //=======================================================================
4952 //function : ExtrusionAlongTrack
4954 //=======================================================================
4955 SMESH_MeshEditor::Extrusion_Error
4956 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4957 SMESH_Mesh* theTrack,
4958 const SMDS_MeshNode* theN1,
4959 const bool theHasAngles,
4960 list<double>& theAngles,
4961 const bool theLinearVariation,
4962 const bool theHasRefPoint,
4963 const gp_Pnt& theRefPoint,
4964 const bool theMakeGroups)
4966 myLastCreatedElems.Clear();
4967 myLastCreatedNodes.Clear();
4970 std::list<double> aPrms;
4971 TIDSortedElemSet::iterator itElem;
4974 TopoDS_Edge aTrackEdge;
4975 TopoDS_Vertex aV1, aV2;
4977 SMDS_ElemIteratorPtr aItE;
4978 SMDS_NodeIteratorPtr aItN;
4979 SMDSAbs_ElementType aTypeE;
4981 TNodeOfNodeListMap mapNewNodes;
4984 aNbE = theElements.size();
4987 return EXTR_NO_ELEMENTS;
4989 // 1.1 Track Pattern
4992 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4994 aItE = pMeshDS->elementsIterator();
4995 while ( aItE->more() ) {
4996 const SMDS_MeshElement* pE = aItE->next();
4997 aTypeE = pE->GetType();
4998 // Pattern must contain links only
4999 if ( aTypeE != SMDSAbs_Edge )
5000 return EXTR_PATH_NOT_EDGE;
5003 list<SMESH_MeshEditor_PathPoint> fullList;
5005 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5007 if( aS == SMESH_Mesh::PseudoShape() ) {
5008 //Mesh without shape
5009 const SMDS_MeshNode* currentNode = NULL;
5010 const SMDS_MeshNode* prevNode = theN1;
5011 std::vector<const SMDS_MeshNode*> aNodesList;
5012 aNodesList.push_back(theN1);
5013 int nbEdges = 0, conn=0;
5014 const SMDS_MeshElement* prevElem = NULL;
5015 const SMDS_MeshElement* currentElem = NULL;
5016 int totalNbEdges = theTrack->NbEdges();
5017 SMDS_ElemIteratorPtr nIt;
5020 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5021 return EXTR_BAD_STARTING_NODE;
5024 conn = nbEdgeConnectivity(theN1);
5026 return EXTR_PATH_NOT_EDGE;
5028 aItE = theN1->GetInverseElementIterator();
5029 prevElem = aItE->next();
5030 currentElem = prevElem;
5032 if(totalNbEdges == 1 ) {
5033 nIt = currentElem->nodesIterator();
5034 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5035 if(currentNode == prevNode)
5036 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5037 aNodesList.push_back(currentNode);
5039 nIt = currentElem->nodesIterator();
5040 while( nIt->more() ) {
5041 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5042 if(currentNode == prevNode)
5043 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5044 aNodesList.push_back(currentNode);
5046 //case of the closed mesh
5047 if(currentNode == theN1) {
5052 conn = nbEdgeConnectivity(currentNode);
5054 return EXTR_PATH_NOT_EDGE;
5055 }else if( conn == 1 && nbEdges > 0 ) {
5060 prevNode = currentNode;
5061 aItE = currentNode->GetInverseElementIterator();
5062 currentElem = aItE->next();
5063 if( currentElem == prevElem)
5064 currentElem = aItE->next();
5065 nIt = currentElem->nodesIterator();
5066 prevElem = currentElem;
5072 if(nbEdges != totalNbEdges)
5073 return EXTR_PATH_NOT_EDGE;
5075 TopTools_SequenceOfShape Edges;
5076 double x1,x2,y1,y2,z1,z2;
5077 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5078 int startNid = theN1->GetID();
5079 for(int i = 1; i < aNodesList.size(); i++) {
5080 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5081 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5082 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5083 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5084 list<SMESH_MeshEditor_PathPoint> LPP;
5086 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5087 LLPPs.push_back(LPP);
5088 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5089 else startNid = aNodesList[i-1]->GetID();
5093 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5094 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5095 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5096 for(; itPP!=firstList.end(); itPP++) {
5097 fullList.push_back( *itPP );
5100 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5101 SMESH_MeshEditor_PathPoint PP2;
5102 fullList.pop_back();
5104 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5105 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5106 itPP = currList.begin();
5107 PP2 = currList.front();
5108 gp_Dir D1 = PP1.Tangent();
5109 gp_Dir D2 = PP2.Tangent();
5110 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5111 (D1.Z()+D2.Z())/2 ) );
5112 PP1.SetTangent(Dnew);
5113 fullList.push_back(PP1);
5115 for(; itPP!=currList.end(); itPP++) {
5116 fullList.push_back( *itPP );
5118 PP1 = fullList.back();
5119 fullList.pop_back();
5121 fullList.push_back(PP1);
5123 } // Sub-shape for the Pattern must be an Edge or Wire
5124 else if( aS.ShapeType() == TopAbs_EDGE ) {
5125 aTrackEdge = TopoDS::Edge( aS );
5126 // the Edge must not be degenerated
5127 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5128 return EXTR_BAD_PATH_SHAPE;
5129 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5130 const SMDS_MeshNode* aN1 = 0;
5131 const SMDS_MeshNode* aN2 = 0;
5132 if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5133 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5136 if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5137 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5140 // starting node must be aN1 or aN2
5141 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5142 return EXTR_BAD_STARTING_NODE;
5143 aItN = pMeshDS->nodesIterator();
5144 while ( aItN->more() ) {
5145 const SMDS_MeshNode* pNode = aItN->next();
5146 if( pNode==aN1 || pNode==aN2 ) continue;
5147 const SMDS_EdgePosition* pEPos =
5148 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5149 double aT = pEPos->GetUParameter();
5150 aPrms.push_back( aT );
5152 //Extrusion_Error err =
5153 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5155 else if( aS.ShapeType() == TopAbs_WIRE ) {
5156 list< SMESH_subMesh* > LSM;
5157 TopTools_SequenceOfShape Edges;
5158 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5159 for(; eExp.More(); eExp.Next()) {
5160 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5161 if( BRep_Tool::Degenerated(E) ) continue;
5162 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5168 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5169 TopoDS_Vertex aVprev;
5170 TColStd_MapOfInteger UsedNums;
5171 int NbEdges = Edges.Length();
5173 for(; i<=NbEdges; i++) {
5175 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5176 for(; itLSM!=LSM.end(); itLSM++) {
5178 if(UsedNums.Contains(k)) continue;
5179 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5180 SMESH_subMesh* locTrack = *itLSM;
5181 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5182 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5183 bool aN1isOK = false, aN2isOK = false;
5184 if ( aVprev.IsNull() ) {
5185 // if previous vertex is not yet defined, it means that we in the beginning of wire
5186 // and we have to find initial vertex corresponding to starting node theN1
5187 const SMDS_MeshNode* aN1 = 0;
5188 const SMDS_MeshNode* aN2 = 0;
5190 if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5191 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5194 if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5195 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5198 // starting node must be aN1 or aN2
5199 aN1isOK = ( aN1 && aN1 == theN1 );
5200 aN2isOK = ( aN2 && aN2 == theN1 );
5203 // we have specified ending vertex of the previous edge on the previous iteration
5204 // and we have just to check that it corresponds to any vertex in current segment
5205 aN1isOK = aVprev.IsSame( aV1 );
5206 aN2isOK = aVprev.IsSame( aV2 );
5208 if ( !aN1isOK && !aN2isOK ) continue;
5209 // 2. Collect parameters on the track edge
5211 aItN = locMeshDS->GetNodes();
5212 while ( aItN->more() ) {
5213 const SMDS_MeshNode* pNode = aItN->next();
5214 const SMDS_EdgePosition* pEPos =
5215 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5216 double aT = pEPos->GetUParameter();
5217 aPrms.push_back( aT );
5219 list<SMESH_MeshEditor_PathPoint> LPP;
5220 //Extrusion_Error err =
5221 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5222 LLPPs.push_back(LPP);
5224 // update startN for search following egde
5225 if ( aN1isOK ) aVprev = aV2;
5230 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5231 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5232 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5233 for(; itPP!=firstList.end(); itPP++) {
5234 fullList.push_back( *itPP );
5236 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5237 fullList.pop_back();
5239 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5240 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5241 itPP = currList.begin();
5242 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5243 gp_Dir D1 = PP1.Tangent();
5244 gp_Dir D2 = PP2.Tangent();
5245 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5246 PP1.SetTangent(Dnew);
5247 fullList.push_back(PP1);
5249 for(; itPP!=currList.end(); itPP++) {
5250 fullList.push_back( *itPP );
5252 PP1 = fullList.back();
5253 fullList.pop_back();
5255 // if wire not closed
5256 fullList.push_back(PP1);
5260 return EXTR_BAD_PATH_SHAPE;
5263 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5264 theHasRefPoint, theRefPoint, theMakeGroups);
5268 //=======================================================================
5269 //function : MakeEdgePathPoints
5270 //purpose : auxilary for ExtrusionAlongTrack
5271 //=======================================================================
5272 SMESH_MeshEditor::Extrusion_Error
5273 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5274 const TopoDS_Edge& aTrackEdge,
5276 list<SMESH_MeshEditor_PathPoint>& LPP)
5278 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5280 aTolVec2=aTolVec*aTolVec;
5282 TopoDS_Vertex aV1, aV2;
5283 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5284 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5285 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5286 // 2. Collect parameters on the track edge
5287 aPrms.push_front( aT1 );
5288 aPrms.push_back( aT2 );
5291 if( FirstIsStart ) {
5302 SMESH_MeshEditor_PathPoint aPP;
5303 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5304 std::list<double>::iterator aItD = aPrms.begin();
5305 for(; aItD != aPrms.end(); ++aItD) {
5309 aC3D->D1( aT, aP3D, aVec );
5310 aL2 = aVec.SquareMagnitude();
5311 if ( aL2 < aTolVec2 )
5312 return EXTR_CANT_GET_TANGENT;
5313 gp_Dir aTgt( aVec );
5315 aPP.SetTangent( aTgt );
5316 aPP.SetParameter( aT );
5323 //=======================================================================
5324 //function : MakeExtrElements
5325 //purpose : auxilary for ExtrusionAlongTrack
5326 //=======================================================================
5327 SMESH_MeshEditor::Extrusion_Error
5328 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5329 list<SMESH_MeshEditor_PathPoint>& fullList,
5330 const bool theHasAngles,
5331 list<double>& theAngles,
5332 const bool theLinearVariation,
5333 const bool theHasRefPoint,
5334 const gp_Pnt& theRefPoint,
5335 const bool theMakeGroups)
5337 MESSAGE("MakeExtrElements");
5338 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5339 int aNbTP = fullList.size();
5340 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5342 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5343 LinearAngleVariation(aNbTP-1, theAngles);
5345 vector<double> aAngles( aNbTP );
5347 for(; j<aNbTP; ++j) {
5350 if ( theHasAngles ) {
5352 std::list<double>::iterator aItD = theAngles.begin();
5353 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5355 aAngles[j] = anAngle;
5358 // fill vector of path points with angles
5359 //aPPs.resize(fullList.size());
5361 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5362 for(; itPP!=fullList.end(); itPP++) {
5364 SMESH_MeshEditor_PathPoint PP = *itPP;
5365 PP.SetAngle(aAngles[j]);
5369 TNodeOfNodeListMap mapNewNodes;
5370 TElemOfVecOfNnlmiMap mapElemNewNodes;
5371 TElemOfElemListMap newElemsMap;
5372 TIDSortedElemSet::iterator itElem;
5375 SMDSAbs_ElementType aTypeE;
5376 // source elements for each generated one
5377 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5379 // 3. Center of rotation aV0
5380 gp_Pnt aV0 = theRefPoint;
5382 if ( !theHasRefPoint ) {
5384 aGC.SetCoord( 0.,0.,0. );
5386 itElem = theElements.begin();
5387 for ( ; itElem != theElements.end(); itElem++ ) {
5388 const SMDS_MeshElement* elem = *itElem;
5390 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5391 while ( itN->more() ) {
5392 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5397 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5398 list<const SMDS_MeshNode*> aLNx;
5399 mapNewNodes[node] = aLNx;
5401 gp_XYZ aXYZ( aX, aY, aZ );
5409 } // if (!theHasRefPoint) {
5410 mapNewNodes.clear();
5412 // 4. Processing the elements
5413 SMESHDS_Mesh* aMesh = GetMeshDS();
5415 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5416 // check element type
5417 const SMDS_MeshElement* elem = *itElem;
5418 aTypeE = elem->GetType();
5419 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5422 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5423 newNodesItVec.reserve( elem->NbNodes() );
5425 // loop on elem nodes
5427 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5428 while ( itN->more() )
5431 // check if a node has been already processed
5432 const SMDS_MeshNode* node =
5433 static_cast<const SMDS_MeshNode*>( itN->next() );
5434 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5435 if ( nIt == mapNewNodes.end() ) {
5436 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5437 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5440 aX = node->X(); aY = node->Y(); aZ = node->Z();
5442 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5443 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5444 gp_Ax1 anAx1, anAxT1T0;
5445 gp_Dir aDT1x, aDT0x, aDT1T0;
5450 aPN0.SetCoord(aX, aY, aZ);
5452 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5454 aDT0x= aPP0.Tangent();
5455 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5457 for ( j = 1; j < aNbTP; ++j ) {
5458 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5460 aDT1x = aPP1.Tangent();
5461 aAngle1x = aPP1.Angle();
5463 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5465 gp_Vec aV01x( aP0x, aP1x );
5466 aTrsf.SetTranslation( aV01x );
5469 aV1x = aV0x.Transformed( aTrsf );
5470 aPN1 = aPN0.Transformed( aTrsf );
5472 // rotation 1 [ T1,T0 ]
5473 aAngleT1T0=-aDT1x.Angle( aDT0x );
5474 if (fabs(aAngleT1T0) > aTolAng) {
5476 anAxT1T0.SetLocation( aV1x );
5477 anAxT1T0.SetDirection( aDT1T0 );
5478 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5480 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5484 if ( theHasAngles ) {
5485 anAx1.SetLocation( aV1x );
5486 anAx1.SetDirection( aDT1x );
5487 aTrsfRot.SetRotation( anAx1, aAngle1x );
5489 aPN1 = aPN1.Transformed( aTrsfRot );
5493 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5494 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5495 // create additional node
5496 double x = ( aPN1.X() + aPN0.X() )/2.;
5497 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5498 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5499 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5500 myLastCreatedNodes.Append(newNode);
5501 srcNodes.Append( node );
5502 listNewNodes.push_back( newNode );
5507 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5508 myLastCreatedNodes.Append(newNode);
5509 srcNodes.Append( node );
5510 listNewNodes.push_back( newNode );
5520 // if current elem is quadratic and current node is not medium
5521 // we have to check - may be it is needed to insert additional nodes
5522 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5523 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5524 if(listNewNodes.size()==aNbTP-1) {
5525 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5526 gp_XYZ P(node->X(), node->Y(), node->Z());
5527 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5529 for(i=0; i<aNbTP-1; i++) {
5530 const SMDS_MeshNode* N = *it;
5531 double x = ( N->X() + P.X() )/2.;
5532 double y = ( N->Y() + P.Y() )/2.;
5533 double z = ( N->Z() + P.Z() )/2.;
5534 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5535 srcNodes.Append( node );
5536 myLastCreatedNodes.Append(newN);
5539 P = gp_XYZ(N->X(),N->Y(),N->Z());
5541 listNewNodes.clear();
5542 for(i=0; i<2*(aNbTP-1); i++) {
5543 listNewNodes.push_back(aNodes[i]);
5549 newNodesItVec.push_back( nIt );
5551 // make new elements
5552 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5553 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5554 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5557 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5559 if ( theMakeGroups )
5560 generateGroups( srcNodes, srcElems, "extruded");
5566 //=======================================================================
5567 //function : LinearAngleVariation
5568 //purpose : auxilary for ExtrusionAlongTrack
5569 //=======================================================================
5570 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5571 list<double>& Angles)
5573 int nbAngles = Angles.size();
5574 if( nbSteps > nbAngles ) {
5575 vector<double> theAngles(nbAngles);
5576 list<double>::iterator it = Angles.begin();
5578 for(; it!=Angles.end(); it++) {
5580 theAngles[i] = (*it);
5583 double rAn2St = double( nbAngles ) / double( nbSteps );
5584 double angPrev = 0, angle;
5585 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5586 double angCur = rAn2St * ( iSt+1 );
5587 double angCurFloor = floor( angCur );
5588 double angPrevFloor = floor( angPrev );
5589 if ( angPrevFloor == angCurFloor )
5590 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5592 int iP = int( angPrevFloor );
5593 double angPrevCeil = ceil(angPrev);
5594 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5596 int iC = int( angCurFloor );
5597 if ( iC < nbAngles )
5598 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5600 iP = int( angPrevCeil );
5602 angle += theAngles[ iC ];
5604 res.push_back(angle);
5609 for(; it!=res.end(); it++)
5610 Angles.push_back( *it );
5615 //================================================================================
5617 * \brief Move or copy theElements applying theTrsf to their nodes
5618 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5619 * \param theTrsf - transformation to apply
5620 * \param theCopy - if true, create translated copies of theElems
5621 * \param theMakeGroups - if true and theCopy, create translated groups
5622 * \param theTargetMesh - mesh to copy translated elements into
5623 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5625 //================================================================================
5627 SMESH_MeshEditor::PGroupIDs
5628 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5629 const gp_Trsf& theTrsf,
5631 const bool theMakeGroups,
5632 SMESH_Mesh* theTargetMesh)
5634 myLastCreatedElems.Clear();
5635 myLastCreatedNodes.Clear();
5637 bool needReverse = false;
5638 string groupPostfix;
5639 switch ( theTrsf.Form() ) {
5641 MESSAGE("gp_PntMirror");
5643 groupPostfix = "mirrored";
5646 MESSAGE("gp_Ax1Mirror");
5647 groupPostfix = "mirrored";
5650 MESSAGE("gp_Ax2Mirror");
5652 groupPostfix = "mirrored";
5655 MESSAGE("gp_Rotation");
5656 groupPostfix = "rotated";
5658 case gp_Translation:
5659 MESSAGE("gp_Translation");
5660 groupPostfix = "translated";
5663 MESSAGE("gp_Scale");
5664 groupPostfix = "scaled";
5666 case gp_CompoundTrsf: // different scale by axis
5667 MESSAGE("gp_CompoundTrsf");
5668 groupPostfix = "scaled";
5672 needReverse = false;
5673 groupPostfix = "transformed";
5676 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5677 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5678 SMESHDS_Mesh* aMesh = GetMeshDS();
5681 // map old node to new one
5682 TNodeNodeMap nodeMap;
5684 // elements sharing moved nodes; those of them which have all
5685 // nodes mirrored but are not in theElems are to be reversed
5686 TIDSortedElemSet inverseElemSet;
5688 // source elements for each generated one
5689 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5691 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5692 TIDSortedElemSet orphanNode;
5694 if ( theElems.empty() ) // transform the whole mesh
5697 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5698 while ( eIt->more() ) theElems.insert( eIt->next() );
5700 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5701 while ( nIt->more() )
5703 const SMDS_MeshNode* node = nIt->next();
5704 if ( node->NbInverseElements() == 0)
5705 orphanNode.insert( node );
5709 // loop on elements to transform nodes : first orphan nodes then elems
5710 TIDSortedElemSet::iterator itElem;
5711 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5712 for (int i=0; i<2; i++)
5713 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5714 const SMDS_MeshElement* elem = *itElem;
5718 // loop on elem nodes
5719 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5720 while ( itN->more() ) {
5722 const SMDS_MeshNode* node = cast2Node( itN->next() );
5723 // check if a node has been already transformed
5724 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5725 nodeMap.insert( make_pair ( node, node ));
5726 if ( !n2n_isnew.second )
5730 coord[0] = node->X();
5731 coord[1] = node->Y();
5732 coord[2] = node->Z();
5733 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5734 if ( theTargetMesh ) {
5735 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5736 n2n_isnew.first->second = newNode;
5737 myLastCreatedNodes.Append(newNode);
5738 srcNodes.Append( node );
5740 else if ( theCopy ) {
5741 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5742 n2n_isnew.first->second = newNode;
5743 myLastCreatedNodes.Append(newNode);
5744 srcNodes.Append( node );
5747 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5748 // node position on shape becomes invalid
5749 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5750 ( SMDS_SpacePosition::originSpacePosition() );
5753 // keep inverse elements
5754 if ( !theCopy && !theTargetMesh && needReverse ) {
5755 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5756 while ( invElemIt->more() ) {
5757 const SMDS_MeshElement* iel = invElemIt->next();
5758 inverseElemSet.insert( iel );
5764 // either create new elements or reverse mirrored ones
5765 if ( !theCopy && !needReverse && !theTargetMesh )
5768 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5769 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5770 theElems.insert( *invElemIt );
5772 // Replicate or reverse elements
5774 std::vector<int> iForw;
5775 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5777 const SMDS_MeshElement* elem = *itElem;
5778 if ( !elem ) continue;
5780 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5781 int nbNodes = elem->NbNodes();
5782 if ( geomType == SMDSGeom_NONE ) continue; // node
5784 switch ( geomType ) {
5786 case SMDSGeom_POLYGON: // ---------------------- polygon
5788 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5790 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5791 while (itN->more()) {
5792 const SMDS_MeshNode* node =
5793 static_cast<const SMDS_MeshNode*>(itN->next());
5794 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5795 if (nodeMapIt == nodeMap.end())
5796 break; // not all nodes transformed
5798 // reverse mirrored faces and volumes
5799 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5801 poly_nodes[iNode] = (*nodeMapIt).second;
5805 if ( iNode != nbNodes )
5806 continue; // not all nodes transformed
5808 if ( theTargetMesh ) {
5809 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5810 srcElems.Append( elem );
5812 else if ( theCopy ) {
5813 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5814 srcElems.Append( elem );
5817 aMesh->ChangePolygonNodes(elem, poly_nodes);
5822 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5824 const SMDS_VtkVolume* aPolyedre =
5825 dynamic_cast<const SMDS_VtkVolume*>( elem );
5827 MESSAGE("Warning: bad volumic element");
5831 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5832 vector<int> quantities; quantities.reserve( nbNodes );
5834 bool allTransformed = true;
5835 int nbFaces = aPolyedre->NbFaces();
5836 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5837 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5838 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5839 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5840 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5841 if (nodeMapIt == nodeMap.end()) {
5842 allTransformed = false; // not all nodes transformed
5844 poly_nodes.push_back((*nodeMapIt).second);
5846 if ( needReverse && allTransformed )
5847 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5849 quantities.push_back(nbFaceNodes);
5851 if ( !allTransformed )
5852 continue; // not all nodes transformed
5854 if ( theTargetMesh ) {
5855 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5856 srcElems.Append( elem );
5858 else if ( theCopy ) {
5859 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5860 srcElems.Append( elem );
5863 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5868 case SMDSGeom_BALL: // -------------------- Ball
5870 if ( !theCopy && !theTargetMesh ) continue;
5872 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5873 if (nodeMapIt == nodeMap.end())
5874 continue; // not all nodes transformed
5876 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5877 if ( theTargetMesh ) {
5878 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5879 srcElems.Append( elem );
5882 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5883 srcElems.Append( elem );
5888 default: // ----------------------- Regular elements
5890 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5891 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5892 const std::vector<int>& i = needReverse ? iRev : iForw;
5894 // find transformed nodes
5895 vector<const SMDS_MeshNode*> nodes(nbNodes);
5897 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5898 while ( itN->more() ) {
5899 const SMDS_MeshNode* node =
5900 static_cast<const SMDS_MeshNode*>( itN->next() );
5901 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5902 if ( nodeMapIt == nodeMap.end() )
5903 break; // not all nodes transformed
5904 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5906 if ( iNode != nbNodes )
5907 continue; // not all nodes transformed
5909 if ( theTargetMesh ) {
5910 if ( SMDS_MeshElement* copy =
5911 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5912 myLastCreatedElems.Append( copy );
5913 srcElems.Append( elem );
5916 else if ( theCopy ) {
5917 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5918 srcElems.Append( elem );
5921 // reverse element as it was reversed by transformation
5923 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5925 } // switch ( geomType )
5927 } // loop on elements
5929 PGroupIDs newGroupIDs;
5931 if ( ( theMakeGroups && theCopy ) ||
5932 ( theMakeGroups && theTargetMesh ) )
5933 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5938 //=======================================================================
5940 * \brief Create groups of elements made during transformation
5941 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5942 * \param elemGens - elements making corresponding myLastCreatedElems
5943 * \param postfix - to append to names of new groups
5945 //=======================================================================
5947 SMESH_MeshEditor::PGroupIDs
5948 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5949 const SMESH_SequenceOfElemPtr& elemGens,
5950 const std::string& postfix,
5951 SMESH_Mesh* targetMesh)
5953 PGroupIDs newGroupIDs( new list<int> );
5954 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5956 // Sort existing groups by types and collect their names
5958 // to store an old group and a generated new ones
5960 using boost::make_tuple;
5961 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
5962 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5963 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5965 set< string > groupNames;
5967 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5968 if ( !groupIt->more() ) return newGroupIDs;
5970 int newGroupID = mesh->GetGroupIds().back()+1;
5971 while ( groupIt->more() )
5973 SMESH_Group * group = groupIt->next();
5974 if ( !group ) continue;
5975 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5976 if ( !groupDS || groupDS->IsEmpty() ) continue;
5977 groupNames.insert ( group->GetName() );
5978 groupDS->SetStoreName( group->GetName() );
5979 const SMDSAbs_ElementType type = groupDS->GetType();
5980 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
5981 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
5982 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
5983 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
5986 // Loop on nodes and elements to add them in new groups
5988 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5990 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5991 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5992 if ( gens.Length() != elems.Length() )
5993 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
5995 // loop on created elements
5996 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5998 const SMDS_MeshElement* sourceElem = gens( iElem );
5999 if ( !sourceElem ) {
6000 MESSAGE("generateGroups(): NULL source element");
6003 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6004 if ( groupsOldNew.empty() ) { // no groups of this type at all
6005 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6006 ++iElem; // skip all elements made by sourceElem
6009 // collect all elements made by the iElem-th sourceElem
6010 list< const SMDS_MeshElement* > resultElems;
6011 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6012 if ( resElem != sourceElem )
6013 resultElems.push_back( resElem );
6014 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6015 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6016 if ( resElem != sourceElem )
6017 resultElems.push_back( resElem );
6019 // there must be a top element
6020 const SMDS_MeshElement* topElem = 0;
6023 topElem = resultElems.back();
6024 resultElems.pop_back();
6028 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6029 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6030 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6032 topElem = *resElemIt;
6033 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6038 // add resultElems to groups originted from ones the sourceElem belongs to
6039 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6040 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6042 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6043 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6045 // fill in a new group
6046 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6047 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6048 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6049 newGroup.Add( *resElemIt );
6051 // fill a "top" group
6054 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6055 newTopGroup.Add( topElem );
6059 } // loop on created elements
6060 }// loop on nodes and elements
6062 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6064 list<int> topGrouIds;
6065 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6067 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6068 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6069 orderedOldNewGroups[i]->get<2>() };
6070 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6071 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6073 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6074 if ( newGroupDS->IsEmpty() )
6076 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6081 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6084 const bool isTop = ( nbNewGroups == 2 &&
6085 newGroupDS->GetType() == oldGroupDS->GetType() &&
6088 string name = oldGroupDS->GetStoreName();
6089 if ( !targetMesh ) {
6090 string suffix = ( isTop ? "top": postfix.c_str() );
6094 while ( !groupNames.insert( name ).second ) // name exists
6095 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6100 newGroupDS->SetStoreName( name.c_str() );
6102 // make a SMESH_Groups
6103 mesh->AddGroup( newGroupDS );
6105 topGrouIds.push_back( newGroupDS->GetID() );
6107 newGroupIDs->push_back( newGroupDS->GetID() );
6111 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6116 //================================================================================
6118 * \brief Return list of group of nodes close to each other within theTolerance
6119 * Search among theNodes or in the whole mesh if theNodes is empty using
6120 * an Octree algorithm
6122 //================================================================================
6124 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6125 const double theTolerance,
6126 TListOfListOfNodes & theGroupsOfNodes)
6128 myLastCreatedElems.Clear();
6129 myLastCreatedNodes.Clear();
6131 if ( theNodes.empty() )
6132 { // get all nodes in the mesh
6133 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6134 while ( nIt->more() )
6135 theNodes.insert( theNodes.end(),nIt->next());
6138 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6141 //=======================================================================
6142 //function : SimplifyFace
6144 //=======================================================================
6146 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6147 vector<const SMDS_MeshNode *>& poly_nodes,
6148 vector<int>& quantities) const
6150 int nbNodes = faceNodes.size();
6155 set<const SMDS_MeshNode*> nodeSet;
6157 // get simple seq of nodes
6158 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6159 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6160 int iSimple = 0, nbUnique = 0;
6162 simpleNodes[iSimple++] = faceNodes[0];
6164 for (int iCur = 1; iCur < nbNodes; iCur++) {
6165 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6166 simpleNodes[iSimple++] = faceNodes[iCur];
6167 if (nodeSet.insert( faceNodes[iCur] ).second)
6171 int nbSimple = iSimple;
6172 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6182 bool foundLoop = (nbSimple > nbUnique);
6185 set<const SMDS_MeshNode*> loopSet;
6186 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6187 const SMDS_MeshNode* n = simpleNodes[iSimple];
6188 if (!loopSet.insert( n ).second) {
6192 int iC = 0, curLast = iSimple;
6193 for (; iC < curLast; iC++) {
6194 if (simpleNodes[iC] == n) break;
6196 int loopLen = curLast - iC;
6198 // create sub-element
6200 quantities.push_back(loopLen);
6201 for (; iC < curLast; iC++) {
6202 poly_nodes.push_back(simpleNodes[iC]);
6205 // shift the rest nodes (place from the first loop position)
6206 for (iC = curLast + 1; iC < nbSimple; iC++) {
6207 simpleNodes[iC - loopLen] = simpleNodes[iC];
6209 nbSimple -= loopLen;
6212 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6213 } // while (foundLoop)
6217 quantities.push_back(iSimple);
6218 for (int i = 0; i < iSimple; i++)
6219 poly_nodes.push_back(simpleNodes[i]);
6225 //=======================================================================
6226 //function : MergeNodes
6227 //purpose : In each group, the cdr of nodes are substituted by the first one
6229 //=======================================================================
6231 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6233 MESSAGE("MergeNodes");
6234 myLastCreatedElems.Clear();
6235 myLastCreatedNodes.Clear();
6237 SMESHDS_Mesh* aMesh = GetMeshDS();
6239 TNodeNodeMap nodeNodeMap; // node to replace - new node
6240 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6241 list< int > rmElemIds, rmNodeIds;
6243 // Fill nodeNodeMap and elems
6245 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6246 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6247 list<const SMDS_MeshNode*>& nodes = *grIt;
6248 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6249 const SMDS_MeshNode* nToKeep = *nIt;
6250 //MESSAGE("node to keep " << nToKeep->GetID());
6251 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6252 const SMDS_MeshNode* nToRemove = *nIt;
6253 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6254 if ( nToRemove != nToKeep ) {
6255 //MESSAGE(" node to remove " << nToRemove->GetID());
6256 rmNodeIds.push_back( nToRemove->GetID() );
6257 AddToSameGroups( nToKeep, nToRemove, aMesh );
6258 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6259 // after MergeNodes() w/o creating node in place of merged ones.
6260 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6261 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6262 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6263 sm->SetIsAlwaysComputed( true );
6266 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6267 while ( invElemIt->more() ) {
6268 const SMDS_MeshElement* elem = invElemIt->next();
6273 // Change element nodes or remove an element
6275 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6276 for ( ; eIt != elems.end(); eIt++ ) {
6277 const SMDS_MeshElement* elem = *eIt;
6278 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6279 int nbNodes = elem->NbNodes();
6280 int aShapeId = FindShape( elem );
6282 set<const SMDS_MeshNode*> nodeSet;
6283 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6284 int iUnique = 0, iCur = 0, nbRepl = 0;
6285 vector<int> iRepl( nbNodes );
6287 // get new seq of nodes
6288 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289 while ( itN->more() ) {
6290 const SMDS_MeshNode* n =
6291 static_cast<const SMDS_MeshNode*>( itN->next() );
6293 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6294 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6296 // BUG 0020185: begin
6298 bool stopRecur = false;
6299 set<const SMDS_MeshNode*> nodesRecur;
6300 nodesRecur.insert(n);
6301 while (!stopRecur) {
6302 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6303 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6304 n = (*nnIt_i).second;
6305 if (!nodesRecur.insert(n).second) {
6306 // error: recursive dependancy
6316 curNodes[ iCur ] = n;
6317 bool isUnique = nodeSet.insert( n ).second;
6319 uniqueNodes[ iUnique++ ] = n;
6321 iRepl[ nbRepl++ ] = iCur;
6325 // Analyse element topology after replacement
6328 int nbUniqueNodes = nodeSet.size();
6329 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6330 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6331 // Polygons and Polyhedral volumes
6332 if (elem->IsPoly()) {
6334 if (elem->GetType() == SMDSAbs_Face) {
6336 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6338 for (; inode < nbNodes; inode++) {
6339 face_nodes[inode] = curNodes[inode];
6342 vector<const SMDS_MeshNode *> polygons_nodes;
6343 vector<int> quantities;
6344 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6347 for (int iface = 0; iface < nbNew; iface++) {
6348 int nbNodes = quantities[iface];
6349 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6350 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6351 poly_nodes[ii] = polygons_nodes[inode];
6353 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6354 myLastCreatedElems.Append(newElem);
6356 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6359 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6360 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6361 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6363 if (nbNew > 0) quid = nbNew - 1;
6364 vector<int> newquant(quantities.begin()+quid, quantities.end());
6365 const SMDS_MeshElement* newElem = 0;
6366 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6367 myLastCreatedElems.Append(newElem);
6368 if ( aShapeId && newElem )
6369 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6370 rmElemIds.push_back(elem->GetID());
6373 rmElemIds.push_back(elem->GetID());
6377 else if (elem->GetType() == SMDSAbs_Volume) {
6378 // Polyhedral volume
6379 if (nbUniqueNodes < 4) {
6380 rmElemIds.push_back(elem->GetID());
6383 // each face has to be analyzed in order to check volume validity
6384 const SMDS_VtkVolume* aPolyedre =
6385 dynamic_cast<const SMDS_VtkVolume*>( elem );
6387 int nbFaces = aPolyedre->NbFaces();
6389 vector<const SMDS_MeshNode *> poly_nodes;
6390 vector<int> quantities;
6392 for (int iface = 1; iface <= nbFaces; iface++) {
6393 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6394 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6396 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6397 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6398 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6399 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6400 faceNode = (*nnIt).second;
6402 faceNodes[inode - 1] = faceNode;
6405 SimplifyFace(faceNodes, poly_nodes, quantities);
6408 if (quantities.size() > 3) {
6409 // to be done: remove coincident faces
6412 if (quantities.size() > 3)
6414 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6415 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6416 const SMDS_MeshElement* newElem = 0;
6417 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6418 myLastCreatedElems.Append(newElem);
6419 if ( aShapeId && newElem )
6420 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6421 rmElemIds.push_back(elem->GetID());
6425 rmElemIds.push_back(elem->GetID());
6436 // TODO not all the possible cases are solved. Find something more generic?
6437 switch ( nbNodes ) {
6438 case 2: ///////////////////////////////////// EDGE
6439 isOk = false; break;
6440 case 3: ///////////////////////////////////// TRIANGLE
6441 isOk = false; break;
6443 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6445 else { //////////////////////////////////// QUADRANGLE
6446 if ( nbUniqueNodes < 3 )
6448 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6449 isOk = false; // opposite nodes stick
6450 //MESSAGE("isOk " << isOk);
6453 case 6: ///////////////////////////////////// PENTAHEDRON
6454 if ( nbUniqueNodes == 4 ) {
6455 // ---------------------------------> tetrahedron
6457 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6458 // all top nodes stick: reverse a bottom
6459 uniqueNodes[ 0 ] = curNodes [ 1 ];
6460 uniqueNodes[ 1 ] = curNodes [ 0 ];
6462 else if (nbRepl == 3 &&
6463 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6464 // all bottom nodes stick: set a top before
6465 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6466 uniqueNodes[ 0 ] = curNodes [ 3 ];
6467 uniqueNodes[ 1 ] = curNodes [ 4 ];
6468 uniqueNodes[ 2 ] = curNodes [ 5 ];
6470 else if (nbRepl == 4 &&
6471 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6472 // a lateral face turns into a line: reverse a bottom
6473 uniqueNodes[ 0 ] = curNodes [ 1 ];
6474 uniqueNodes[ 1 ] = curNodes [ 0 ];
6479 else if ( nbUniqueNodes == 5 ) {
6480 // PENTAHEDRON --------------------> 2 tetrahedrons
6481 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6482 // a bottom node sticks with a linked top one
6484 SMDS_MeshElement* newElem =
6485 aMesh->AddVolume(curNodes[ 3 ],
6488 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6489 myLastCreatedElems.Append(newElem);
6491 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6492 // 2. : reverse a bottom
6493 uniqueNodes[ 0 ] = curNodes [ 1 ];
6494 uniqueNodes[ 1 ] = curNodes [ 0 ];
6504 if(elem->IsQuadratic()) { // Quadratic quadrangle
6516 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
6519 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
6521 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
6522 uniqueNodes[0] = curNodes[0];
6523 uniqueNodes[1] = curNodes[2];
6524 uniqueNodes[2] = curNodes[3];
6525 uniqueNodes[3] = curNodes[5];
6526 uniqueNodes[4] = curNodes[6];
6527 uniqueNodes[5] = curNodes[7];
6530 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
6531 uniqueNodes[0] = curNodes[0];
6532 uniqueNodes[1] = curNodes[1];
6533 uniqueNodes[2] = curNodes[2];
6534 uniqueNodes[3] = curNodes[4];
6535 uniqueNodes[4] = curNodes[5];
6536 uniqueNodes[5] = curNodes[6];
6539 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
6540 uniqueNodes[0] = curNodes[1];
6541 uniqueNodes[1] = curNodes[2];
6542 uniqueNodes[2] = curNodes[3];
6543 uniqueNodes[3] = curNodes[5];
6544 uniqueNodes[4] = curNodes[6];
6545 uniqueNodes[5] = curNodes[0];
6548 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
6549 uniqueNodes[0] = curNodes[0];
6550 uniqueNodes[1] = curNodes[1];
6551 uniqueNodes[2] = curNodes[3];
6552 uniqueNodes[3] = curNodes[4];
6553 uniqueNodes[4] = curNodes[6];
6554 uniqueNodes[5] = curNodes[7];
6557 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
6558 uniqueNodes[0] = curNodes[0];
6559 uniqueNodes[1] = curNodes[2];
6560 uniqueNodes[2] = curNodes[3];
6561 uniqueNodes[3] = curNodes[1];
6562 uniqueNodes[4] = curNodes[6];
6563 uniqueNodes[5] = curNodes[7];
6566 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
6567 uniqueNodes[0] = curNodes[0];
6568 uniqueNodes[1] = curNodes[1];
6569 uniqueNodes[2] = curNodes[2];
6570 uniqueNodes[3] = curNodes[4];
6571 uniqueNodes[4] = curNodes[5];
6572 uniqueNodes[5] = curNodes[7];
6575 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
6576 uniqueNodes[0] = curNodes[0];
6577 uniqueNodes[1] = curNodes[1];
6578 uniqueNodes[2] = curNodes[3];
6579 uniqueNodes[3] = curNodes[4];
6580 uniqueNodes[4] = curNodes[2];
6581 uniqueNodes[5] = curNodes[7];
6584 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
6585 uniqueNodes[0] = curNodes[0];
6586 uniqueNodes[1] = curNodes[1];
6587 uniqueNodes[2] = curNodes[2];
6588 uniqueNodes[3] = curNodes[4];
6589 uniqueNodes[4] = curNodes[5];
6590 uniqueNodes[5] = curNodes[3];
6595 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
6598 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
6602 //////////////////////////////////// HEXAHEDRON
6604 SMDS_VolumeTool hexa (elem);
6605 hexa.SetExternalNormal();
6606 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
6607 //////////////////////// HEX ---> 1 tetrahedron
6608 for ( int iFace = 0; iFace < 6; iFace++ ) {
6609 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6610 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6611 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6612 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6613 // one face turns into a point ...
6614 int iOppFace = hexa.GetOppFaceIndex( iFace );
6615 ind = hexa.GetFaceNodesIndices( iOppFace );
6617 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
6618 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6621 if ( nbStick == 1 ) {
6622 // ... and the opposite one - into a triangle.
6624 ind = hexa.GetFaceNodesIndices( iFace );
6625 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
6632 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
6633 //////////////////////// HEX ---> 1 prism
6634 int nbTria = 0, iTria[3];
6635 const int *ind; // indices of face nodes
6636 // look for triangular faces
6637 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
6638 ind = hexa.GetFaceNodesIndices( iFace );
6639 TIDSortedNodeSet faceNodes;
6640 for ( iCur = 0; iCur < 4; iCur++ )
6641 faceNodes.insert( curNodes[ind[iCur]] );
6642 if ( faceNodes.size() == 3 )
6643 iTria[ nbTria++ ] = iFace;
6645 // check if triangles are opposite
6646 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
6649 // set nodes of the bottom triangle
6650 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
6652 for ( iCur = 0; iCur < 4; iCur++ )
6653 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
6654 indB.push_back( ind[iCur] );
6655 if ( !hexa.IsForward() )
6656 std::swap( indB[0], indB[2] );
6657 for ( iCur = 0; iCur < 3; iCur++ )
6658 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
6659 // set nodes of the top triangle
6660 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
6661 for ( iCur = 0; iCur < 3; ++iCur )
6662 for ( int j = 0; j < 4; ++j )
6663 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
6665 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
6671 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
6672 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
6673 for ( int iFace = 0; iFace < 6; iFace++ ) {
6674 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6675 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6676 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6677 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6678 // one face turns into a point ...
6679 int iOppFace = hexa.GetOppFaceIndex( iFace );
6680 ind = hexa.GetFaceNodesIndices( iOppFace );
6682 iUnique = 2; // reverse a tetrahedron 1 bottom
6683 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
6684 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6686 else if ( iUnique >= 0 )
6687 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
6689 if ( nbStick == 0 ) {
6690 // ... and the opposite one is a quadrangle
6692 const int* indTop = hexa.GetFaceNodesIndices( iFace );
6693 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
6696 SMDS_MeshElement* newElem =
6697 aMesh->AddVolume(curNodes[ind[ 0 ]],
6700 curNodes[indTop[ 0 ]]);
6701 myLastCreatedElems.Append(newElem);
6703 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6710 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
6711 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
6712 // find indices of quad and tri faces
6713 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
6714 for ( iFace = 0; iFace < 6; iFace++ ) {
6715 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6717 for ( iCur = 0; iCur < 4; iCur++ )
6718 nodeSet.insert( curNodes[ind[ iCur ]] );
6719 nbUniqueNodes = nodeSet.size();
6720 if ( nbUniqueNodes == 3 )
6721 iTriFace[ nbTri++ ] = iFace;
6722 else if ( nbUniqueNodes == 4 )
6723 iQuadFace[ nbQuad++ ] = iFace;
6725 if (nbQuad == 2 && nbTri == 4 &&
6726 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
6727 // 2 opposite quadrangles stuck with a diagonal;
6728 // sample groups of merged indices: (0-4)(2-6)
6729 // --------------------------------------------> 2 tetrahedrons
6730 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
6731 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
6732 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
6733 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
6734 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
6735 // stuck with 0-2 diagonal
6743 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
6744 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
6745 // stuck with 1-3 diagonal
6757 uniqueNodes[ 0 ] = curNodes [ i0 ];
6758 uniqueNodes[ 1 ] = curNodes [ i1d ];
6759 uniqueNodes[ 2 ] = curNodes [ i3d ];
6760 uniqueNodes[ 3 ] = curNodes [ i0t ];
6763 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
6767 myLastCreatedElems.Append(newElem);
6769 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6772 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
6773 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
6774 // --------------------------------------------> prism
6775 // find 2 opposite triangles
6777 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
6778 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
6779 // find indices of kept and replaced nodes
6780 // and fill unique nodes of 2 opposite triangles
6781 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
6782 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
6783 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
6784 // fill unique nodes
6787 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
6788 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
6789 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
6791 // iCur of a linked node of the opposite face (make normals co-directed):
6792 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
6793 // check that correspondent corners of triangles are linked
6794 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
6797 uniqueNodes[ iUnique ] = n;
6798 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
6807 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
6810 MESSAGE("MergeNodes() removes hexahedron "<< elem);
6817 } // switch ( nbNodes )
6819 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
6821 if ( isOk ) { // the elem remains valid after sticking nodes
6822 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
6824 // Change nodes of polyedre
6825 const SMDS_VtkVolume* aPolyedre =
6826 dynamic_cast<const SMDS_VtkVolume*>( elem );
6828 int nbFaces = aPolyedre->NbFaces();
6830 vector<const SMDS_MeshNode *> poly_nodes;
6831 vector<int> quantities (nbFaces);
6833 for (int iface = 1; iface <= nbFaces; iface++) {
6834 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6835 quantities[iface - 1] = nbFaceNodes;
6837 for (inode = 1; inode <= nbFaceNodes; inode++) {
6838 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
6840 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
6841 if (nnIt != nodeNodeMap.end()) { // curNode sticks
6842 curNode = (*nnIt).second;
6844 poly_nodes.push_back(curNode);
6847 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
6850 else // replace non-polyhedron elements
6852 const SMDSAbs_ElementType etyp = elem->GetType();
6853 const int elemId = elem->GetID();
6854 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
6855 uniqueNodes.resize(nbUniqueNodes);
6857 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
6859 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6860 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
6861 if ( sm && newElem )
6862 sm->AddElement( newElem );
6863 if ( elem != newElem )
6864 ReplaceElemInGroups( elem, newElem, aMesh );
6868 // Remove invalid regular element or invalid polygon
6869 rmElemIds.push_back( elem->GetID() );
6872 } // loop on elements
6874 // Remove bad elements, then equal nodes (order important)
6876 Remove( rmElemIds, false );
6877 Remove( rmNodeIds, true );
6882 // ========================================================
6883 // class : SortableElement
6884 // purpose : allow sorting elements basing on their nodes
6885 // ========================================================
6886 class SortableElement : public set <const SMDS_MeshElement*>
6890 SortableElement( const SMDS_MeshElement* theElem )
6893 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
6894 while ( nodeIt->more() )
6895 this->insert( nodeIt->next() );
6898 const SMDS_MeshElement* Get() const
6901 void Set(const SMDS_MeshElement* e) const
6906 mutable const SMDS_MeshElement* myElem;
6909 //=======================================================================
6910 //function : FindEqualElements
6911 //purpose : Return list of group of elements built on the same nodes.
6912 // Search among theElements or in the whole mesh if theElements is empty
6913 //=======================================================================
6915 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
6916 TListOfListOfElementsID & theGroupsOfElementsID)
6918 myLastCreatedElems.Clear();
6919 myLastCreatedNodes.Clear();
6921 typedef map< SortableElement, int > TMapOfNodeSet;
6922 typedef list<int> TGroupOfElems;
6924 if ( theElements.empty() )
6925 { // get all elements in the mesh
6926 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
6927 while ( eIt->more() )
6928 theElements.insert( theElements.end(), eIt->next());
6931 vector< TGroupOfElems > arrayOfGroups;
6932 TGroupOfElems groupOfElems;
6933 TMapOfNodeSet mapOfNodeSet;
6935 TIDSortedElemSet::iterator elemIt = theElements.begin();
6936 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
6937 const SMDS_MeshElement* curElem = *elemIt;
6938 SortableElement SE(curElem);
6941 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
6942 if( !(pp.second) ) {
6943 TMapOfNodeSet::iterator& itSE = pp.first;
6944 ind = (*itSE).second;
6945 arrayOfGroups[ind].push_back(curElem->GetID());
6948 groupOfElems.clear();
6949 groupOfElems.push_back(curElem->GetID());
6950 arrayOfGroups.push_back(groupOfElems);
6955 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
6956 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
6957 groupOfElems = *groupIt;
6958 if ( groupOfElems.size() > 1 ) {
6959 groupOfElems.sort();
6960 theGroupsOfElementsID.push_back(groupOfElems);
6965 //=======================================================================
6966 //function : MergeElements
6967 //purpose : In each given group, substitute all elements by the first one.
6968 //=======================================================================
6970 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
6972 myLastCreatedElems.Clear();
6973 myLastCreatedNodes.Clear();
6975 typedef list<int> TListOfIDs;
6976 TListOfIDs rmElemIds; // IDs of elems to remove
6978 SMESHDS_Mesh* aMesh = GetMeshDS();
6980 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
6981 while ( groupsIt != theGroupsOfElementsID.end() ) {
6982 TListOfIDs& aGroupOfElemID = *groupsIt;
6983 aGroupOfElemID.sort();
6984 int elemIDToKeep = aGroupOfElemID.front();
6985 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
6986 aGroupOfElemID.pop_front();
6987 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
6988 while ( idIt != aGroupOfElemID.end() ) {
6989 int elemIDToRemove = *idIt;
6990 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
6991 // add the kept element in groups of removed one (PAL15188)
6992 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
6993 rmElemIds.push_back( elemIDToRemove );
6999 Remove( rmElemIds, false );
7002 //=======================================================================
7003 //function : MergeEqualElements
7004 //purpose : Remove all but one of elements built on the same nodes.
7005 //=======================================================================
7007 void SMESH_MeshEditor::MergeEqualElements()
7009 TIDSortedElemSet aMeshElements; /* empty input ==
7010 to merge equal elements in the whole mesh */
7011 TListOfListOfElementsID aGroupsOfElementsID;
7012 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7013 MergeElements(aGroupsOfElementsID);
7016 //=======================================================================
7017 //function : findAdjacentFace
7019 //=======================================================================
7021 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7022 const SMDS_MeshNode* n2,
7023 const SMDS_MeshElement* elem)
7025 TIDSortedElemSet elemSet, avoidSet;
7027 avoidSet.insert ( elem );
7028 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7031 //=======================================================================
7032 //function : FindFreeBorder
7034 //=======================================================================
7036 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7038 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7039 const SMDS_MeshNode* theSecondNode,
7040 const SMDS_MeshNode* theLastNode,
7041 list< const SMDS_MeshNode* > & theNodes,
7042 list< const SMDS_MeshElement* >& theFaces)
7044 if ( !theFirstNode || !theSecondNode )
7046 // find border face between theFirstNode and theSecondNode
7047 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7051 theFaces.push_back( curElem );
7052 theNodes.push_back( theFirstNode );
7053 theNodes.push_back( theSecondNode );
7055 //vector<const SMDS_MeshNode*> nodes;
7056 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7057 TIDSortedElemSet foundElems;
7058 bool needTheLast = ( theLastNode != 0 );
7060 while ( nStart != theLastNode ) {
7061 if ( nStart == theFirstNode )
7062 return !needTheLast;
7064 // find all free border faces sharing form nStart
7066 list< const SMDS_MeshElement* > curElemList;
7067 list< const SMDS_MeshNode* > nStartList;
7068 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7069 while ( invElemIt->more() ) {
7070 const SMDS_MeshElement* e = invElemIt->next();
7071 if ( e == curElem || foundElems.insert( e ).second ) {
7073 int iNode = 0, nbNodes = e->NbNodes();
7074 //const SMDS_MeshNode* nodes[nbNodes+1];
7075 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7077 if(e->IsQuadratic()) {
7078 const SMDS_VtkFace* F =
7079 dynamic_cast<const SMDS_VtkFace*>(e);
7080 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7081 // use special nodes iterator
7082 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7083 while( anIter->more() ) {
7084 nodes[ iNode++ ] = cast2Node(anIter->next());
7088 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7089 while ( nIt->more() )
7090 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7092 nodes[ iNode ] = nodes[ 0 ];
7094 for ( iNode = 0; iNode < nbNodes; iNode++ )
7095 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7096 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7097 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7099 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7100 curElemList.push_back( e );
7104 // analyse the found
7106 int nbNewBorders = curElemList.size();
7107 if ( nbNewBorders == 0 ) {
7108 // no free border furthermore
7109 return !needTheLast;
7111 else if ( nbNewBorders == 1 ) {
7112 // one more element found
7114 nStart = nStartList.front();
7115 curElem = curElemList.front();
7116 theFaces.push_back( curElem );
7117 theNodes.push_back( nStart );
7120 // several continuations found
7121 list< const SMDS_MeshElement* >::iterator curElemIt;
7122 list< const SMDS_MeshNode* >::iterator nStartIt;
7123 // check if one of them reached the last node
7124 if ( needTheLast ) {
7125 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7126 curElemIt!= curElemList.end();
7127 curElemIt++, nStartIt++ )
7128 if ( *nStartIt == theLastNode ) {
7129 theFaces.push_back( *curElemIt );
7130 theNodes.push_back( *nStartIt );
7134 // find the best free border by the continuations
7135 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7136 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7137 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7138 curElemIt!= curElemList.end();
7139 curElemIt++, nStartIt++ )
7141 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7142 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7143 // find one more free border
7144 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7148 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7149 // choice: clear a worse one
7150 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7151 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7152 contNodes[ iWorse ].clear();
7153 contFaces[ iWorse ].clear();
7156 if ( contNodes[0].empty() && contNodes[1].empty() )
7159 // append the best free border
7160 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7161 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7162 theNodes.pop_back(); // remove nIgnore
7163 theNodes.pop_back(); // remove nStart
7164 theFaces.pop_back(); // remove curElem
7165 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7166 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7167 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7168 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7171 } // several continuations found
7172 } // while ( nStart != theLastNode )
7177 //=======================================================================
7178 //function : CheckFreeBorderNodes
7179 //purpose : Return true if the tree nodes are on a free border
7180 //=======================================================================
7182 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7183 const SMDS_MeshNode* theNode2,
7184 const SMDS_MeshNode* theNode3)
7186 list< const SMDS_MeshNode* > nodes;
7187 list< const SMDS_MeshElement* > faces;
7188 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7191 //=======================================================================
7192 //function : SewFreeBorder
7194 //=======================================================================
7196 SMESH_MeshEditor::Sew_Error
7197 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7198 const SMDS_MeshNode* theBordSecondNode,
7199 const SMDS_MeshNode* theBordLastNode,
7200 const SMDS_MeshNode* theSideFirstNode,
7201 const SMDS_MeshNode* theSideSecondNode,
7202 const SMDS_MeshNode* theSideThirdNode,
7203 const bool theSideIsFreeBorder,
7204 const bool toCreatePolygons,
7205 const bool toCreatePolyedrs)
7207 myLastCreatedElems.Clear();
7208 myLastCreatedNodes.Clear();
7210 MESSAGE("::SewFreeBorder()");
7211 Sew_Error aResult = SEW_OK;
7213 // ====================================
7214 // find side nodes and elements
7215 // ====================================
7217 list< const SMDS_MeshNode* > nSide[ 2 ];
7218 list< const SMDS_MeshElement* > eSide[ 2 ];
7219 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7220 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7224 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7225 nSide[0], eSide[0])) {
7226 MESSAGE(" Free Border 1 not found " );
7227 aResult = SEW_BORDER1_NOT_FOUND;
7229 if (theSideIsFreeBorder) {
7232 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7233 nSide[1], eSide[1])) {
7234 MESSAGE(" Free Border 2 not found " );
7235 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7238 if ( aResult != SEW_OK )
7241 if (!theSideIsFreeBorder) {
7245 // -------------------------------------------------------------------------
7247 // 1. If nodes to merge are not coincident, move nodes of the free border
7248 // from the coord sys defined by the direction from the first to last
7249 // nodes of the border to the correspondent sys of the side 2
7250 // 2. On the side 2, find the links most co-directed with the correspondent
7251 // links of the free border
7252 // -------------------------------------------------------------------------
7254 // 1. Since sewing may break if there are volumes to split on the side 2,
7255 // we wont move nodes but just compute new coordinates for them
7256 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7257 TNodeXYZMap nBordXYZ;
7258 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7259 list< const SMDS_MeshNode* >::iterator nBordIt;
7261 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7262 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7263 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7264 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7265 double tol2 = 1.e-8;
7266 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7267 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7268 // Need node movement.
7270 // find X and Z axes to create trsf
7271 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7273 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7275 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7278 gp_Ax3 toBordAx( Pb1, Zb, X );
7279 gp_Ax3 fromSideAx( Ps1, Zs, X );
7280 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7282 gp_Trsf toBordSys, fromSide2Sys;
7283 toBordSys.SetTransformation( toBordAx );
7284 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7285 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7288 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7289 const SMDS_MeshNode* n = *nBordIt;
7290 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7291 toBordSys.Transforms( xyz );
7292 fromSide2Sys.Transforms( xyz );
7293 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7297 // just insert nodes XYZ in the nBordXYZ map
7298 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7299 const SMDS_MeshNode* n = *nBordIt;
7300 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7304 // 2. On the side 2, find the links most co-directed with the correspondent
7305 // links of the free border
7307 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7308 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7309 sideNodes.push_back( theSideFirstNode );
7311 bool hasVolumes = false;
7312 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7313 set<long> foundSideLinkIDs, checkedLinkIDs;
7314 SMDS_VolumeTool volume;
7315 //const SMDS_MeshNode* faceNodes[ 4 ];
7317 const SMDS_MeshNode* sideNode;
7318 const SMDS_MeshElement* sideElem;
7319 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7320 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7321 nBordIt = bordNodes.begin();
7323 // border node position and border link direction to compare with
7324 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7325 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7326 // choose next side node by link direction or by closeness to
7327 // the current border node:
7328 bool searchByDir = ( *nBordIt != theBordLastNode );
7330 // find the next node on the Side 2
7332 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7334 checkedLinkIDs.clear();
7335 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7337 // loop on inverse elements of current node (prevSideNode) on the Side 2
7338 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7339 while ( invElemIt->more() )
7341 const SMDS_MeshElement* elem = invElemIt->next();
7342 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7343 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7344 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7345 bool isVolume = volume.Set( elem );
7346 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7347 if ( isVolume ) // --volume
7349 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7350 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7351 if(elem->IsQuadratic()) {
7352 const SMDS_VtkFace* F =
7353 dynamic_cast<const SMDS_VtkFace*>(elem);
7354 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7355 // use special nodes iterator
7356 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7357 while( anIter->more() ) {
7358 nodes[ iNode ] = cast2Node(anIter->next());
7359 if ( nodes[ iNode++ ] == prevSideNode )
7360 iPrevNode = iNode - 1;
7364 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7365 while ( nIt->more() ) {
7366 nodes[ iNode ] = cast2Node( nIt->next() );
7367 if ( nodes[ iNode++ ] == prevSideNode )
7368 iPrevNode = iNode - 1;
7371 // there are 2 links to check
7376 // loop on links, to be precise, on the second node of links
7377 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7378 const SMDS_MeshNode* n = nodes[ iNode ];
7380 if ( !volume.IsLinked( n, prevSideNode ))
7384 if ( iNode ) // a node before prevSideNode
7385 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7386 else // a node after prevSideNode
7387 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7389 // check if this link was already used
7390 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7391 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7392 if (!isJustChecked &&
7393 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7395 // test a link geometrically
7396 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7397 bool linkIsBetter = false;
7398 double dot = 0.0, dist = 0.0;
7399 if ( searchByDir ) { // choose most co-directed link
7400 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7401 linkIsBetter = ( dot > maxDot );
7403 else { // choose link with the node closest to bordPos
7404 dist = ( nextXYZ - bordPos ).SquareModulus();
7405 linkIsBetter = ( dist < minDist );
7407 if ( linkIsBetter ) {
7416 } // loop on inverse elements of prevSideNode
7419 MESSAGE(" Cant find path by links of the Side 2 ");
7420 return SEW_BAD_SIDE_NODES;
7422 sideNodes.push_back( sideNode );
7423 sideElems.push_back( sideElem );
7424 foundSideLinkIDs.insert ( linkID );
7425 prevSideNode = sideNode;
7427 if ( *nBordIt == theBordLastNode )
7428 searchByDir = false;
7430 // find the next border link to compare with
7431 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7432 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7433 // move to next border node if sideNode is before forward border node (bordPos)
7434 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7435 prevBordNode = *nBordIt;
7437 bordPos = nBordXYZ[ *nBordIt ];
7438 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7439 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7443 while ( sideNode != theSideSecondNode );
7445 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7446 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7447 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7449 } // end nodes search on the side 2
7451 // ============================
7452 // sew the border to the side 2
7453 // ============================
7455 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7456 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7458 TListOfListOfNodes nodeGroupsToMerge;
7459 if ( nbNodes[0] == nbNodes[1] ||
7460 ( theSideIsFreeBorder && !theSideThirdNode)) {
7462 // all nodes are to be merged
7464 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7465 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7466 nIt[0]++, nIt[1]++ )
7468 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7469 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7470 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7475 // insert new nodes into the border and the side to get equal nb of segments
7477 // get normalized parameters of nodes on the borders
7478 //double param[ 2 ][ maxNbNodes ];
7480 param[0] = new double [ maxNbNodes ];
7481 param[1] = new double [ maxNbNodes ];
7483 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7484 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7485 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7486 const SMDS_MeshNode* nPrev = *nIt;
7487 double bordLength = 0;
7488 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7489 const SMDS_MeshNode* nCur = *nIt;
7490 gp_XYZ segment (nCur->X() - nPrev->X(),
7491 nCur->Y() - nPrev->Y(),
7492 nCur->Z() - nPrev->Z());
7493 double segmentLen = segment.Modulus();
7494 bordLength += segmentLen;
7495 param[ iBord ][ iNode ] = bordLength;
7498 // normalize within [0,1]
7499 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7500 param[ iBord ][ iNode ] /= bordLength;
7504 // loop on border segments
7505 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
7506 int i[ 2 ] = { 0, 0 };
7507 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
7508 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
7510 TElemOfNodeListMap insertMap;
7511 TElemOfNodeListMap::iterator insertMapIt;
7513 // key: elem to insert nodes into
7514 // value: 2 nodes to insert between + nodes to be inserted
7516 bool next[ 2 ] = { false, false };
7518 // find min adjacent segment length after sewing
7519 double nextParam = 10., prevParam = 0;
7520 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7521 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
7522 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
7523 if ( i[ iBord ] > 0 )
7524 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
7526 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7527 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7528 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
7530 // choose to insert or to merge nodes
7531 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
7532 if ( Abs( du ) <= minSegLen * 0.2 ) {
7535 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7536 const SMDS_MeshNode* n0 = *nIt[0];
7537 const SMDS_MeshNode* n1 = *nIt[1];
7538 nodeGroupsToMerge.back().push_back( n1 );
7539 nodeGroupsToMerge.back().push_back( n0 );
7540 // position of node of the border changes due to merge
7541 param[ 0 ][ i[0] ] += du;
7542 // move n1 for the sake of elem shape evaluation during insertion.
7543 // n1 will be removed by MergeNodes() anyway
7544 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
7545 next[0] = next[1] = true;
7550 int intoBord = ( du < 0 ) ? 0 : 1;
7551 const SMDS_MeshElement* elem = *eIt[ intoBord ];
7552 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
7553 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
7554 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
7555 if ( intoBord == 1 ) {
7556 // move node of the border to be on a link of elem of the side
7557 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
7558 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
7559 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
7560 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
7561 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
7563 insertMapIt = insertMap.find( elem );
7564 bool notFound = ( insertMapIt == insertMap.end() );
7565 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
7567 // insert into another link of the same element:
7568 // 1. perform insertion into the other link of the elem
7569 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7570 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
7571 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
7572 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
7573 // 2. perform insertion into the link of adjacent faces
7575 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
7577 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
7581 if (toCreatePolyedrs) {
7582 // perform insertion into the links of adjacent volumes
7583 UpdateVolumes(n12, n22, nodeList);
7585 // 3. find an element appeared on n1 and n2 after the insertion
7586 insertMap.erase( elem );
7587 elem = findAdjacentFace( n1, n2, 0 );
7589 if ( notFound || otherLink ) {
7590 // add element and nodes of the side into the insertMap
7591 insertMapIt = insertMap.insert
7592 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
7593 (*insertMapIt).second.push_back( n1 );
7594 (*insertMapIt).second.push_back( n2 );
7596 // add node to be inserted into elem
7597 (*insertMapIt).second.push_back( nIns );
7598 next[ 1 - intoBord ] = true;
7601 // go to the next segment
7602 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7603 if ( next[ iBord ] ) {
7604 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
7606 nPrev[ iBord ] = *nIt[ iBord ];
7607 nIt[ iBord ]++; i[ iBord ]++;
7611 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
7613 // perform insertion of nodes into elements
7615 for (insertMapIt = insertMap.begin();
7616 insertMapIt != insertMap.end();
7619 const SMDS_MeshElement* elem = (*insertMapIt).first;
7620 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7621 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
7622 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
7624 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
7626 if ( !theSideIsFreeBorder ) {
7627 // look for and insert nodes into the faces adjacent to elem
7629 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
7631 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
7636 if (toCreatePolyedrs) {
7637 // perform insertion into the links of adjacent volumes
7638 UpdateVolumes(n1, n2, nodeList);
7644 } // end: insert new nodes
7646 MergeNodes ( nodeGroupsToMerge );
7651 //=======================================================================
7652 //function : InsertNodesIntoLink
7653 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
7654 // and theBetweenNode2 and split theElement
7655 //=======================================================================
7657 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
7658 const SMDS_MeshNode* theBetweenNode1,
7659 const SMDS_MeshNode* theBetweenNode2,
7660 list<const SMDS_MeshNode*>& theNodesToInsert,
7661 const bool toCreatePoly)
7663 if ( theFace->GetType() != SMDSAbs_Face ) return;
7665 // find indices of 2 link nodes and of the rest nodes
7666 int iNode = 0, il1, il2, i3, i4;
7667 il1 = il2 = i3 = i4 = -1;
7668 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
7669 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
7671 if(theFace->IsQuadratic()) {
7672 const SMDS_VtkFace* F =
7673 dynamic_cast<const SMDS_VtkFace*>(theFace);
7674 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7675 // use special nodes iterator
7676 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7677 while( anIter->more() ) {
7678 const SMDS_MeshNode* n = cast2Node(anIter->next());
7679 if ( n == theBetweenNode1 )
7681 else if ( n == theBetweenNode2 )
7687 nodes[ iNode++ ] = n;
7691 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7692 while ( nodeIt->more() ) {
7693 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7694 if ( n == theBetweenNode1 )
7696 else if ( n == theBetweenNode2 )
7702 nodes[ iNode++ ] = n;
7705 if ( il1 < 0 || il2 < 0 || i3 < 0 )
7708 // arrange link nodes to go one after another regarding the face orientation
7709 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
7710 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
7715 aNodesToInsert.reverse();
7717 // check that not link nodes of a quadrangles are in good order
7718 int nbFaceNodes = theFace->NbNodes();
7719 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
7725 if (toCreatePoly || theFace->IsPoly()) {
7728 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
7730 // add nodes of face up to first node of link
7733 if(theFace->IsQuadratic()) {
7734 const SMDS_VtkFace* F =
7735 dynamic_cast<const SMDS_VtkFace*>(theFace);
7736 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7737 // use special nodes iterator
7738 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7739 while( anIter->more() && !isFLN ) {
7740 const SMDS_MeshNode* n = cast2Node(anIter->next());
7741 poly_nodes[iNode++] = n;
7742 if (n == nodes[il1]) {
7746 // add nodes to insert
7747 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7748 for (; nIt != aNodesToInsert.end(); nIt++) {
7749 poly_nodes[iNode++] = *nIt;
7751 // add nodes of face starting from last node of link
7752 while ( anIter->more() ) {
7753 poly_nodes[iNode++] = cast2Node(anIter->next());
7757 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7758 while ( nodeIt->more() && !isFLN ) {
7759 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7760 poly_nodes[iNode++] = n;
7761 if (n == nodes[il1]) {
7765 // add nodes to insert
7766 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7767 for (; nIt != aNodesToInsert.end(); nIt++) {
7768 poly_nodes[iNode++] = *nIt;
7770 // add nodes of face starting from last node of link
7771 while ( nodeIt->more() ) {
7772 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7773 poly_nodes[iNode++] = n;
7777 // edit or replace the face
7778 SMESHDS_Mesh *aMesh = GetMeshDS();
7780 if (theFace->IsPoly()) {
7781 aMesh->ChangePolygonNodes(theFace, poly_nodes);
7784 int aShapeId = FindShape( theFace );
7786 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7787 myLastCreatedElems.Append(newElem);
7788 if ( aShapeId && newElem )
7789 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7791 aMesh->RemoveElement(theFace);
7796 SMESHDS_Mesh *aMesh = GetMeshDS();
7797 if( !theFace->IsQuadratic() ) {
7799 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
7800 int nbLinkNodes = 2 + aNodesToInsert.size();
7801 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
7802 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
7803 linkNodes[ 0 ] = nodes[ il1 ];
7804 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
7805 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7806 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
7807 linkNodes[ iNode++ ] = *nIt;
7809 // decide how to split a quadrangle: compare possible variants
7810 // and choose which of splits to be a quadrangle
7811 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
7812 if ( nbFaceNodes == 3 ) {
7813 iBestQuad = nbSplits;
7816 else if ( nbFaceNodes == 4 ) {
7817 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
7818 double aBestRate = DBL_MAX;
7819 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
7821 double aBadRate = 0;
7822 // evaluate elements quality
7823 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
7824 if ( iSplit == iQuad ) {
7825 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
7829 aBadRate += getBadRate( &quad, aCrit );
7832 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
7834 nodes[ iSplit < iQuad ? i4 : i3 ]);
7835 aBadRate += getBadRate( &tria, aCrit );
7839 if ( aBadRate < aBestRate ) {
7841 aBestRate = aBadRate;
7846 // create new elements
7847 int aShapeId = FindShape( theFace );
7850 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
7851 SMDS_MeshElement* newElem = 0;
7852 if ( iSplit == iBestQuad )
7853 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7858 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7860 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
7861 myLastCreatedElems.Append(newElem);
7862 if ( aShapeId && newElem )
7863 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7866 // change nodes of theFace
7867 const SMDS_MeshNode* newNodes[ 4 ];
7868 newNodes[ 0 ] = linkNodes[ i1 ];
7869 newNodes[ 1 ] = linkNodes[ i2 ];
7870 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
7871 newNodes[ 3 ] = nodes[ i4 ];
7872 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
7873 const SMDS_MeshElement* newElem = 0;
7874 if (iSplit == iBestQuad)
7875 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
7877 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
7878 myLastCreatedElems.Append(newElem);
7879 if ( aShapeId && newElem )
7880 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7881 } // end if(!theFace->IsQuadratic())
7882 else { // theFace is quadratic
7883 // we have to split theFace on simple triangles and one simple quadrangle
7885 int nbshift = tmp*2;
7886 // shift nodes in nodes[] by nbshift
7888 for(i=0; i<nbshift; i++) {
7889 const SMDS_MeshNode* n = nodes[0];
7890 for(j=0; j<nbFaceNodes-1; j++) {
7891 nodes[j] = nodes[j+1];
7893 nodes[nbFaceNodes-1] = n;
7895 il1 = il1 - nbshift;
7896 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
7897 // n0 n1 n2 n0 n1 n2
7898 // +-----+-----+ +-----+-----+
7907 // create new elements
7908 int aShapeId = FindShape( theFace );
7911 if(nbFaceNodes==6) { // quadratic triangle
7912 SMDS_MeshElement* newElem =
7913 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
7914 myLastCreatedElems.Append(newElem);
7915 if ( aShapeId && newElem )
7916 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7917 if(theFace->IsMediumNode(nodes[il1])) {
7918 // create quadrangle
7919 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
7920 myLastCreatedElems.Append(newElem);
7921 if ( aShapeId && newElem )
7922 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7928 // create quadrangle
7929 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
7930 myLastCreatedElems.Append(newElem);
7931 if ( aShapeId && newElem )
7932 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7938 else { // nbFaceNodes==8 - quadratic quadrangle
7939 SMDS_MeshElement* newElem =
7940 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
7941 myLastCreatedElems.Append(newElem);
7942 if ( aShapeId && newElem )
7943 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7944 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
7945 myLastCreatedElems.Append(newElem);
7946 if ( aShapeId && newElem )
7947 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7948 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
7949 myLastCreatedElems.Append(newElem);
7950 if ( aShapeId && newElem )
7951 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7952 if(theFace->IsMediumNode(nodes[il1])) {
7953 // create quadrangle
7954 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
7955 myLastCreatedElems.Append(newElem);
7956 if ( aShapeId && newElem )
7957 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7963 // create quadrangle
7964 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
7965 myLastCreatedElems.Append(newElem);
7966 if ( aShapeId && newElem )
7967 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7973 // create needed triangles using n1,n2,n3 and inserted nodes
7974 int nbn = 2 + aNodesToInsert.size();
7975 //const SMDS_MeshNode* aNodes[nbn];
7976 vector<const SMDS_MeshNode*> aNodes(nbn);
7977 aNodes[0] = nodes[n1];
7978 aNodes[nbn-1] = nodes[n2];
7979 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7980 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
7981 aNodes[iNode++] = *nIt;
7983 for(i=1; i<nbn; i++) {
7984 SMDS_MeshElement* newElem =
7985 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
7986 myLastCreatedElems.Append(newElem);
7987 if ( aShapeId && newElem )
7988 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7992 aMesh->RemoveElement(theFace);
7995 //=======================================================================
7996 //function : UpdateVolumes
7998 //=======================================================================
7999 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8000 const SMDS_MeshNode* theBetweenNode2,
8001 list<const SMDS_MeshNode*>& theNodesToInsert)
8003 myLastCreatedElems.Clear();
8004 myLastCreatedNodes.Clear();
8006 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8007 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8008 const SMDS_MeshElement* elem = invElemIt->next();
8010 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8011 SMDS_VolumeTool aVolume (elem);
8012 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8015 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8016 int iface, nbFaces = aVolume.NbFaces();
8017 vector<const SMDS_MeshNode *> poly_nodes;
8018 vector<int> quantities (nbFaces);
8020 for (iface = 0; iface < nbFaces; iface++) {
8021 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8022 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8023 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8025 for (int inode = 0; inode < nbFaceNodes; inode++) {
8026 poly_nodes.push_back(faceNodes[inode]);
8028 if (nbInserted == 0) {
8029 if (faceNodes[inode] == theBetweenNode1) {
8030 if (faceNodes[inode + 1] == theBetweenNode2) {
8031 nbInserted = theNodesToInsert.size();
8033 // add nodes to insert
8034 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8035 for (; nIt != theNodesToInsert.end(); nIt++) {
8036 poly_nodes.push_back(*nIt);
8040 else if (faceNodes[inode] == theBetweenNode2) {
8041 if (faceNodes[inode + 1] == theBetweenNode1) {
8042 nbInserted = theNodesToInsert.size();
8044 // add nodes to insert in reversed order
8045 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8047 for (; nIt != theNodesToInsert.begin(); nIt--) {
8048 poly_nodes.push_back(*nIt);
8050 poly_nodes.push_back(*nIt);
8057 quantities[iface] = nbFaceNodes + nbInserted;
8060 // Replace or update the volume
8061 SMESHDS_Mesh *aMesh = GetMeshDS();
8063 if (elem->IsPoly()) {
8064 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8068 int aShapeId = FindShape( elem );
8070 SMDS_MeshElement* newElem =
8071 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8072 myLastCreatedElems.Append(newElem);
8073 if (aShapeId && newElem)
8074 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8076 aMesh->RemoveElement(elem);
8083 //================================================================================
8085 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8087 //================================================================================
8089 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8090 vector<const SMDS_MeshNode *> & nodes,
8091 vector<int> & nbNodeInFaces )
8094 nbNodeInFaces.clear();
8095 SMDS_VolumeTool vTool ( elem );
8096 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8098 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8099 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8100 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8105 //=======================================================================
8107 * \brief Convert elements contained in a submesh to quadratic
8108 * \return int - nb of checked elements
8110 //=======================================================================
8112 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8113 SMESH_MesherHelper& theHelper,
8114 const bool theForce3d)
8117 if( !theSm ) return nbElem;
8119 vector<int> nbNodeInFaces;
8120 vector<const SMDS_MeshNode *> nodes;
8121 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8122 while(ElemItr->more())
8125 const SMDS_MeshElement* elem = ElemItr->next();
8126 if( !elem ) continue;
8128 // analyse a necessity of conversion
8129 const SMDSAbs_ElementType aType = elem->GetType();
8130 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8132 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8133 bool hasCentralNodes = false;
8134 if ( elem->IsQuadratic() )
8137 switch ( aGeomType ) {
8138 case SMDSEntity_Quad_Triangle:
8139 case SMDSEntity_Quad_Quadrangle:
8140 case SMDSEntity_Quad_Hexa:
8141 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8143 case SMDSEntity_BiQuad_Triangle:
8144 case SMDSEntity_BiQuad_Quadrangle:
8145 case SMDSEntity_TriQuad_Hexa:
8146 alreadyOK = theHelper.GetIsBiQuadratic();
8147 hasCentralNodes = true;
8152 // take into account already present modium nodes
8154 case SMDSAbs_Volume:
8155 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8157 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8159 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8165 // get elem data needed to re-create it
8167 const int id = elem->GetID();
8168 const int nbNodes = elem->NbCornerNodes();
8169 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8170 if ( aGeomType == SMDSEntity_Polyhedra )
8171 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8172 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8173 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8175 // remove a linear element
8176 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8178 // remove central nodes of biquadratic elements (biquad->quad convertion)
8179 if ( hasCentralNodes )
8180 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8181 if ( nodes[i]->NbInverseElements() == 0 )
8182 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8184 const SMDS_MeshElement* NewElem = 0;
8190 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8198 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8201 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8204 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8208 case SMDSAbs_Volume :
8212 case SMDSEntity_Tetra:
8213 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8215 case SMDSEntity_Pyramid:
8216 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8218 case SMDSEntity_Penta:
8219 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8221 case SMDSEntity_Hexa:
8222 case SMDSEntity_Quad_Hexa:
8223 case SMDSEntity_TriQuad_Hexa:
8224 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8225 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8227 case SMDSEntity_Hexagonal_Prism:
8229 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8236 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8237 if( NewElem && NewElem->getshapeId() < 1 )
8238 theSm->AddElement( NewElem );
8242 //=======================================================================
8243 //function : ConvertToQuadratic
8245 //=======================================================================
8247 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8249 SMESHDS_Mesh* meshDS = GetMeshDS();
8251 SMESH_MesherHelper aHelper(*myMesh);
8253 aHelper.SetIsQuadratic( true );
8254 aHelper.SetIsBiQuadratic( theToBiQuad );
8255 aHelper.SetElementsOnShape(true);
8257 // convert elements assigned to sub-meshes
8258 int nbCheckedElems = 0;
8259 if ( myMesh->HasShapeToMesh() )
8261 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8263 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8264 while ( smIt->more() ) {
8265 SMESH_subMesh* sm = smIt->next();
8266 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8267 aHelper.SetSubShape( sm->GetSubShape() );
8268 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8274 // convert elements NOT assigned to sub-meshes
8275 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8276 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8278 aHelper.SetElementsOnShape(false);
8279 SMESHDS_SubMesh *smDS = 0;
8282 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8283 while( aEdgeItr->more() )
8285 const SMDS_MeshEdge* edge = aEdgeItr->next();
8286 if ( !edge->IsQuadratic() )
8288 int id = edge->GetID();
8289 const SMDS_MeshNode* n1 = edge->GetNode(0);
8290 const SMDS_MeshNode* n2 = edge->GetNode(1);
8292 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8294 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8295 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8299 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8304 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8305 while( aFaceItr->more() )
8307 const SMDS_MeshFace* face = aFaceItr->next();
8308 if ( !face ) continue;
8310 const SMDSAbs_EntityType type = face->GetEntityType();
8314 case SMDSEntity_Quad_Triangle:
8315 case SMDSEntity_Quad_Quadrangle:
8316 alreadyOK = !theToBiQuad;
8317 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8319 case SMDSEntity_BiQuad_Triangle:
8320 case SMDSEntity_BiQuad_Quadrangle:
8321 alreadyOK = theToBiQuad;
8322 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8324 default: alreadyOK = false;
8329 const int id = face->GetID();
8330 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8332 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8334 SMDS_MeshFace * NewFace = 0;
8337 case SMDSEntity_Triangle:
8338 case SMDSEntity_Quad_Triangle:
8339 case SMDSEntity_BiQuad_Triangle:
8340 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8341 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8342 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8345 case SMDSEntity_Quadrangle:
8346 case SMDSEntity_Quad_Quadrangle:
8347 case SMDSEntity_BiQuad_Quadrangle:
8348 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8349 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8350 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8354 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8356 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8360 vector<int> nbNodeInFaces;
8361 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8362 while(aVolumeItr->more())
8364 const SMDS_MeshVolume* volume = aVolumeItr->next();
8365 if ( !volume ) continue;
8367 const SMDSAbs_EntityType type = volume->GetEntityType();
8368 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
8369 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
8371 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8374 const int id = volume->GetID();
8375 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8376 if ( type == SMDSEntity_Polyhedra )
8377 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8378 else if ( type == SMDSEntity_Hexagonal_Prism )
8379 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8381 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8383 SMDS_MeshVolume * NewVolume = 0;
8386 case SMDSEntity_Tetra:
8387 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8389 case SMDSEntity_Hexa:
8390 case SMDSEntity_Quad_Hexa:
8391 case SMDSEntity_TriQuad_Hexa:
8392 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8393 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8394 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8395 if ( nodes[i]->NbInverseElements() == 0 )
8396 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8398 case SMDSEntity_Pyramid:
8399 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8400 nodes[3], nodes[4], id, theForce3d);
8402 case SMDSEntity_Penta:
8403 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8404 nodes[3], nodes[4], nodes[5], id, theForce3d);
8406 case SMDSEntity_Hexagonal_Prism:
8408 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8410 ReplaceElemInGroups(volume, NewVolume, meshDS);
8415 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8416 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8417 // aHelper.FixQuadraticElements(myError);
8418 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8422 //================================================================================
8424 * \brief Makes given elements quadratic
8425 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8426 * \param theElements - elements to make quadratic
8428 //================================================================================
8430 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8431 TIDSortedElemSet& theElements,
8432 const bool theToBiQuad)
8434 if ( theElements.empty() ) return;
8436 // we believe that all theElements are of the same type
8437 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8439 // get all nodes shared by theElements
8440 TIDSortedNodeSet allNodes;
8441 TIDSortedElemSet::iterator eIt = theElements.begin();
8442 for ( ; eIt != theElements.end(); ++eIt )
8443 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8445 // complete theElements with elements of lower dim whose all nodes are in allNodes
8447 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8448 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8449 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8450 for ( ; nIt != allNodes.end(); ++nIt )
8452 const SMDS_MeshNode* n = *nIt;
8453 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8454 while ( invIt->more() )
8456 const SMDS_MeshElement* e = invIt->next();
8457 const SMDSAbs_ElementType type = e->GetType();
8458 if ( e->IsQuadratic() )
8460 quadAdjacentElems[ type ].insert( e );
8463 switch ( e->GetEntityType() ) {
8464 case SMDSEntity_Quad_Triangle:
8465 case SMDSEntity_Quad_Quadrangle:
8466 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8467 case SMDSEntity_BiQuad_Triangle:
8468 case SMDSEntity_BiQuad_Quadrangle:
8469 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8470 default: alreadyOK = true;
8475 if ( type >= elemType )
8476 continue; // same type or more complex linear element
8478 if ( !checkedAdjacentElems[ type ].insert( e ).second )
8479 continue; // e is already checked
8483 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8484 while ( nodeIt->more() && allIn )
8485 allIn = allNodes.count( nodeIt->next() );
8487 theElements.insert(e );
8491 SMESH_MesherHelper helper(*myMesh);
8492 helper.SetIsQuadratic( true );
8493 helper.SetIsBiQuadratic( theToBiQuad );
8495 // add links of quadratic adjacent elements to the helper
8497 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
8498 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
8499 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
8501 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
8503 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
8504 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
8505 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
8507 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
8509 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
8510 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
8511 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
8513 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
8516 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
8518 SMESHDS_Mesh* meshDS = GetMeshDS();
8519 SMESHDS_SubMesh* smDS = 0;
8520 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
8522 const SMDS_MeshElement* elem = *eIt;
8525 int nbCentralNodes = 0;
8526 switch ( elem->GetEntityType() ) {
8527 // linear convertible
8528 case SMDSEntity_Edge:
8529 case SMDSEntity_Triangle:
8530 case SMDSEntity_Quadrangle:
8531 case SMDSEntity_Tetra:
8532 case SMDSEntity_Pyramid:
8533 case SMDSEntity_Hexa:
8534 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
8535 // quadratic that can become bi-quadratic
8536 case SMDSEntity_Quad_Triangle:
8537 case SMDSEntity_Quad_Quadrangle:
8538 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
8540 case SMDSEntity_BiQuad_Triangle:
8541 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
8542 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
8544 default: alreadyOK = true;
8546 if ( alreadyOK ) continue;
8548 const SMDSAbs_ElementType type = elem->GetType();
8549 const int id = elem->GetID();
8550 const int nbNodes = elem->NbCornerNodes();
8551 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
8553 helper.SetSubShape( elem->getshapeId() );
8555 if ( !smDS || !smDS->Contains( elem ))
8556 smDS = meshDS->MeshElements( elem->getshapeId() );
8557 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
8559 SMDS_MeshElement * newElem = 0;
8562 case 4: // cases for most frequently used element types go first (for optimization)
8563 if ( type == SMDSAbs_Volume )
8564 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8566 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8569 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8570 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8573 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
8576 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8579 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8580 nodes[4], id, theForce3d);
8583 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8584 nodes[4], nodes[5], id, theForce3d);
8588 ReplaceElemInGroups( elem, newElem, meshDS);
8589 if( newElem && smDS )
8590 smDS->AddElement( newElem );
8592 // remove central nodes
8593 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
8594 if ( nodes[i]->NbInverseElements() == 0 )
8595 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
8597 } // loop on theElements
8600 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8601 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8602 // helper.FixQuadraticElements( myError );
8603 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8607 //=======================================================================
8609 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
8610 * \return int - nb of checked elements
8612 //=======================================================================
8614 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
8615 SMDS_ElemIteratorPtr theItr,
8616 const int theShapeID)
8619 SMESHDS_Mesh* meshDS = GetMeshDS();
8621 while( theItr->more() )
8623 const SMDS_MeshElement* elem = theItr->next();
8625 if( elem && elem->IsQuadratic())
8627 int id = elem->GetID();
8628 int nbCornerNodes = elem->NbCornerNodes();
8629 SMDSAbs_ElementType aType = elem->GetType();
8631 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
8633 //remove a quadratic element
8634 if ( !theSm || !theSm->Contains( elem ))
8635 theSm = meshDS->MeshElements( elem->getshapeId() );
8636 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
8638 // remove medium nodes
8639 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
8640 if ( nodes[i]->NbInverseElements() == 0 )
8641 meshDS->RemoveFreeNode( nodes[i], theSm );
8643 // add a linear element
8644 nodes.resize( nbCornerNodes );
8645 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
8646 ReplaceElemInGroups(elem, newElem, meshDS);
8647 if( theSm && newElem )
8648 theSm->AddElement( newElem );
8654 //=======================================================================
8655 //function : ConvertFromQuadratic
8657 //=======================================================================
8659 bool SMESH_MeshEditor::ConvertFromQuadratic()
8661 int nbCheckedElems = 0;
8662 if ( myMesh->HasShapeToMesh() )
8664 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8666 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8667 while ( smIt->more() ) {
8668 SMESH_subMesh* sm = smIt->next();
8669 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
8670 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
8676 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
8677 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8679 SMESHDS_SubMesh *aSM = 0;
8680 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
8688 //================================================================================
8690 * \brief Return true if all medium nodes of the element are in the node set
8692 //================================================================================
8694 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
8696 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
8697 if ( !nodeSet.count( elem->GetNode(i) ))
8703 //================================================================================
8705 * \brief Makes given elements linear
8707 //================================================================================
8709 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
8711 if ( theElements.empty() ) return;
8713 // collect IDs of medium nodes of theElements; some of these nodes will be removed
8714 set<int> mediumNodeIDs;
8715 TIDSortedElemSet::iterator eIt = theElements.begin();
8716 for ( ; eIt != theElements.end(); ++eIt )
8718 const SMDS_MeshElement* e = *eIt;
8719 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
8720 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
8723 // replace given elements by linear ones
8724 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
8725 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
8726 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8728 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
8729 // except those elements sharing medium nodes of quadratic element whose medium nodes
8730 // are not all in mediumNodeIDs
8732 // get remaining medium nodes
8733 TIDSortedNodeSet mediumNodes;
8734 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
8735 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
8736 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
8737 mediumNodes.insert( mediumNodes.end(), n );
8739 // find more quadratic elements to convert
8740 TIDSortedElemSet moreElemsToConvert;
8741 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
8742 for ( ; nIt != mediumNodes.end(); ++nIt )
8744 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
8745 while ( invIt->more() )
8747 const SMDS_MeshElement* e = invIt->next();
8748 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
8750 // find a more complex element including e and
8751 // whose medium nodes are not in mediumNodes
8752 bool complexFound = false;
8753 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
8755 SMDS_ElemIteratorPtr invIt2 =
8756 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
8757 while ( invIt2->more() )
8759 const SMDS_MeshElement* eComplex = invIt2->next();
8760 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
8762 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
8763 if ( nbCommonNodes == e->NbNodes())
8765 complexFound = true;
8766 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
8772 if ( !complexFound )
8773 moreElemsToConvert.insert( e );
8777 elemIt = SMDS_ElemIteratorPtr
8778 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
8779 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8782 //=======================================================================
8783 //function : SewSideElements
8785 //=======================================================================
8787 SMESH_MeshEditor::Sew_Error
8788 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
8789 TIDSortedElemSet& theSide2,
8790 const SMDS_MeshNode* theFirstNode1,
8791 const SMDS_MeshNode* theFirstNode2,
8792 const SMDS_MeshNode* theSecondNode1,
8793 const SMDS_MeshNode* theSecondNode2)
8795 myLastCreatedElems.Clear();
8796 myLastCreatedNodes.Clear();
8798 MESSAGE ("::::SewSideElements()");
8799 if ( theSide1.size() != theSide2.size() )
8800 return SEW_DIFF_NB_OF_ELEMENTS;
8802 Sew_Error aResult = SEW_OK;
8804 // 1. Build set of faces representing each side
8805 // 2. Find which nodes of the side 1 to merge with ones on the side 2
8806 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
8808 // =======================================================================
8809 // 1. Build set of faces representing each side:
8810 // =======================================================================
8811 // a. build set of nodes belonging to faces
8812 // b. complete set of faces: find missing faces whose nodes are in set of nodes
8813 // c. create temporary faces representing side of volumes if correspondent
8814 // face does not exist
8816 SMESHDS_Mesh* aMesh = GetMeshDS();
8817 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
8818 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
8819 TIDSortedElemSet faceSet1, faceSet2;
8820 set<const SMDS_MeshElement*> volSet1, volSet2;
8821 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
8822 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
8823 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
8824 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
8825 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
8826 int iSide, iFace, iNode;
8828 list<const SMDS_MeshElement* > tempFaceList;
8829 for ( iSide = 0; iSide < 2; iSide++ ) {
8830 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
8831 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
8832 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
8833 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
8834 set<const SMDS_MeshElement*>::iterator vIt;
8835 TIDSortedElemSet::iterator eIt;
8836 set<const SMDS_MeshNode*>::iterator nIt;
8838 // check that given nodes belong to given elements
8839 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
8840 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
8841 int firstIndex = -1, secondIndex = -1;
8842 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8843 const SMDS_MeshElement* elem = *eIt;
8844 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
8845 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
8846 if ( firstIndex > -1 && secondIndex > -1 ) break;
8848 if ( firstIndex < 0 || secondIndex < 0 ) {
8849 // we can simply return until temporary faces created
8850 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
8853 // -----------------------------------------------------------
8854 // 1a. Collect nodes of existing faces
8855 // and build set of face nodes in order to detect missing
8856 // faces corresponding to sides of volumes
8857 // -----------------------------------------------------------
8859 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
8861 // loop on the given element of a side
8862 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8863 //const SMDS_MeshElement* elem = *eIt;
8864 const SMDS_MeshElement* elem = *eIt;
8865 if ( elem->GetType() == SMDSAbs_Face ) {
8866 faceSet->insert( elem );
8867 set <const SMDS_MeshNode*> faceNodeSet;
8868 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
8869 while ( nodeIt->more() ) {
8870 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8871 nodeSet->insert( n );
8872 faceNodeSet.insert( n );
8874 setOfFaceNodeSet.insert( faceNodeSet );
8876 else if ( elem->GetType() == SMDSAbs_Volume )
8877 volSet->insert( elem );
8879 // ------------------------------------------------------------------------------
8880 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
8881 // ------------------------------------------------------------------------------
8883 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
8884 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
8885 while ( fIt->more() ) { // loop on faces sharing a node
8886 const SMDS_MeshElement* f = fIt->next();
8887 if ( faceSet->find( f ) == faceSet->end() ) {
8888 // check if all nodes are in nodeSet and
8889 // complete setOfFaceNodeSet if they are
8890 set <const SMDS_MeshNode*> faceNodeSet;
8891 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
8892 bool allInSet = true;
8893 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
8894 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8895 if ( nodeSet->find( n ) == nodeSet->end() )
8898 faceNodeSet.insert( n );
8901 faceSet->insert( f );
8902 setOfFaceNodeSet.insert( faceNodeSet );
8908 // -------------------------------------------------------------------------
8909 // 1c. Create temporary faces representing sides of volumes if correspondent
8910 // face does not exist
8911 // -------------------------------------------------------------------------
8913 if ( !volSet->empty() ) {
8914 //int nodeSetSize = nodeSet->size();
8916 // loop on given volumes
8917 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
8918 SMDS_VolumeTool vol (*vIt);
8919 // loop on volume faces: find free faces
8920 // --------------------------------------
8921 list<const SMDS_MeshElement* > freeFaceList;
8922 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
8923 if ( !vol.IsFreeFace( iFace ))
8925 // check if there is already a face with same nodes in a face set
8926 const SMDS_MeshElement* aFreeFace = 0;
8927 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
8928 int nbNodes = vol.NbFaceNodes( iFace );
8929 set <const SMDS_MeshNode*> faceNodeSet;
8930 vol.GetFaceNodes( iFace, faceNodeSet );
8931 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
8933 // no such a face is given but it still can exist, check it
8934 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
8935 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
8938 // create a temporary face
8939 if ( nbNodes == 3 ) {
8940 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
8941 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
8943 else if ( nbNodes == 4 ) {
8944 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
8945 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
8948 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
8949 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
8950 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
8953 tempFaceList.push_back( aFreeFace );
8957 freeFaceList.push_back( aFreeFace );
8959 } // loop on faces of a volume
8961 // choose one of several free faces of a volume
8962 // --------------------------------------------
8963 if ( freeFaceList.size() > 1 ) {
8964 // choose a face having max nb of nodes shared by other elems of a side
8965 int maxNbNodes = -1;
8966 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
8967 while ( fIt != freeFaceList.end() ) { // loop on free faces
8968 int nbSharedNodes = 0;
8969 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
8970 while ( nodeIt->more() ) { // loop on free face nodes
8971 const SMDS_MeshNode* n =
8972 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8973 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
8974 while ( invElemIt->more() ) {
8975 const SMDS_MeshElement* e = invElemIt->next();
8976 nbSharedNodes += faceSet->count( e );
8977 nbSharedNodes += elemSet->count( e );
8980 if ( nbSharedNodes > maxNbNodes ) {
8981 maxNbNodes = nbSharedNodes;
8982 freeFaceList.erase( freeFaceList.begin(), fIt++ );
8984 else if ( nbSharedNodes == maxNbNodes ) {
8988 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
8991 if ( freeFaceList.size() > 1 )
8993 // could not choose one face, use another way
8994 // choose a face most close to the bary center of the opposite side
8995 gp_XYZ aBC( 0., 0., 0. );
8996 set <const SMDS_MeshNode*> addedNodes;
8997 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
8998 eIt = elemSet2->begin();
8999 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9000 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9001 while ( nodeIt->more() ) { // loop on free face nodes
9002 const SMDS_MeshNode* n =
9003 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9004 if ( addedNodes.insert( n ).second )
9005 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9008 aBC /= addedNodes.size();
9009 double minDist = DBL_MAX;
9010 fIt = freeFaceList.begin();
9011 while ( fIt != freeFaceList.end() ) { // loop on free faces
9013 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9014 while ( nodeIt->more() ) { // loop on free face nodes
9015 const SMDS_MeshNode* n =
9016 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9017 gp_XYZ p( n->X(),n->Y(),n->Z() );
9018 dist += ( aBC - p ).SquareModulus();
9020 if ( dist < minDist ) {
9022 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9025 fIt = freeFaceList.erase( fIt++ );
9028 } // choose one of several free faces of a volume
9030 if ( freeFaceList.size() == 1 ) {
9031 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9032 faceSet->insert( aFreeFace );
9033 // complete a node set with nodes of a found free face
9034 // for ( iNode = 0; iNode < ; iNode++ )
9035 // nodeSet->insert( fNodes[ iNode ] );
9038 } // loop on volumes of a side
9040 // // complete a set of faces if new nodes in a nodeSet appeared
9041 // // ----------------------------------------------------------
9042 // if ( nodeSetSize != nodeSet->size() ) {
9043 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9044 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9045 // while ( fIt->more() ) { // loop on faces sharing a node
9046 // const SMDS_MeshElement* f = fIt->next();
9047 // if ( faceSet->find( f ) == faceSet->end() ) {
9048 // // check if all nodes are in nodeSet and
9049 // // complete setOfFaceNodeSet if they are
9050 // set <const SMDS_MeshNode*> faceNodeSet;
9051 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9052 // bool allInSet = true;
9053 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9054 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9055 // if ( nodeSet->find( n ) == nodeSet->end() )
9056 // allInSet = false;
9058 // faceNodeSet.insert( n );
9060 // if ( allInSet ) {
9061 // faceSet->insert( f );
9062 // setOfFaceNodeSet.insert( faceNodeSet );
9068 } // Create temporary faces, if there are volumes given
9071 if ( faceSet1.size() != faceSet2.size() ) {
9072 // delete temporary faces: they are in reverseElements of actual nodes
9073 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9074 // while ( tmpFaceIt->more() )
9075 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9076 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9077 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9078 // aMesh->RemoveElement(*tmpFaceIt);
9079 MESSAGE("Diff nb of faces");
9080 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9083 // ============================================================
9084 // 2. Find nodes to merge:
9085 // bind a node to remove to a node to put instead
9086 // ============================================================
9088 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9089 if ( theFirstNode1 != theFirstNode2 )
9090 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9091 if ( theSecondNode1 != theSecondNode2 )
9092 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9094 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9095 set< long > linkIdSet; // links to process
9096 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9098 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9099 list< NLink > linkList[2];
9100 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9101 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9102 // loop on links in linkList; find faces by links and append links
9103 // of the found faces to linkList
9104 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9105 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9107 NLink link[] = { *linkIt[0], *linkIt[1] };
9108 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9109 if ( !linkIdSet.count( linkID ) )
9112 // by links, find faces in the face sets,
9113 // and find indices of link nodes in the found faces;
9114 // in a face set, there is only one or no face sharing a link
9115 // ---------------------------------------------------------------
9117 const SMDS_MeshElement* face[] = { 0, 0 };
9118 vector<const SMDS_MeshNode*> fnodes[2];
9119 int iLinkNode[2][2];
9120 TIDSortedElemSet avoidSet;
9121 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9122 const SMDS_MeshNode* n1 = link[iSide].first;
9123 const SMDS_MeshNode* n2 = link[iSide].second;
9124 //cout << "Side " << iSide << " ";
9125 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9126 // find a face by two link nodes
9127 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9128 *faceSetPtr[ iSide ], avoidSet,
9129 &iLinkNode[iSide][0],
9130 &iLinkNode[iSide][1] );
9133 //cout << " F " << face[ iSide]->GetID() <<endl;
9134 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9135 // put face nodes to fnodes
9136 if ( face[ iSide ]->IsQuadratic() )
9138 // use interlaced nodes iterator
9139 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9140 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9141 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9142 while ( nIter->more() )
9143 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9147 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9148 face[ iSide ]->end_nodes() );
9150 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9154 // check similarity of elements of the sides
9155 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9156 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9157 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9158 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9161 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9163 break; // do not return because it's necessary to remove tmp faces
9166 // set nodes to merge
9167 // -------------------
9169 if ( face[0] && face[1] ) {
9170 const int nbNodes = face[0]->NbNodes();
9171 if ( nbNodes != face[1]->NbNodes() ) {
9172 MESSAGE("Diff nb of face nodes");
9173 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9174 break; // do not return because it s necessary to remove tmp faces
9176 bool reverse[] = { false, false }; // order of nodes in the link
9177 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9178 // analyse link orientation in faces
9179 int i1 = iLinkNode[ iSide ][ 0 ];
9180 int i2 = iLinkNode[ iSide ][ 1 ];
9181 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9183 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9184 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9185 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9187 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9188 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9191 // add other links of the faces to linkList
9192 // -----------------------------------------
9194 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9195 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9196 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9197 if ( !iter_isnew.second ) { // already in a set: no need to process
9198 linkIdSet.erase( iter_isnew.first );
9200 else // new in set == encountered for the first time: add
9202 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9203 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9204 linkList[0].push_back ( NLink( n1, n2 ));
9205 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9210 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9213 } // loop on link lists
9215 if ( aResult == SEW_OK &&
9216 ( //linkIt[0] != linkList[0].end() ||
9217 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9218 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9219 " " << (faceSetPtr[1]->empty()));
9220 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9223 // ====================================================================
9224 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9225 // ====================================================================
9227 // delete temporary faces
9228 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9229 // while ( tmpFaceIt->more() )
9230 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9231 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9232 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9233 aMesh->RemoveElement(*tmpFaceIt);
9235 if ( aResult != SEW_OK)
9238 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9239 // loop on nodes replacement map
9240 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9241 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9242 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9243 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9244 nodeIDsToRemove.push_back( nToRemove->GetID() );
9245 // loop on elements sharing nToRemove
9246 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9247 while ( invElemIt->more() ) {
9248 const SMDS_MeshElement* e = invElemIt->next();
9249 // get a new suite of nodes: make replacement
9250 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9251 vector< const SMDS_MeshNode*> nodes( nbNodes );
9252 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9253 while ( nIt->more() ) {
9254 const SMDS_MeshNode* n =
9255 static_cast<const SMDS_MeshNode*>( nIt->next() );
9256 nnIt = nReplaceMap.find( n );
9257 if ( nnIt != nReplaceMap.end() ) {
9263 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9264 // elemIDsToRemove.push_back( e->GetID() );
9268 SMDSAbs_ElementType etyp = e->GetType();
9269 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9272 myLastCreatedElems.Append(newElem);
9273 AddToSameGroups(newElem, e, aMesh);
9274 int aShapeId = e->getshapeId();
9277 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9280 aMesh->RemoveElement(e);
9285 Remove( nodeIDsToRemove, true );
9290 //================================================================================
9292 * \brief Find corresponding nodes in two sets of faces
9293 * \param theSide1 - first face set
9294 * \param theSide2 - second first face
9295 * \param theFirstNode1 - a boundary node of set 1
9296 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9297 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9298 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9299 * \param nReplaceMap - output map of corresponding nodes
9300 * \return bool - is a success or not
9302 //================================================================================
9305 //#define DEBUG_MATCHING_NODES
9308 SMESH_MeshEditor::Sew_Error
9309 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9310 set<const SMDS_MeshElement*>& theSide2,
9311 const SMDS_MeshNode* theFirstNode1,
9312 const SMDS_MeshNode* theFirstNode2,
9313 const SMDS_MeshNode* theSecondNode1,
9314 const SMDS_MeshNode* theSecondNode2,
9315 TNodeNodeMap & nReplaceMap)
9317 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9319 nReplaceMap.clear();
9320 if ( theFirstNode1 != theFirstNode2 )
9321 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9322 if ( theSecondNode1 != theSecondNode2 )
9323 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9325 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9326 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9328 list< NLink > linkList[2];
9329 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9330 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9332 // loop on links in linkList; find faces by links and append links
9333 // of the found faces to linkList
9334 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9335 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9336 NLink link[] = { *linkIt[0], *linkIt[1] };
9337 if ( linkSet.find( link[0] ) == linkSet.end() )
9340 // by links, find faces in the face sets,
9341 // and find indices of link nodes in the found faces;
9342 // in a face set, there is only one or no face sharing a link
9343 // ---------------------------------------------------------------
9345 const SMDS_MeshElement* face[] = { 0, 0 };
9346 list<const SMDS_MeshNode*> notLinkNodes[2];
9347 //bool reverse[] = { false, false }; // order of notLinkNodes
9349 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9351 const SMDS_MeshNode* n1 = link[iSide].first;
9352 const SMDS_MeshNode* n2 = link[iSide].second;
9353 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9354 set< const SMDS_MeshElement* > facesOfNode1;
9355 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9357 // during a loop of the first node, we find all faces around n1,
9358 // during a loop of the second node, we find one face sharing both n1 and n2
9359 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9360 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9361 while ( fIt->more() ) { // loop on faces sharing a node
9362 const SMDS_MeshElement* f = fIt->next();
9363 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9364 ! facesOfNode1.insert( f ).second ) // f encounters twice
9366 if ( face[ iSide ] ) {
9367 MESSAGE( "2 faces per link " );
9368 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9371 faceSet->erase( f );
9373 // get not link nodes
9374 int nbN = f->NbNodes();
9375 if ( f->IsQuadratic() )
9377 nbNodes[ iSide ] = nbN;
9378 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9379 int i1 = f->GetNodeIndex( n1 );
9380 int i2 = f->GetNodeIndex( n2 );
9381 int iEnd = nbN, iBeg = -1, iDelta = 1;
9382 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9384 std::swap( iEnd, iBeg ); iDelta = -1;
9389 if ( i == iEnd ) i = iBeg + iDelta;
9390 if ( i == i1 ) break;
9391 nodes.push_back ( f->GetNode( i ) );
9397 // check similarity of elements of the sides
9398 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9399 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9400 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9401 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9404 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9408 // set nodes to merge
9409 // -------------------
9411 if ( face[0] && face[1] ) {
9412 if ( nbNodes[0] != nbNodes[1] ) {
9413 MESSAGE("Diff nb of face nodes");
9414 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9416 #ifdef DEBUG_MATCHING_NODES
9417 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9418 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9419 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9421 int nbN = nbNodes[0];
9423 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9424 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9425 for ( int i = 0 ; i < nbN - 2; ++i ) {
9426 #ifdef DEBUG_MATCHING_NODES
9427 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9429 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9433 // add other links of the face 1 to linkList
9434 // -----------------------------------------
9436 const SMDS_MeshElement* f0 = face[0];
9437 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9438 for ( int i = 0; i < nbN; i++ )
9440 const SMDS_MeshNode* n2 = f0->GetNode( i );
9441 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9442 linkSet.insert( SMESH_TLink( n1, n2 ));
9443 if ( !iter_isnew.second ) { // already in a set: no need to process
9444 linkSet.erase( iter_isnew.first );
9446 else // new in set == encountered for the first time: add
9448 #ifdef DEBUG_MATCHING_NODES
9449 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9450 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9452 linkList[0].push_back ( NLink( n1, n2 ));
9453 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9458 } // loop on link lists
9463 //================================================================================
9465 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9466 \param theElems - the list of elements (edges or faces) to be replicated
9467 The nodes for duplication could be found from these elements
9468 \param theNodesNot - list of nodes to NOT replicate
9469 \param theAffectedElems - the list of elements (cells and edges) to which the
9470 replicated nodes should be associated to.
9471 \return TRUE if operation has been completed successfully, FALSE otherwise
9473 //================================================================================
9475 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
9476 const TIDSortedElemSet& theNodesNot,
9477 const TIDSortedElemSet& theAffectedElems )
9479 myLastCreatedElems.Clear();
9480 myLastCreatedNodes.Clear();
9482 if ( theElems.size() == 0 )
9485 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9490 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9491 // duplicate elements and nodes
9492 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
9493 // replce nodes by duplications
9494 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
9498 //================================================================================
9500 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9501 \param theMeshDS - mesh instance
9502 \param theElems - the elements replicated or modified (nodes should be changed)
9503 \param theNodesNot - nodes to NOT replicate
9504 \param theNodeNodeMap - relation of old node to new created node
9505 \param theIsDoubleElem - flag os to replicate element or modify
9506 \return TRUE if operation has been completed successfully, FALSE otherwise
9508 //================================================================================
9510 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
9511 const TIDSortedElemSet& theElems,
9512 const TIDSortedElemSet& theNodesNot,
9513 std::map< const SMDS_MeshNode*,
9514 const SMDS_MeshNode* >& theNodeNodeMap,
9515 const bool theIsDoubleElem )
9517 MESSAGE("doubleNodes");
9518 // iterate on through element and duplicate them (by nodes duplication)
9520 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9521 for ( ; elemItr != theElems.end(); ++elemItr )
9523 const SMDS_MeshElement* anElem = *elemItr;
9527 bool isDuplicate = false;
9528 // duplicate nodes to duplicate element
9529 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
9530 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9532 while ( anIter->more() )
9535 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9536 SMDS_MeshNode* aNewNode = aCurrNode;
9537 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
9538 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
9539 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
9542 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
9543 theNodeNodeMap[ aCurrNode ] = aNewNode;
9544 myLastCreatedNodes.Append( aNewNode );
9546 isDuplicate |= (aCurrNode != aNewNode);
9547 newNodes[ ind++ ] = aNewNode;
9552 if ( theIsDoubleElem )
9553 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
9556 MESSAGE("ChangeElementNodes");
9557 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
9564 //================================================================================
9566 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9567 \param theNodes - identifiers of nodes to be doubled
9568 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
9569 nodes. If list of element identifiers is empty then nodes are doubled but
9570 they not assigned to elements
9571 \return TRUE if operation has been completed successfully, FALSE otherwise
9573 //================================================================================
9575 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
9576 const std::list< int >& theListOfModifiedElems )
9578 MESSAGE("DoubleNodes");
9579 myLastCreatedElems.Clear();
9580 myLastCreatedNodes.Clear();
9582 if ( theListOfNodes.size() == 0 )
9585 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9589 // iterate through nodes and duplicate them
9591 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9593 std::list< int >::const_iterator aNodeIter;
9594 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
9596 int aCurr = *aNodeIter;
9597 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
9603 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
9606 anOldNodeToNewNode[ aNode ] = aNewNode;
9607 myLastCreatedNodes.Append( aNewNode );
9611 // Create map of new nodes for modified elements
9613 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
9615 std::list< int >::const_iterator anElemIter;
9616 for ( anElemIter = theListOfModifiedElems.begin();
9617 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
9619 int aCurr = *anElemIter;
9620 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
9624 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
9626 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9628 while ( anIter->more() )
9630 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9631 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
9633 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
9634 aNodeArr[ ind++ ] = aNewNode;
9637 aNodeArr[ ind++ ] = aCurrNode;
9639 anElemToNodes[ anElem ] = aNodeArr;
9642 // Change nodes of elements
9644 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
9645 anElemToNodesIter = anElemToNodes.begin();
9646 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
9648 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
9649 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
9652 MESSAGE("ChangeElementNodes");
9653 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
9662 //================================================================================
9664 \brief Check if element located inside shape
9665 \return TRUE if IN or ON shape, FALSE otherwise
9667 //================================================================================
9669 template<class Classifier>
9670 bool isInside(const SMDS_MeshElement* theElem,
9671 Classifier& theClassifier,
9672 const double theTol)
9674 gp_XYZ centerXYZ (0, 0, 0);
9675 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
9676 while (aNodeItr->more())
9677 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
9679 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
9680 theClassifier.Perform(aPnt, theTol);
9681 TopAbs_State aState = theClassifier.State();
9682 return (aState == TopAbs_IN || aState == TopAbs_ON );
9685 //================================================================================
9687 * \brief Classifier of the 3D point on the TopoDS_Face
9688 * with interaface suitable for isInside()
9690 //================================================================================
9692 struct _FaceClassifier
9694 Extrema_ExtPS _extremum;
9695 BRepAdaptor_Surface _surface;
9696 TopAbs_State _state;
9698 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
9700 _extremum.Initialize( _surface,
9701 _surface.FirstUParameter(), _surface.LastUParameter(),
9702 _surface.FirstVParameter(), _surface.LastVParameter(),
9703 _surface.Tolerance(), _surface.Tolerance() );
9705 void Perform(const gp_Pnt& aPnt, double theTol)
9707 _state = TopAbs_OUT;
9708 _extremum.Perform(aPnt);
9709 if ( _extremum.IsDone() )
9710 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
9711 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
9712 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9714 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9717 TopAbs_State State() const
9724 //================================================================================
9726 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
9727 This method is the first step of DoubleNodeElemGroupsInRegion.
9728 \param theElems - list of groups of elements (edges or faces) to be replicated
9729 \param theNodesNot - list of groups of nodes not to replicated
9730 \param theShape - shape to detect affected elements (element which geometric center
9731 located on or inside shape).
9732 The replicated nodes should be associated to affected elements.
9733 \return groups of affected elements
9734 \sa DoubleNodeElemGroupsInRegion()
9736 //================================================================================
9738 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
9739 const TIDSortedElemSet& theNodesNot,
9740 const TopoDS_Shape& theShape,
9741 TIDSortedElemSet& theAffectedElems)
9743 if ( theShape.IsNull() )
9746 const double aTol = Precision::Confusion();
9747 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9748 auto_ptr<_FaceClassifier> aFaceClassifier;
9749 if ( theShape.ShapeType() == TopAbs_SOLID )
9751 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9752 bsc3d->PerformInfinitePoint(aTol);
9754 else if (theShape.ShapeType() == TopAbs_FACE )
9756 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9759 // iterates on indicated elements and get elements by back references from their nodes
9760 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9761 for ( ; elemItr != theElems.end(); ++elemItr )
9763 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9767 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9768 while ( nodeItr->more() )
9770 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9771 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9773 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9774 while ( backElemItr->more() )
9776 const SMDS_MeshElement* curElem = backElemItr->next();
9777 if ( curElem && theElems.find(curElem) == theElems.end() &&
9779 isInside( curElem, *bsc3d, aTol ) :
9780 isInside( curElem, *aFaceClassifier, aTol )))
9781 theAffectedElems.insert( curElem );
9788 //================================================================================
9790 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9791 \param theElems - group of of elements (edges or faces) to be replicated
9792 \param theNodesNot - group of nodes not to replicate
9793 \param theShape - shape to detect affected elements (element which geometric center
9794 located on or inside shape).
9795 The replicated nodes should be associated to affected elements.
9796 \return TRUE if operation has been completed successfully, FALSE otherwise
9798 //================================================================================
9800 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
9801 const TIDSortedElemSet& theNodesNot,
9802 const TopoDS_Shape& theShape )
9804 if ( theShape.IsNull() )
9807 const double aTol = Precision::Confusion();
9808 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9809 auto_ptr<_FaceClassifier> aFaceClassifier;
9810 if ( theShape.ShapeType() == TopAbs_SOLID )
9812 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9813 bsc3d->PerformInfinitePoint(aTol);
9815 else if (theShape.ShapeType() == TopAbs_FACE )
9817 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9820 // iterates on indicated elements and get elements by back references from their nodes
9821 TIDSortedElemSet anAffected;
9822 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9823 for ( ; elemItr != theElems.end(); ++elemItr )
9825 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9829 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9830 while ( nodeItr->more() )
9832 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9833 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9835 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9836 while ( backElemItr->more() )
9838 const SMDS_MeshElement* curElem = backElemItr->next();
9839 if ( curElem && theElems.find(curElem) == theElems.end() &&
9841 isInside( curElem, *bsc3d, aTol ) :
9842 isInside( curElem, *aFaceClassifier, aTol )))
9843 anAffected.insert( curElem );
9847 return DoubleNodes( theElems, theNodesNot, anAffected );
9851 * \brief compute an oriented angle between two planes defined by four points.
9852 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
9853 * @param p0 base of the rotation axe
9854 * @param p1 extremity of the rotation axe
9855 * @param g1 belongs to the first plane
9856 * @param g2 belongs to the second plane
9858 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
9860 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
9861 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
9862 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
9863 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
9864 gp_Vec vref(p0, p1);
9867 gp_Vec n1 = vref.Crossed(v1);
9868 gp_Vec n2 = vref.Crossed(v2);
9869 return n2.AngleWithRef(n1, vref);
9873 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
9874 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
9875 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
9876 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
9877 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
9878 * 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.
9879 * 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.
9880 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
9881 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
9882 * @param theElems - list of groups of volumes, where a group of volume is a set of
9883 * SMDS_MeshElements sorted by Id.
9884 * @param createJointElems - if TRUE, create the elements
9885 * @return TRUE if operation has been completed successfully, FALSE otherwise
9887 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
9888 bool createJointElems)
9890 MESSAGE("----------------------------------------------");
9891 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
9892 MESSAGE("----------------------------------------------");
9894 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
9895 meshDS->BuildDownWardConnectivity(true);
9897 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
9899 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
9900 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
9901 // build the list of nodes shared by 2 or more domains, with their domain indexes
9903 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
9904 std::map<int,int>celldom; // cell vtkId --> domain
9905 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
9906 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
9907 faceDomains.clear();
9909 cellDomains.clear();
9910 nodeDomains.clear();
9911 std::map<int,int> emptyMap;
9912 std::set<int> emptySet;
9915 MESSAGE(".. Number of domains :"<<theElems.size());
9917 // Check if the domains do not share an element
9918 for (int idom = 0; idom < theElems.size()-1; idom++)
9920 // MESSAGE("... Check of domain #" << idom);
9921 const TIDSortedElemSet& domain = theElems[idom];
9922 TIDSortedElemSet::const_iterator elemItr = domain.begin();
9923 for (; elemItr != domain.end(); ++elemItr)
9925 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
9926 int idombisdeb = idom + 1 ;
9927 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
9929 const TIDSortedElemSet& domainbis = theElems[idombis];
9930 if ( domainbis.count(anElem) )
9932 MESSAGE(".... Domain #" << idom);
9933 MESSAGE(".... Domain #" << idombis);
9934 throw SALOME_Exception("The domains are not disjoint.");
9941 for (int idom = 0; idom < theElems.size(); idom++)
9944 // --- build a map (face to duplicate --> volume to modify)
9945 // with all the faces shared by 2 domains (group of elements)
9946 // and corresponding volume of this domain, for each shared face.
9947 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
9949 MESSAGE("... Neighbors of domain #" << idom);
9950 const TIDSortedElemSet& domain = theElems[idom];
9951 TIDSortedElemSet::const_iterator elemItr = domain.begin();
9952 for (; elemItr != domain.end(); ++elemItr)
9954 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
9957 int vtkId = anElem->getVtkId();
9958 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
9959 int neighborsVtkIds[NBMAXNEIGHBORS];
9960 int downIds[NBMAXNEIGHBORS];
9961 unsigned char downTypes[NBMAXNEIGHBORS];
9962 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
9963 for (int n = 0; n < nbNeighbors; n++)
9965 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
9966 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
9967 if (! domain.count(elem)) // neighbor is in another domain : face is shared
9970 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
9972 // MESSAGE("Domain " << idombis);
9973 const TIDSortedElemSet& domainbis = theElems[idombis];
9974 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
9976 if ( ok ) // the characteristics of the face is stored
9978 DownIdType face(downIds[n], downTypes[n]);
9979 if (!faceDomains.count(face))
9980 faceDomains[face] = emptyMap; // create an empty entry for face
9981 if (!faceDomains[face].count(idom))
9983 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
9984 celldom[vtkId] = idom;
9985 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
9993 //MESSAGE("Number of shared faces " << faceDomains.size());
9994 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
9996 // --- explore the shared faces domain by domain,
9997 // explore the nodes of the face and see if they belong to a cell in the domain,
9998 // which has only a node or an edge on the border (not a shared face)
10000 for (int idomain = 0; idomain < theElems.size(); idomain++)
10002 //MESSAGE("Domain " << idomain);
10003 const TIDSortedElemSet& domain = theElems[idomain];
10004 itface = faceDomains.begin();
10005 for (; itface != faceDomains.end(); ++itface)
10007 std::map<int, int> domvol = itface->second;
10008 if (!domvol.count(idomain))
10010 DownIdType face = itface->first;
10011 //MESSAGE(" --- face " << face.cellId);
10012 std::set<int> oldNodes;
10014 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10015 std::set<int>::iterator itn = oldNodes.begin();
10016 for (; itn != oldNodes.end(); ++itn)
10019 //MESSAGE(" node " << oldId);
10020 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10021 for (int i=0; i<l.ncells; i++)
10023 int vtkId = l.cells[i];
10024 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10025 if (!domain.count(anElem))
10027 int vtkType = grid->GetCellType(vtkId);
10028 int downId = grid->CellIdToDownId(vtkId);
10031 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10032 continue; // not OK at this stage of the algorithm:
10033 //no cells created after BuildDownWardConnectivity
10035 DownIdType aCell(downId, vtkType);
10036 if (!cellDomains.count(aCell))
10037 cellDomains[aCell] = emptyMap; // create an empty entry for cell
10038 cellDomains[aCell][idomain] = vtkId;
10039 celldom[vtkId] = idomain;
10040 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10046 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10047 // for each shared face, get the nodes
10048 // for each node, for each domain of the face, create a clone of the node
10050 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10051 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10052 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10054 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10055 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10056 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10058 MESSAGE(".. Duplication of the nodes");
10059 for (int idomain = 0; idomain < theElems.size(); idomain++)
10061 itface = faceDomains.begin();
10062 for (; itface != faceDomains.end(); ++itface)
10064 std::map<int, int> domvol = itface->second;
10065 if (!domvol.count(idomain))
10067 DownIdType face = itface->first;
10068 //MESSAGE(" --- face " << face.cellId);
10069 std::set<int> oldNodes;
10071 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10072 std::set<int>::iterator itn = oldNodes.begin();
10073 for (; itn != oldNodes.end(); ++itn)
10076 //MESSAGE("-+-+-a node " << oldId);
10077 if (!nodeDomains.count(oldId))
10078 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10079 if (nodeDomains[oldId].empty())
10081 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10082 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10084 std::map<int, int>::iterator itdom = domvol.begin();
10085 for (; itdom != domvol.end(); ++itdom)
10087 int idom = itdom->first;
10088 //MESSAGE(" domain " << idom);
10089 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10091 if (nodeDomains[oldId].size() >= 2) // a multiple node
10093 vector<int> orderedDoms;
10094 //MESSAGE("multiple node " << oldId);
10095 if (mutipleNodes.count(oldId))
10096 orderedDoms = mutipleNodes[oldId];
10099 map<int,int>::iterator it = nodeDomains[oldId].begin();
10100 for (; it != nodeDomains[oldId].end(); ++it)
10101 orderedDoms.push_back(it->first);
10103 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10104 //stringstream txt;
10105 //for (int i=0; i<orderedDoms.size(); i++)
10106 // txt << orderedDoms[i] << " ";
10107 //MESSAGE("orderedDoms " << txt.str());
10108 mutipleNodes[oldId] = orderedDoms;
10110 double *coords = grid->GetPoint(oldId);
10111 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10112 int newId = newNode->getVtkId();
10113 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10114 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10121 MESSAGE(".. Creation of elements");
10122 for (int idomain = 0; idomain < theElems.size(); idomain++)
10124 itface = faceDomains.begin();
10125 for (; itface != faceDomains.end(); ++itface)
10127 std::map<int, int> domvol = itface->second;
10128 if (!domvol.count(idomain))
10130 DownIdType face = itface->first;
10131 //MESSAGE(" --- face " << face.cellId);
10132 std::set<int> oldNodes;
10134 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10135 int nbMultipleNodes = 0;
10136 std::set<int>::iterator itn = oldNodes.begin();
10137 for (; itn != oldNodes.end(); ++itn)
10140 if (mutipleNodes.count(oldId))
10143 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10145 //MESSAGE("multiple Nodes detected on a shared face");
10146 int downId = itface->first.cellId;
10147 unsigned char cellType = itface->first.cellType;
10148 // --- shared edge or shared face ?
10149 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10152 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10153 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10154 if (mutipleNodes.count(nodes[i]))
10155 if (!mutipleNodesToFace.count(nodes[i]))
10156 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10158 else // shared face (between two volumes)
10160 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10161 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10162 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10163 for (int ie =0; ie < nbEdges; ie++)
10166 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10167 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10169 vector<int> vn0 = mutipleNodes[nodes[0]];
10170 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10172 for (int i0 = 0; i0 < vn0.size(); i0++)
10173 for (int i1 = 0; i1 < vn1.size(); i1++)
10174 if (vn0[i0] == vn1[i1])
10175 doms.push_back(vn0[i0]);
10176 if (doms.size() >2)
10178 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10179 double *coords = grid->GetPoint(nodes[0]);
10180 gp_Pnt p0(coords[0], coords[1], coords[2]);
10181 coords = grid->GetPoint(nodes[nbNodes - 1]);
10182 gp_Pnt p1(coords[0], coords[1], coords[2]);
10184 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10185 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10186 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10187 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10188 for (int id=0; id < doms.size(); id++)
10190 int idom = doms[id];
10191 for (int ivol=0; ivol<nbvol; ivol++)
10193 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10194 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10195 if (theElems[idom].count(elem))
10197 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10198 domvol[idom] = svol;
10199 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10201 vtkIdType npts = 0;
10202 vtkIdType* pts = 0;
10203 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10204 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10207 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10208 angleDom[idom] = 0;
10212 gp_Pnt g(values[0], values[1], values[2]);
10213 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10214 //MESSAGE(" angle=" << angleDom[idom]);
10220 map<double, int> sortedDom; // sort domains by angle
10221 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10222 sortedDom[ia->second] = ia->first;
10223 vector<int> vnodes;
10225 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10227 vdom.push_back(ib->second);
10228 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10230 for (int ino = 0; ino < nbNodes; ino++)
10231 vnodes.push_back(nodes[ino]);
10232 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10241 // --- iterate on shared faces (volumes to modify, face to extrude)
10242 // get node id's of the face (id SMDS = id VTK)
10243 // create flat element with old and new nodes if requested
10245 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10246 // (domain1 X domain2) = domain1 + MAXINT*domain2
10248 std::map<int, std::map<long,int> > nodeQuadDomains;
10249 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10251 MESSAGE(".. Creation of elements: simple junction");
10252 if (createJointElems)
10255 string joints2DName = "joints2D";
10256 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10257 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10258 string joints3DName = "joints3D";
10259 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10260 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10262 itface = faceDomains.begin();
10263 for (; itface != faceDomains.end(); ++itface)
10265 DownIdType face = itface->first;
10266 std::set<int> oldNodes;
10267 std::set<int>::iterator itn;
10269 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10271 std::map<int, int> domvol = itface->second;
10272 std::map<int, int>::iterator itdom = domvol.begin();
10273 int dom1 = itdom->first;
10274 int vtkVolId = itdom->second;
10276 int dom2 = itdom->first;
10277 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10279 stringstream grpname;
10282 grpname << dom1 << "_" << dom2;
10284 grpname << dom2 << "_" << dom1;
10285 string namegrp = grpname.str();
10286 if (!mapOfJunctionGroups.count(namegrp))
10287 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10288 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10290 sgrp->Add(vol->GetID());
10291 if (vol->GetType() == SMDSAbs_Volume)
10292 joints3DGrp->Add(vol->GetID());
10293 else if (vol->GetType() == SMDSAbs_Face)
10294 joints2DGrp->Add(vol->GetID());
10298 // --- create volumes on multiple domain intersection if requested
10299 // iterate on mutipleNodesToFace
10300 // iterate on edgesMultiDomains
10302 MESSAGE(".. Creation of elements: multiple junction");
10303 if (createJointElems)
10305 // --- iterate on mutipleNodesToFace
10307 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
10308 for (; itn != mutipleNodesToFace.end(); ++itn)
10310 int node = itn->first;
10311 vector<int> orderDom = itn->second;
10312 vector<vtkIdType> orderedNodes;
10313 for (int idom = 0; idom <orderDom.size(); idom++)
10314 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
10315 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
10317 stringstream grpname;
10319 grpname << 0 << "_" << 0;
10321 string namegrp = grpname.str();
10322 if (!mapOfJunctionGroups.count(namegrp))
10323 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
10324 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10326 sgrp->Add(face->GetID());
10329 // --- iterate on edgesMultiDomains
10331 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
10332 for (; ite != edgesMultiDomains.end(); ++ite)
10334 vector<int> nodes = ite->first;
10335 vector<int> orderDom = ite->second;
10336 vector<vtkIdType> orderedNodes;
10337 if (nodes.size() == 2)
10339 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
10340 for (int ino=0; ino < nodes.size(); ino++)
10341 if (orderDom.size() == 3)
10342 for (int idom = 0; idom <orderDom.size(); idom++)
10343 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10345 for (int idom = orderDom.size()-1; idom >=0; idom--)
10346 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10347 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
10350 string namegrp = "jointsMultiples";
10351 if (!mapOfJunctionGroups.count(namegrp))
10352 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10353 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10355 sgrp->Add(vol->GetID());
10359 INFOS("Quadratic multiple joints not implemented");
10360 // TODO quadratic nodes
10365 // --- list the explicit faces and edges of the mesh that need to be modified,
10366 // i.e. faces and edges built with one or more duplicated nodes.
10367 // associate these faces or edges to their corresponding domain.
10368 // only the first domain found is kept when a face or edge is shared
10370 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
10371 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
10372 faceOrEdgeDom.clear();
10375 MESSAGE(".. Modification of elements");
10376 for (int idomain = 0; idomain < theElems.size(); idomain++)
10378 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
10379 for (; itnod != nodeDomains.end(); ++itnod)
10381 int oldId = itnod->first;
10382 //MESSAGE(" node " << oldId);
10383 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10384 for (int i = 0; i < l.ncells; i++)
10386 int vtkId = l.cells[i];
10387 int vtkType = grid->GetCellType(vtkId);
10388 int downId = grid->CellIdToDownId(vtkId);
10390 continue; // new cells: not to be modified
10391 DownIdType aCell(downId, vtkType);
10392 int volParents[1000];
10393 int nbvol = grid->GetParentVolumes(volParents, vtkId);
10394 for (int j = 0; j < nbvol; j++)
10395 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
10396 if (!feDom.count(vtkId))
10398 feDom[vtkId] = idomain;
10399 faceOrEdgeDom[aCell] = emptyMap;
10400 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
10401 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
10402 // << " type " << vtkType << " downId " << downId);
10408 // --- iterate on shared faces (volumes to modify, face to extrude)
10409 // get node id's of the face
10410 // replace old nodes by new nodes in volumes, and update inverse connectivity
10412 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
10413 for (int m=0; m<3; m++)
10415 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
10416 itface = (*amap).begin();
10417 for (; itface != (*amap).end(); ++itface)
10419 DownIdType face = itface->first;
10420 std::set<int> oldNodes;
10421 std::set<int>::iterator itn;
10423 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10424 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
10425 std::map<int, int> localClonedNodeIds;
10427 std::map<int, int> domvol = itface->second;
10428 std::map<int, int>::iterator itdom = domvol.begin();
10429 for (; itdom != domvol.end(); ++itdom)
10431 int idom = itdom->first;
10432 int vtkVolId = itdom->second;
10433 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
10434 localClonedNodeIds.clear();
10435 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
10438 if (nodeDomains[oldId].count(idom))
10440 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
10441 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
10444 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
10449 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
10450 grid->BuildLinks();
10458 * \brief Double nodes on some external faces and create flat elements.
10459 * Flat elements are mainly used by some types of mechanic calculations.
10461 * Each group of the list must be constituted of faces.
10462 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10463 * @param theElems - list of groups of faces, where a group of faces is a set of
10464 * SMDS_MeshElements sorted by Id.
10465 * @return TRUE if operation has been completed successfully, FALSE otherwise
10467 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
10469 MESSAGE("-------------------------------------------------");
10470 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
10471 MESSAGE("-------------------------------------------------");
10473 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10475 // --- For each group of faces
10476 // duplicate the nodes, create a flat element based on the face
10477 // replace the nodes of the faces by their clones
10479 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
10480 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
10481 clonedNodes.clear();
10482 intermediateNodes.clear();
10483 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10484 mapOfJunctionGroups.clear();
10486 for (int idom = 0; idom < theElems.size(); idom++)
10488 const TIDSortedElemSet& domain = theElems[idom];
10489 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10490 for (; elemItr != domain.end(); ++elemItr)
10492 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10493 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
10496 // MESSAGE("aFace=" << aFace->GetID());
10497 bool isQuad = aFace->IsQuadratic();
10498 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
10500 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
10502 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
10503 while (nodeIt->more())
10505 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
10506 bool isMedium = isQuad && (aFace->IsMediumNode(node));
10508 ln2.push_back(node);
10510 ln0.push_back(node);
10512 const SMDS_MeshNode* clone = 0;
10513 if (!clonedNodes.count(node))
10515 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
10516 clonedNodes[node] = clone;
10519 clone = clonedNodes[node];
10522 ln3.push_back(clone);
10524 ln1.push_back(clone);
10526 const SMDS_MeshNode* inter = 0;
10527 if (isQuad && (!isMedium))
10529 if (!intermediateNodes.count(node))
10531 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
10532 intermediateNodes[node] = inter;
10535 inter = intermediateNodes[node];
10536 ln4.push_back(inter);
10540 // --- extrude the face
10542 vector<const SMDS_MeshNode*> ln;
10543 SMDS_MeshVolume* vol = 0;
10544 vtkIdType aType = aFace->GetVtkType();
10548 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
10549 // MESSAGE("vol prism " << vol->GetID());
10550 ln.push_back(ln1[0]);
10551 ln.push_back(ln1[1]);
10552 ln.push_back(ln1[2]);
10555 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
10556 // MESSAGE("vol hexa " << vol->GetID());
10557 ln.push_back(ln1[0]);
10558 ln.push_back(ln1[1]);
10559 ln.push_back(ln1[2]);
10560 ln.push_back(ln1[3]);
10562 case VTK_QUADRATIC_TRIANGLE:
10563 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
10564 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
10565 // MESSAGE("vol quad prism " << vol->GetID());
10566 ln.push_back(ln1[0]);
10567 ln.push_back(ln1[1]);
10568 ln.push_back(ln1[2]);
10569 ln.push_back(ln3[0]);
10570 ln.push_back(ln3[1]);
10571 ln.push_back(ln3[2]);
10573 case VTK_QUADRATIC_QUAD:
10574 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
10575 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
10576 // ln4[0], ln4[1], ln4[2], ln4[3]);
10577 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
10578 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
10579 ln4[0], ln4[1], ln4[2], ln4[3]);
10580 // MESSAGE("vol quad hexa " << vol->GetID());
10581 ln.push_back(ln1[0]);
10582 ln.push_back(ln1[1]);
10583 ln.push_back(ln1[2]);
10584 ln.push_back(ln1[3]);
10585 ln.push_back(ln3[0]);
10586 ln.push_back(ln3[1]);
10587 ln.push_back(ln3[2]);
10588 ln.push_back(ln3[3]);
10598 stringstream grpname;
10602 string namegrp = grpname.str();
10603 if (!mapOfJunctionGroups.count(namegrp))
10604 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10605 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10607 sgrp->Add(vol->GetID());
10610 // --- modify the face
10612 aFace->ChangeNodes(&ln[0], ln.size());
10619 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
10620 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
10621 * groups of faces to remove inside the object, (idem edges).
10622 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
10624 void SMESH_MeshEditor::CreateHoleSkin(double radius,
10625 const TopoDS_Shape& theShape,
10626 SMESH_NodeSearcher* theNodeSearcher,
10627 const char* groupName,
10628 std::vector<double>& nodesCoords,
10629 std::vector<std::vector<int> >& listOfListOfNodes)
10631 MESSAGE("--------------------------------");
10632 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
10633 MESSAGE("--------------------------------");
10635 // --- zone of volumes to remove is given :
10636 // 1 either by a geom shape (one or more vertices) and a radius,
10637 // 2 either by a group of nodes (representative of the shape)to use with the radius,
10638 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
10639 // In the case 2, the group of nodes is an external group of nodes from another mesh,
10640 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
10641 // defined by it's name.
10643 SMESHDS_GroupBase* groupDS = 0;
10644 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
10645 while ( groupIt->more() )
10648 SMESH_Group * group = groupIt->next();
10649 if ( !group ) continue;
10650 groupDS = group->GetGroupDS();
10651 if ( !groupDS || groupDS->IsEmpty() ) continue;
10652 std::string grpName = group->GetName();
10653 //MESSAGE("grpName=" << grpName);
10654 if (grpName == groupName)
10660 bool isNodeGroup = false;
10661 bool isNodeCoords = false;
10664 if (groupDS->GetType() != SMDSAbs_Node)
10666 isNodeGroup = true; // a group of nodes exists and it is in this mesh
10669 if (nodesCoords.size() > 0)
10670 isNodeCoords = true; // a list o nodes given by their coordinates
10671 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
10673 // --- define groups to build
10675 int idg; // --- group of SMDS volumes
10676 string grpvName = groupName;
10677 grpvName += "_vol";
10678 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
10681 MESSAGE("group not created " << grpvName);
10684 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
10686 int idgs; // --- group of SMDS faces on the skin
10687 string grpsName = groupName;
10688 grpsName += "_skin";
10689 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
10692 MESSAGE("group not created " << grpsName);
10695 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
10697 int idgi; // --- group of SMDS faces internal (several shapes)
10698 string grpiName = groupName;
10699 grpiName += "_internalFaces";
10700 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
10703 MESSAGE("group not created " << grpiName);
10706 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
10708 int idgei; // --- group of SMDS faces internal (several shapes)
10709 string grpeiName = groupName;
10710 grpeiName += "_internalEdges";
10711 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
10714 MESSAGE("group not created " << grpeiName);
10717 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
10719 // --- build downward connectivity
10721 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10722 meshDS->BuildDownWardConnectivity(true);
10723 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
10725 // --- set of volumes detected inside
10727 std::set<int> setOfInsideVol;
10728 std::set<int> setOfVolToCheck;
10730 std::vector<gp_Pnt> gpnts;
10733 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
10735 MESSAGE("group of nodes provided");
10736 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
10737 while ( elemIt->more() )
10739 const SMDS_MeshElement* elem = elemIt->next();
10742 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
10745 SMDS_MeshElement* vol = 0;
10746 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
10747 while (volItr->more())
10749 vol = (SMDS_MeshElement*)volItr->next();
10750 setOfInsideVol.insert(vol->getVtkId());
10751 sgrp->Add(vol->GetID());
10755 else if (isNodeCoords)
10757 MESSAGE("list of nodes coordinates provided");
10760 while (i < nodesCoords.size()-2)
10762 double x = nodesCoords[i++];
10763 double y = nodesCoords[i++];
10764 double z = nodesCoords[i++];
10765 gp_Pnt p = gp_Pnt(x, y ,z);
10766 gpnts.push_back(p);
10767 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
10770 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
10772 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
10773 TopTools_IndexedMapOfShape vertexMap;
10774 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
10775 gp_Pnt p = gp_Pnt(0,0,0);
10776 if (vertexMap.Extent() < 1)
10779 for ( int i = 1; i <= vertexMap.Extent(); ++i )
10781 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
10782 p = BRep_Tool::Pnt(vertex);
10783 gpnts.push_back(p);
10784 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
10788 if (gpnts.size() > 0)
10791 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
10793 nodeId = startNode->GetID();
10794 MESSAGE("nodeId " << nodeId);
10796 double radius2 = radius*radius;
10797 MESSAGE("radius2 " << radius2);
10799 // --- volumes on start node
10801 setOfVolToCheck.clear();
10802 SMDS_MeshElement* startVol = 0;
10803 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
10804 while (volItr->more())
10806 startVol = (SMDS_MeshElement*)volItr->next();
10807 setOfVolToCheck.insert(startVol->getVtkId());
10809 if (setOfVolToCheck.empty())
10811 MESSAGE("No volumes found");
10815 // --- starting with central volumes then their neighbors, check if they are inside
10816 // or outside the domain, until no more new neighbor volume is inside.
10817 // Fill the group of inside volumes
10819 std::map<int, double> mapOfNodeDistance2;
10820 mapOfNodeDistance2.clear();
10821 std::set<int> setOfOutsideVol;
10822 while (!setOfVolToCheck.empty())
10824 std::set<int>::iterator it = setOfVolToCheck.begin();
10826 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10827 bool volInside = false;
10828 vtkIdType npts = 0;
10829 vtkIdType* pts = 0;
10830 grid->GetCellPoints(vtkId, npts, pts);
10831 for (int i=0; i<npts; i++)
10833 double distance2 = 0;
10834 if (mapOfNodeDistance2.count(pts[i]))
10836 distance2 = mapOfNodeDistance2[pts[i]];
10837 MESSAGE("point " << pts[i] << " distance2 " << distance2);
10841 double *coords = grid->GetPoint(pts[i]);
10842 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
10844 for (int j=0; j<gpnts.size(); j++)
10846 double d2 = aPoint.SquareDistance(gpnts[j]);
10847 if (d2 < distance2)
10850 if (distance2 < radius2)
10854 mapOfNodeDistance2[pts[i]] = distance2;
10855 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
10857 if (distance2 < radius2)
10859 volInside = true; // one or more nodes inside the domain
10860 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
10866 setOfInsideVol.insert(vtkId);
10867 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10868 int neighborsVtkIds[NBMAXNEIGHBORS];
10869 int downIds[NBMAXNEIGHBORS];
10870 unsigned char downTypes[NBMAXNEIGHBORS];
10871 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10872 for (int n = 0; n < nbNeighbors; n++)
10873 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
10874 setOfVolToCheck.insert(neighborsVtkIds[n]);
10878 setOfOutsideVol.insert(vtkId);
10879 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10881 setOfVolToCheck.erase(vtkId);
10885 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
10886 // If yes, add the volume to the inside set
10888 bool addedInside = true;
10889 std::set<int> setOfVolToReCheck;
10890 while (addedInside)
10892 MESSAGE(" --------------------------- re check");
10893 addedInside = false;
10894 std::set<int>::iterator itv = setOfInsideVol.begin();
10895 for (; itv != setOfInsideVol.end(); ++itv)
10898 int neighborsVtkIds[NBMAXNEIGHBORS];
10899 int downIds[NBMAXNEIGHBORS];
10900 unsigned char downTypes[NBMAXNEIGHBORS];
10901 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10902 for (int n = 0; n < nbNeighbors; n++)
10903 if (!setOfInsideVol.count(neighborsVtkIds[n]))
10904 setOfVolToReCheck.insert(neighborsVtkIds[n]);
10906 setOfVolToCheck = setOfVolToReCheck;
10907 setOfVolToReCheck.clear();
10908 while (!setOfVolToCheck.empty())
10910 std::set<int>::iterator it = setOfVolToCheck.begin();
10912 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
10914 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10915 int countInside = 0;
10916 int neighborsVtkIds[NBMAXNEIGHBORS];
10917 int downIds[NBMAXNEIGHBORS];
10918 unsigned char downTypes[NBMAXNEIGHBORS];
10919 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10920 for (int n = 0; n < nbNeighbors; n++)
10921 if (setOfInsideVol.count(neighborsVtkIds[n]))
10923 MESSAGE("countInside " << countInside);
10924 if (countInside > 1)
10926 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10927 setOfInsideVol.insert(vtkId);
10928 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
10929 addedInside = true;
10932 setOfVolToReCheck.insert(vtkId);
10934 setOfVolToCheck.erase(vtkId);
10938 // --- map of Downward faces at the boundary, inside the global volume
10939 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
10940 // fill group of SMDS faces inside the volume (when several volume shapes)
10941 // fill group of SMDS faces on the skin of the global volume (if skin)
10943 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
10944 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
10945 std::set<int>::iterator it = setOfInsideVol.begin();
10946 for (; it != setOfInsideVol.end(); ++it)
10949 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10950 int neighborsVtkIds[NBMAXNEIGHBORS];
10951 int downIds[NBMAXNEIGHBORS];
10952 unsigned char downTypes[NBMAXNEIGHBORS];
10953 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
10954 for (int n = 0; n < nbNeighbors; n++)
10956 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
10957 if (neighborDim == 3)
10959 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
10961 DownIdType face(downIds[n], downTypes[n]);
10962 boundaryFaces[face] = vtkId;
10964 // if the face between to volumes is in the mesh, get it (internal face between shapes)
10965 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
10966 if (vtkFaceId >= 0)
10968 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
10969 // find also the smds edges on this face
10970 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
10971 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
10972 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
10973 for (int i = 0; i < nbEdges; i++)
10975 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
10976 if (vtkEdgeId >= 0)
10977 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
10981 else if (neighborDim == 2) // skin of the volume
10983 DownIdType face(downIds[n], downTypes[n]);
10984 skinFaces[face] = vtkId;
10985 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
10986 if (vtkFaceId >= 0)
10987 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
10992 // --- identify the edges constituting the wire of each subshape on the skin
10993 // define polylines with the nodes of edges, equivalent to wires
10994 // project polylines on subshapes, and partition, to get geom faces
10996 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
10997 std::set<int> emptySet;
10999 std::set<int> shapeIds;
11001 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11002 while (itelem->more())
11004 const SMDS_MeshElement *elem = itelem->next();
11005 int shapeId = elem->getshapeId();
11006 int vtkId = elem->getVtkId();
11007 if (!shapeIdToVtkIdSet.count(shapeId))
11009 shapeIdToVtkIdSet[shapeId] = emptySet;
11010 shapeIds.insert(shapeId);
11012 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11015 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11016 std::set<DownIdType, DownIdCompare> emptyEdges;
11017 emptyEdges.clear();
11019 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11020 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11022 int shapeId = itShape->first;
11023 MESSAGE(" --- Shape ID --- "<< shapeId);
11024 shapeIdToEdges[shapeId] = emptyEdges;
11026 std::vector<int> nodesEdges;
11028 std::set<int>::iterator its = itShape->second.begin();
11029 for (; its != itShape->second.end(); ++its)
11032 MESSAGE(" " << vtkId);
11033 int neighborsVtkIds[NBMAXNEIGHBORS];
11034 int downIds[NBMAXNEIGHBORS];
11035 unsigned char downTypes[NBMAXNEIGHBORS];
11036 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11037 for (int n = 0; n < nbNeighbors; n++)
11039 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11041 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11042 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11043 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11045 DownIdType edge(downIds[n], downTypes[n]);
11046 if (!shapeIdToEdges[shapeId].count(edge))
11048 shapeIdToEdges[shapeId].insert(edge);
11050 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11051 nodesEdges.push_back(vtkNodeId[0]);
11052 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11053 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11059 std::list<int> order;
11061 if (nodesEdges.size() > 0)
11063 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11064 nodesEdges[0] = -1;
11065 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11066 nodesEdges[1] = -1; // do not reuse this edge
11070 int nodeTofind = order.back(); // try first to push back
11072 for (i = 0; i<nodesEdges.size(); i++)
11073 if (nodesEdges[i] == nodeTofind)
11075 if (i == nodesEdges.size())
11076 found = false; // no follower found on back
11079 if (i%2) // odd ==> use the previous one
11080 if (nodesEdges[i-1] < 0)
11084 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11085 nodesEdges[i-1] = -1;
11087 else // even ==> use the next one
11088 if (nodesEdges[i+1] < 0)
11092 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11093 nodesEdges[i+1] = -1;
11098 // try to push front
11100 nodeTofind = order.front(); // try to push front
11101 for (i = 0; i<nodesEdges.size(); i++)
11102 if (nodesEdges[i] == nodeTofind)
11104 if (i == nodesEdges.size())
11106 found = false; // no predecessor found on front
11109 if (i%2) // odd ==> use the previous one
11110 if (nodesEdges[i-1] < 0)
11114 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11115 nodesEdges[i-1] = -1;
11117 else // even ==> use the next one
11118 if (nodesEdges[i+1] < 0)
11122 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11123 nodesEdges[i+1] = -1;
11129 std::vector<int> nodes;
11130 nodes.push_back(shapeId);
11131 std::list<int>::iterator itl = order.begin();
11132 for (; itl != order.end(); itl++)
11134 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11135 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11137 listOfListOfNodes.push_back(nodes);
11140 // partition geom faces with blocFissure
11141 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11142 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11148 //================================================================================
11150 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11151 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11152 * \return TRUE if operation has been completed successfully, FALSE otherwise
11154 //================================================================================
11156 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11158 // iterates on volume elements and detect all free faces on them
11159 SMESHDS_Mesh* aMesh = GetMeshDS();
11162 //bool res = false;
11163 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11164 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11167 const SMDS_MeshVolume* volume = vIt->next();
11168 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11169 vTool.SetExternalNormal();
11170 //const bool isPoly = volume->IsPoly();
11171 const int iQuad = volume->IsQuadratic();
11172 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11174 if (!vTool.IsFreeFace(iface))
11177 vector<const SMDS_MeshNode *> nodes;
11178 int nbFaceNodes = vTool.NbFaceNodes(iface);
11179 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11181 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11182 nodes.push_back(faceNodes[inode]);
11183 if (iQuad) { // add medium nodes
11184 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11185 nodes.push_back(faceNodes[inode]);
11186 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11187 nodes.push_back(faceNodes[8]);
11189 // add new face based on volume nodes
11190 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11192 continue; // face already exsist
11194 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11198 return ( nbFree==(nbExisted+nbCreated) );
11203 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11205 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11207 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11210 //================================================================================
11212 * \brief Creates missing boundary elements
11213 * \param elements - elements whose boundary is to be checked
11214 * \param dimension - defines type of boundary elements to create
11215 * \param group - a group to store created boundary elements in
11216 * \param targetMesh - a mesh to store created boundary elements in
11217 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11218 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11219 * boundary elements will be copied into the targetMesh
11220 * \param toAddExistingBondary - if true, not only new but also pre-existing
11221 * boundary elements will be added into the new group
11222 * \param aroundElements - if true, elements will be created on boundary of given
11223 * elements else, on boundary of the whole mesh.
11224 * \return nb of added boundary elements
11226 //================================================================================
11228 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11229 Bnd_Dimension dimension,
11230 SMESH_Group* group/*=0*/,
11231 SMESH_Mesh* targetMesh/*=0*/,
11232 bool toCopyElements/*=false*/,
11233 bool toCopyExistingBoundary/*=false*/,
11234 bool toAddExistingBondary/*= false*/,
11235 bool aroundElements/*= false*/)
11237 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11238 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11239 // hope that all elements are of the same type, do not check them all
11240 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11241 throw SALOME_Exception(LOCALIZED("wrong element type"));
11244 toCopyElements = toCopyExistingBoundary = false;
11246 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11247 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11248 int nbAddedBnd = 0;
11250 // editor adding present bnd elements and optionally holding elements to add to the group
11251 SMESH_MeshEditor* presentEditor;
11252 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11253 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11255 SMESH_MesherHelper helper( *myMesh );
11256 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11257 SMDS_VolumeTool vTool;
11258 TIDSortedElemSet avoidSet;
11259 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11262 typedef vector<const SMDS_MeshNode*> TConnectivity;
11264 SMDS_ElemIteratorPtr eIt;
11265 if (elements.empty())
11266 eIt = aMesh->elementsIterator(elemType);
11268 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11270 while (eIt->more())
11272 const SMDS_MeshElement* elem = eIt->next();
11273 const int iQuad = elem->IsQuadratic();
11275 // ------------------------------------------------------------------------------------
11276 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11277 // ------------------------------------------------------------------------------------
11278 vector<const SMDS_MeshElement*> presentBndElems;
11279 vector<TConnectivity> missingBndElems;
11280 TConnectivity nodes, elemNodes;
11281 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11283 vTool.SetExternalNormal();
11284 const SMDS_MeshElement* otherVol = 0;
11285 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11287 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11288 ( !aroundElements || elements.count( otherVol )))
11290 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11291 const int nbFaceNodes = vTool.NbFaceNodes (iface);
11292 if ( missType == SMDSAbs_Edge ) // boundary edges
11294 nodes.resize( 2+iQuad );
11295 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11297 for ( int j = 0; j < nodes.size(); ++j )
11299 if ( const SMDS_MeshElement* edge =
11300 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11301 presentBndElems.push_back( edge );
11303 missingBndElems.push_back( nodes );
11306 else // boundary face
11309 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11310 nodes.push_back( nn[inode] ); // add corner nodes
11312 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11313 nodes.push_back( nn[inode] ); // add medium nodes
11314 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11316 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11318 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11319 SMDSAbs_Face, /*noMedium=*/false ))
11320 presentBndElems.push_back( f );
11322 missingBndElems.push_back( nodes );
11324 if ( targetMesh != myMesh )
11326 // add 1D elements on face boundary to be added to a new mesh
11327 const SMDS_MeshElement* edge;
11328 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11331 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11333 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11334 if ( edge && avoidSet.insert( edge ).second )
11335 presentBndElems.push_back( edge );
11341 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
11343 avoidSet.clear(), avoidSet.insert( elem );
11344 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
11345 SMDS_MeshElement::iterator() );
11346 elemNodes.push_back( elemNodes[0] );
11347 nodes.resize( 2 + iQuad );
11348 const int nbLinks = elem->NbCornerNodes();
11349 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
11351 nodes[0] = elemNodes[iN];
11352 nodes[1] = elemNodes[iN+1+iQuad];
11353 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11354 continue; // not free link
11356 if ( iQuad ) nodes[2] = elemNodes[iN+1];
11357 if ( const SMDS_MeshElement* edge =
11358 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11359 presentBndElems.push_back( edge );
11361 missingBndElems.push_back( nodes );
11365 // ---------------------------------
11366 // 2. Add missing boundary elements
11367 // ---------------------------------
11368 if ( targetMesh != myMesh )
11369 // instead of making a map of nodes in this mesh and targetMesh,
11370 // we create nodes with same IDs.
11371 for ( int i = 0; i < missingBndElems.size(); ++i )
11373 TConnectivity& srcNodes = missingBndElems[i];
11374 TConnectivity nodes( srcNodes.size() );
11375 for ( inode = 0; inode < nodes.size(); ++inode )
11376 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11377 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11379 /*noMedium=*/false))
11381 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11385 for ( int i = 0; i < missingBndElems.size(); ++i )
11387 TConnectivity& nodes = missingBndElems[i];
11388 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11390 /*noMedium=*/false))
11392 SMDS_MeshElement* elem =
11393 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11396 // try to set a new element to a shape
11397 if ( myMesh->HasShapeToMesh() )
11400 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11401 const int nbN = nodes.size() / (iQuad+1 );
11402 for ( inode = 0; inode < nbN && ok; ++inode )
11404 pair<int, TopAbs_ShapeEnum> i_stype =
11405 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11406 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11407 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11409 if ( ok && mediumShapes.size() > 1 )
11411 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11412 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11413 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11415 if (( ok = ( stype_i->first != stype_i_0.first )))
11416 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11417 aMesh->IndexToShape( stype_i_0.second ));
11420 if ( ok && mediumShapes.begin()->first == missShapeType )
11421 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11425 // ----------------------------------
11426 // 3. Copy present boundary elements
11427 // ----------------------------------
11428 if ( toCopyExistingBoundary )
11429 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11431 const SMDS_MeshElement* e = presentBndElems[i];
11432 TConnectivity nodes( e->NbNodes() );
11433 for ( inode = 0; inode < nodes.size(); ++inode )
11434 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11435 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11437 else // store present elements to add them to a group
11438 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11440 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11443 } // loop on given elements
11445 // ---------------------------------------------
11446 // 4. Fill group with boundary elements
11447 // ---------------------------------------------
11450 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11451 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11452 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11454 tgtEditor.myLastCreatedElems.Clear();
11455 tgtEditor2.myLastCreatedElems.Clear();
11457 // -----------------------
11458 // 5. Copy given elements
11459 // -----------------------
11460 if ( toCopyElements && targetMesh != myMesh )
11462 if (elements.empty())
11463 eIt = aMesh->elementsIterator(elemType);
11465 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11466 while (eIt->more())
11468 const SMDS_MeshElement* elem = eIt->next();
11469 TConnectivity nodes( elem->NbNodes() );
11470 for ( inode = 0; inode < nodes.size(); ++inode )
11471 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11472 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11474 tgtEditor.myLastCreatedElems.Clear();