1 // Copyright (C) 2007-2011 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 // SMESH SMESH : idl implementation based on 'SMESH' unit's classes
24 // File : SMESH_MeshEditor.cxx
25 // Created : Mon Apr 12 16:10:22 2004
26 // Author : Edward AGAPOV (eap)
29 #include "SMESH_MeshEditor.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_VolumeTool.hxx"
33 #include "SMDS_EdgePosition.hxx"
34 #include "SMDS_PolyhedralVolumeOfNodes.hxx"
35 #include "SMDS_FacePosition.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 //#include "SMDS_QuadraticFaceOfNodes.hxx"
38 #include "SMDS_MeshGroup.hxx"
39 #include "SMDS_LinearEdge.hxx"
40 #include "SMDS_Downward.hxx"
41 #include "SMDS_SetIterator.hxx"
43 #include "SMESHDS_Group.hxx"
44 #include "SMESHDS_Mesh.hxx"
46 #include "SMESH_Algo.hxx"
47 #include "SMESH_ControlsDef.hxx"
48 #include "SMESH_Group.hxx"
49 #include "SMESH_MesherHelper.hxx"
50 #include "SMESH_OctreeNode.hxx"
51 #include "SMESH_subMesh.hxx"
53 #include "utilities.h"
55 #include <BRepAdaptor_Surface.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 <GC_MakeSegment.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAPI_ExtremaCurveCurve.hxx>
65 #include <GeomAdaptor_Surface.hxx>
66 #include <Geom_Curve.hxx>
67 #include <Geom_Line.hxx>
68 #include <Geom_Surface.hxx>
69 #include <IntAna_IntConicQuad.hxx>
70 #include <IntAna_Quadric.hxx>
71 #include <Precision.hxx>
72 #include <TColStd_ListOfInteger.hxx>
73 #include <TopAbs_State.hxx>
75 #include <TopExp_Explorer.hxx>
76 #include <TopTools_ListIteratorOfListOfShape.hxx>
77 #include <TopTools_ListOfShape.hxx>
78 #include <TopTools_SequenceOfShape.hxx>
80 #include <TopoDS_Face.hxx>
86 #include <gp_Trsf.hxx>
100 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103 using namespace SMESH::Controls;
105 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
106 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
108 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
110 //=======================================================================
111 //function : SMESH_MeshEditor
113 //=======================================================================
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116 :myMesh( theMesh ) // theMesh may be NULL
120 //=======================================================================
124 //=======================================================================
127 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
128 const SMDSAbs_ElementType type,
132 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
133 SMDS_MeshElement* e = 0;
134 int nbnode = node.size();
135 SMESHDS_Mesh* mesh = GetMeshDS();
140 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
141 else e = mesh->AddFace (node[0], node[1], node[2] );
143 else if (nbnode == 4) {
144 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
145 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
147 else if (nbnode == 6) {
148 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
149 node[4], node[5], ID);
150 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
153 else if (nbnode == 8) {
154 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
155 node[4], node[5], node[6], node[7], ID);
156 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
157 node[4], node[5], node[6], node[7] );
160 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
161 else e = mesh->AddPolygonalFace (node );
168 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
169 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
171 else if (nbnode == 5) {
172 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
174 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
177 else if (nbnode == 6) {
178 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
179 node[4], node[5], ID);
180 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
183 else if (nbnode == 8) {
184 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
185 node[4], node[5], node[6], node[7], ID);
186 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
187 node[4], node[5], node[6], node[7] );
189 else if (nbnode == 10) {
190 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
191 node[4], node[5], node[6], node[7],
192 node[8], node[9], ID);
193 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
194 node[4], node[5], node[6], node[7],
197 else if (nbnode == 13) {
198 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
199 node[4], node[5], node[6], node[7],
200 node[8], node[9], node[10],node[11],
202 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
203 node[4], node[5], node[6], node[7],
204 node[8], node[9], node[10],node[11],
207 else if (nbnode == 15) {
208 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], node[7],
210 node[8], node[9], node[10],node[11],
211 node[12],node[13],node[14],ID);
212 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6], node[7],
214 node[8], node[9], node[10],node[11],
215 node[12],node[13],node[14] );
217 else if (nbnode == 20) {
218 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7],
220 node[8], node[9], node[10],node[11],
221 node[12],node[13],node[14],node[15],
222 node[16],node[17],node[18],node[19],ID);
223 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7],
225 node[8], node[9], node[10],node[11],
226 node[12],node[13],node[14],node[15],
227 node[16],node[17],node[18],node[19] );
234 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
235 else e = mesh->AddEdge (node[0], node[1] );
237 else if ( nbnode == 3 ) {
238 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
239 else e = mesh->AddEdge (node[0], node[1], node[2] );
243 case SMDSAbs_0DElement:
245 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
246 else e = mesh->Add0DElement (node[0] );
251 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
252 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
257 if ( e ) myLastCreatedElems.Append( e );
261 //=======================================================================
265 //=======================================================================
267 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
268 const SMDSAbs_ElementType type,
272 vector<const SMDS_MeshNode*> nodes;
273 nodes.reserve( nodeIDs.size() );
274 vector<int>::const_iterator id = nodeIDs.begin();
275 while ( id != nodeIDs.end() ) {
276 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
277 nodes.push_back( node );
281 return AddElement( nodes, type, isPoly, ID );
284 //=======================================================================
286 //purpose : Remove a node or an element.
287 // Modify a compute state of sub-meshes which become empty
288 //=======================================================================
290 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
293 myLastCreatedElems.Clear();
294 myLastCreatedNodes.Clear();
296 SMESHDS_Mesh* aMesh = GetMeshDS();
297 set< SMESH_subMesh *> smmap;
300 list<int>::const_iterator it = theIDs.begin();
301 for ( ; it != theIDs.end(); it++ ) {
302 const SMDS_MeshElement * elem;
304 elem = aMesh->FindNode( *it );
306 elem = aMesh->FindElement( *it );
310 // Notify VERTEX sub-meshes about modification
312 const SMDS_MeshNode* node = cast2Node( elem );
313 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
314 if ( int aShapeID = node->getshapeId() )
315 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
318 // Find sub-meshes to notify about modification
319 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
320 // while ( nodeIt->more() ) {
321 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
322 // const SMDS_PositionPtr& aPosition = node->GetPosition();
323 // if ( aPosition.get() ) {
324 // if ( int aShapeID = aPosition->GetShapeId() ) {
325 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
326 // smmap.insert( sm );
333 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
335 aMesh->RemoveElement( elem );
339 // Notify sub-meshes about modification
340 if ( !smmap.empty() ) {
341 set< SMESH_subMesh *>::iterator smIt;
342 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
343 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
346 // // Check if the whole mesh becomes empty
347 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
348 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
353 //=======================================================================
354 //function : FindShape
355 //purpose : Return an index of the shape theElem is on
356 // or zero if a shape not found
357 //=======================================================================
359 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
361 myLastCreatedElems.Clear();
362 myLastCreatedNodes.Clear();
364 SMESHDS_Mesh * aMesh = GetMeshDS();
365 if ( aMesh->ShapeToMesh().IsNull() )
368 int aShapeID = theElem->getshapeId();
372 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
373 if ( sm->Contains( theElem ))
376 if ( theElem->GetType() == SMDSAbs_Node ) {
377 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
380 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
383 TopoDS_Shape aShape; // the shape a node of theElem is on
384 if ( theElem->GetType() != SMDSAbs_Node )
386 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
387 while ( nodeIt->more() ) {
388 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
389 if ((aShapeID = node->getshapeId()) > 0) {
390 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
391 if ( sm->Contains( theElem ))
393 if ( aShape.IsNull() )
394 aShape = aMesh->IndexToShape( aShapeID );
400 // None of nodes is on a proper shape,
401 // find the shape among ancestors of aShape on which a node is
402 if ( !aShape.IsNull() ) {
403 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
404 for ( ; ancIt.More(); ancIt.Next() ) {
405 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
406 if ( sm && sm->Contains( theElem ))
407 return aMesh->ShapeToIndex( ancIt.Value() );
412 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
413 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
414 for ( ; id_sm != id2sm.end(); ++id_sm )
415 if ( id_sm->second->Contains( theElem ))
419 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
423 //=======================================================================
424 //function : IsMedium
426 //=======================================================================
428 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
429 const SMDSAbs_ElementType typeToCheck)
431 bool isMedium = false;
432 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
433 while (it->more() && !isMedium ) {
434 const SMDS_MeshElement* elem = it->next();
435 isMedium = elem->IsMediumNode(node);
440 //=======================================================================
441 //function : ShiftNodesQuadTria
443 // Shift nodes in the array corresponded to quadratic triangle
444 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
445 //=======================================================================
446 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
448 const SMDS_MeshNode* nd1 = aNodes[0];
449 aNodes[0] = aNodes[1];
450 aNodes[1] = aNodes[2];
452 const SMDS_MeshNode* nd2 = aNodes[3];
453 aNodes[3] = aNodes[4];
454 aNodes[4] = aNodes[5];
458 //=======================================================================
459 //function : GetNodesFromTwoTria
461 // Shift nodes in the array corresponded to quadratic triangle
462 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
463 //=======================================================================
464 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
465 const SMDS_MeshElement * theTria2,
466 const SMDS_MeshNode* N1[],
467 const SMDS_MeshNode* N2[])
469 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
472 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
475 if(it->more()) return false;
476 it = theTria2->nodesIterator();
479 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
482 if(it->more()) return false;
484 int sames[3] = {-1,-1,-1};
496 if(nbsames!=2) return false;
498 ShiftNodesQuadTria(N1);
500 ShiftNodesQuadTria(N1);
503 i = sames[0] + sames[1] + sames[2];
505 ShiftNodesQuadTria(N2);
507 // now we receive following N1 and N2 (using numeration as above image)
508 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
509 // i.e. first nodes from both arrays determ new diagonal
513 //=======================================================================
514 //function : InverseDiag
515 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
516 // but having other common link.
517 // Return False if args are improper
518 //=======================================================================
520 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
521 const SMDS_MeshElement * theTria2 )
523 MESSAGE("InverseDiag");
524 myLastCreatedElems.Clear();
525 myLastCreatedNodes.Clear();
527 if (!theTria1 || !theTria2)
530 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
531 if (!F1) return false;
532 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
533 if (!F2) return false;
534 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
535 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
537 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
538 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
542 // put nodes in array and find out indices of the same ones
543 const SMDS_MeshNode* aNodes [6];
544 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
546 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
547 while ( it->more() ) {
548 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
550 if ( i > 2 ) // theTria2
551 // find same node of theTria1
552 for ( int j = 0; j < 3; j++ )
553 if ( aNodes[ i ] == aNodes[ j ]) {
562 return false; // theTria1 is not a triangle
563 it = theTria2->nodesIterator();
565 if ( i == 6 && it->more() )
566 return false; // theTria2 is not a triangle
569 // find indices of 1,2 and of A,B in theTria1
570 int iA = 0, iB = 0, i1 = 0, i2 = 0;
571 for ( i = 0; i < 6; i++ ) {
572 if ( sameInd [ i ] == 0 ) {
581 // nodes 1 and 2 should not be the same
582 if ( aNodes[ i1 ] == aNodes[ i2 ] )
586 aNodes[ iA ] = aNodes[ i2 ];
588 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
590 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
591 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
595 } // end if(F1 && F2)
597 // check case of quadratic faces
598 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
600 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
604 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
605 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
613 const SMDS_MeshNode* N1 [6];
614 const SMDS_MeshNode* N2 [6];
615 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
617 // now we receive following N1 and N2 (using numeration as above image)
618 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
619 // i.e. first nodes from both arrays determ new diagonal
621 const SMDS_MeshNode* N1new [6];
622 const SMDS_MeshNode* N2new [6];
635 // replaces nodes in faces
636 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
637 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
642 //=======================================================================
643 //function : findTriangles
644 //purpose : find triangles sharing theNode1-theNode2 link
645 //=======================================================================
647 static bool findTriangles(const SMDS_MeshNode * theNode1,
648 const SMDS_MeshNode * theNode2,
649 const SMDS_MeshElement*& theTria1,
650 const SMDS_MeshElement*& theTria2)
652 if ( !theNode1 || !theNode2 ) return false;
654 theTria1 = theTria2 = 0;
656 set< const SMDS_MeshElement* > emap;
657 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
659 const SMDS_MeshElement* elem = it->next();
660 if ( elem->NbNodes() == 3 )
663 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
665 const SMDS_MeshElement* elem = it->next();
666 if ( emap.find( elem ) != emap.end() ) {
668 // theTria1 must be element with minimum ID
669 if( theTria1->GetID() < elem->GetID() ) {
683 return ( theTria1 && theTria2 );
686 //=======================================================================
687 //function : InverseDiag
688 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
689 // with ones built on the same 4 nodes but having other common link.
690 // Return false if proper faces not found
691 //=======================================================================
693 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
694 const SMDS_MeshNode * theNode2)
696 myLastCreatedElems.Clear();
697 myLastCreatedNodes.Clear();
699 MESSAGE( "::InverseDiag()" );
701 const SMDS_MeshElement *tr1, *tr2;
702 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
705 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
706 if (!F1) return false;
707 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
708 if (!F2) return false;
709 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
710 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
712 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
713 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
717 // put nodes in array
718 // and find indices of 1,2 and of A in tr1 and of B in tr2
719 int i, iA1 = 0, i1 = 0;
720 const SMDS_MeshNode* aNodes1 [3];
721 SMDS_ElemIteratorPtr it;
722 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
723 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
724 if ( aNodes1[ i ] == theNode1 )
725 iA1 = i; // node A in tr1
726 else if ( aNodes1[ i ] != theNode2 )
730 const SMDS_MeshNode* aNodes2 [3];
731 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
732 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
733 if ( aNodes2[ i ] == theNode2 )
734 iB2 = i; // node B in tr2
735 else if ( aNodes2[ i ] != theNode1 )
739 // nodes 1 and 2 should not be the same
740 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
744 aNodes1[ iA1 ] = aNodes2[ i2 ];
746 aNodes2[ iB2 ] = aNodes1[ i1 ];
748 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
749 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
754 // check case of quadratic faces
755 return InverseDiag(tr1,tr2);
758 //=======================================================================
759 //function : getQuadrangleNodes
760 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
761 // fusion of triangles tr1 and tr2 having shared link on
762 // theNode1 and theNode2
763 //=======================================================================
765 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
766 const SMDS_MeshNode * theNode1,
767 const SMDS_MeshNode * theNode2,
768 const SMDS_MeshElement * tr1,
769 const SMDS_MeshElement * tr2 )
771 if( tr1->NbNodes() != tr2->NbNodes() )
773 // find the 4-th node to insert into tr1
774 const SMDS_MeshNode* n4 = 0;
775 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
777 while ( !n4 && i<3 ) {
778 const SMDS_MeshNode * n = cast2Node( it->next() );
780 bool isDiag = ( n == theNode1 || n == theNode2 );
784 // Make an array of nodes to be in a quadrangle
785 int iNode = 0, iFirstDiag = -1;
786 it = tr1->nodesIterator();
789 const SMDS_MeshNode * n = cast2Node( it->next() );
791 bool isDiag = ( n == theNode1 || n == theNode2 );
793 if ( iFirstDiag < 0 )
795 else if ( iNode - iFirstDiag == 1 )
796 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
798 else if ( n == n4 ) {
799 return false; // tr1 and tr2 should not have all the same nodes
801 theQuadNodes[ iNode++ ] = n;
803 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
804 theQuadNodes[ iNode ] = n4;
809 //=======================================================================
810 //function : DeleteDiag
811 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
812 // with a quadrangle built on the same 4 nodes.
813 // Return false if proper faces not found
814 //=======================================================================
816 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
817 const SMDS_MeshNode * theNode2)
819 myLastCreatedElems.Clear();
820 myLastCreatedNodes.Clear();
822 MESSAGE( "::DeleteDiag()" );
824 const SMDS_MeshElement *tr1, *tr2;
825 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
828 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
829 if (!F1) return false;
830 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
831 if (!F2) return false;
832 SMESHDS_Mesh * aMesh = GetMeshDS();
834 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
835 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
837 const SMDS_MeshNode* aNodes [ 4 ];
838 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
841 const SMDS_MeshElement* newElem = 0;
842 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
843 myLastCreatedElems.Append(newElem);
844 AddToSameGroups( newElem, tr1, aMesh );
845 int aShapeId = tr1->getshapeId();
848 aMesh->SetMeshElementOnShape( newElem, aShapeId );
850 aMesh->RemoveElement( tr1 );
851 aMesh->RemoveElement( tr2 );
856 // check case of quadratic faces
857 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
859 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
863 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
864 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
872 const SMDS_MeshNode* N1 [6];
873 const SMDS_MeshNode* N2 [6];
874 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
876 // now we receive following N1 and N2 (using numeration as above image)
877 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
878 // i.e. first nodes from both arrays determ new diagonal
880 const SMDS_MeshNode* aNodes[8];
890 const SMDS_MeshElement* newElem = 0;
891 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
892 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
893 myLastCreatedElems.Append(newElem);
894 AddToSameGroups( newElem, tr1, aMesh );
895 int aShapeId = tr1->getshapeId();
898 aMesh->SetMeshElementOnShape( newElem, aShapeId );
900 aMesh->RemoveElement( tr1 );
901 aMesh->RemoveElement( tr2 );
903 // remove middle node (9)
904 GetMeshDS()->RemoveNode( N1[4] );
909 //=======================================================================
910 //function : Reorient
911 //purpose : Reverse theElement orientation
912 //=======================================================================
914 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
917 myLastCreatedElems.Clear();
918 myLastCreatedNodes.Clear();
922 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
923 if ( !it || !it->more() )
926 switch ( theElem->GetType() ) {
930 if(!theElem->IsQuadratic()) {
931 int i = theElem->NbNodes();
932 vector<const SMDS_MeshNode*> aNodes( i );
934 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
935 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
938 // quadratic elements
939 if(theElem->GetType()==SMDSAbs_Edge) {
940 vector<const SMDS_MeshNode*> aNodes(3);
941 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
942 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
943 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
944 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
947 int nbn = theElem->NbNodes();
948 vector<const SMDS_MeshNode*> aNodes(nbn);
949 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
951 for(; i<nbn/2; i++) {
952 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
954 for(i=0; i<nbn/2; i++) {
955 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
957 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
961 case SMDSAbs_Volume: {
962 if (theElem->IsPoly()) {
963 // TODO reorient vtk polyhedron
964 MESSAGE("reorient vtk polyhedron ?");
965 const SMDS_VtkVolume* aPolyedre =
966 dynamic_cast<const SMDS_VtkVolume*>( theElem );
968 MESSAGE("Warning: bad volumic element");
972 int nbFaces = aPolyedre->NbFaces();
973 vector<const SMDS_MeshNode *> poly_nodes;
974 vector<int> quantities (nbFaces);
976 // reverse each face of the polyedre
977 for (int iface = 1; iface <= nbFaces; iface++) {
978 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
979 quantities[iface - 1] = nbFaceNodes;
981 for (inode = nbFaceNodes; inode >= 1; inode--) {
982 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
983 poly_nodes.push_back(curNode);
987 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
991 SMDS_VolumeTool vTool;
992 if ( !vTool.Set( theElem ))
995 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
996 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1005 //=======================================================================
1006 //function : getBadRate
1008 //=======================================================================
1010 static double getBadRate (const SMDS_MeshElement* theElem,
1011 SMESH::Controls::NumericalFunctorPtr& theCrit)
1013 SMESH::Controls::TSequenceOfXYZ P;
1014 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1016 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1017 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1020 //=======================================================================
1021 //function : QuadToTri
1022 //purpose : Cut quadrangles into triangles.
1023 // theCrit is used to select a diagonal to cut
1024 //=======================================================================
1026 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1027 SMESH::Controls::NumericalFunctorPtr theCrit)
1029 myLastCreatedElems.Clear();
1030 myLastCreatedNodes.Clear();
1032 MESSAGE( "::QuadToTri()" );
1034 if ( !theCrit.get() )
1037 SMESHDS_Mesh * aMesh = GetMeshDS();
1039 Handle(Geom_Surface) surface;
1040 SMESH_MesherHelper helper( *GetMesh() );
1042 TIDSortedElemSet::iterator itElem;
1043 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1044 const SMDS_MeshElement* elem = *itElem;
1045 if ( !elem || elem->GetType() != SMDSAbs_Face )
1047 if ( elem->NbNodes() != ( elem->IsQuadratic() ? 8 : 4 ))
1050 // retrieve element nodes
1051 const SMDS_MeshNode* aNodes [8];
1052 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1054 while ( itN->more() )
1055 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1057 // compare two sets of possible triangles
1058 double aBadRate1, aBadRate2; // to what extent a set is bad
1059 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1060 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1061 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1063 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1064 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1065 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1067 int aShapeId = FindShape( elem );
1068 const SMDS_MeshElement* newElem1 = 0;
1069 const SMDS_MeshElement* newElem2 = 0;
1071 if( !elem->IsQuadratic() ) {
1073 // split liner quadrangle
1074 if ( aBadRate1 <= aBadRate2 ) {
1075 // tr1 + tr2 is better
1076 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1077 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1080 // tr3 + tr4 is better
1081 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1082 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1087 // split quadratic quadrangle
1089 // get surface elem is on
1090 if ( aShapeId != helper.GetSubShapeID() ) {
1094 shape = aMesh->IndexToShape( aShapeId );
1095 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1096 TopoDS_Face face = TopoDS::Face( shape );
1097 surface = BRep_Tool::Surface( face );
1098 if ( !surface.IsNull() )
1099 helper.SetSubShape( shape );
1103 const SMDS_MeshNode* aNodes [8];
1104 const SMDS_MeshNode* inFaceNode = 0;
1105 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1107 while ( itN->more() ) {
1108 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1109 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
1110 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1112 inFaceNode = aNodes[ i-1 ];
1115 // find middle point for (0,1,2,3)
1116 // and create a node in this point;
1118 if ( surface.IsNull() ) {
1120 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1124 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1127 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1129 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1131 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1132 myLastCreatedNodes.Append(newN);
1134 // create a new element
1135 if ( aBadRate1 <= aBadRate2 ) {
1136 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1137 aNodes[6], aNodes[7], newN );
1138 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1139 newN, aNodes[4], aNodes[5] );
1142 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1143 aNodes[7], aNodes[4], newN );
1144 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1145 newN, aNodes[5], aNodes[6] );
1149 // care of a new element
1151 myLastCreatedElems.Append(newElem1);
1152 myLastCreatedElems.Append(newElem2);
1153 AddToSameGroups( newElem1, elem, aMesh );
1154 AddToSameGroups( newElem2, elem, aMesh );
1156 // put a new triangle on the same shape
1159 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1160 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1162 aMesh->RemoveElement( elem );
1167 //=======================================================================
1168 //function : BestSplit
1169 //purpose : Find better diagonal for cutting.
1170 //=======================================================================
1172 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1173 SMESH::Controls::NumericalFunctorPtr theCrit)
1175 myLastCreatedElems.Clear();
1176 myLastCreatedNodes.Clear();
1181 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1184 if( theQuad->NbNodes()==4 ||
1185 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1187 // retrieve element nodes
1188 const SMDS_MeshNode* aNodes [4];
1189 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1191 //while (itN->more())
1193 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1195 // compare two sets of possible triangles
1196 double aBadRate1, aBadRate2; // to what extent a set is bad
1197 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1198 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1199 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1201 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1202 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1203 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1205 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1206 return 1; // diagonal 1-3
1208 return 2; // diagonal 2-4
1215 // Methods of splitting volumes into tetra
1217 const int theHexTo5_1[5*4+1] =
1219 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1221 const int theHexTo5_2[5*4+1] =
1223 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1225 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1227 const int theHexTo6_1[6*4+1] =
1229 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
1231 const int theHexTo6_2[6*4+1] =
1233 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
1235 const int theHexTo6_3[6*4+1] =
1237 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
1239 const int theHexTo6_4[6*4+1] =
1241 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
1243 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1245 const int thePyraTo2_1[2*4+1] =
1247 0, 1, 2, 4, 0, 2, 3, 4, -1
1249 const int thePyraTo2_2[2*4+1] =
1251 1, 2, 3, 4, 1, 3, 0, 4, -1
1253 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1255 const int thePentaTo3_1[3*4+1] =
1257 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1259 const int thePentaTo3_2[3*4+1] =
1261 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1263 const int thePentaTo3_3[3*4+1] =
1265 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1267 const int thePentaTo3_4[3*4+1] =
1269 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1271 const int thePentaTo3_5[3*4+1] =
1273 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1275 const int thePentaTo3_6[3*4+1] =
1277 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1279 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1280 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1282 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1285 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1286 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1287 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1292 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1293 bool _baryNode; //!< additional node is to be created at cell barycenter
1294 bool _ownConn; //!< to delete _connectivity in destructor
1295 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1297 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1298 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1299 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1300 bool hasFacet( const TTriangleFacet& facet ) const
1302 const int* tetConn = _connectivity;
1303 for ( ; tetConn[0] >= 0; tetConn += 4 )
1304 if (( facet.contains( tetConn[0] ) +
1305 facet.contains( tetConn[1] ) +
1306 facet.contains( tetConn[2] ) +
1307 facet.contains( tetConn[3] )) == 3 )
1313 //=======================================================================
1315 * \brief return TSplitMethod for the given element
1317 //=======================================================================
1319 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1321 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1323 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1324 // an edge and a face barycenter; tertaherdons are based on triangles and
1325 // a volume barycenter
1326 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1328 // Find out how adjacent volumes are split
1330 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1331 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1332 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1334 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1335 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1336 if ( nbNodes < 4 ) continue;
1338 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1339 const int* nInd = vol.GetFaceNodesIndices( iF );
1342 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1343 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1344 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1345 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1349 int iCom = 0; // common node of triangle faces to split into
1350 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1352 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1353 nInd[ iQ * ( (iCom+1)%nbNodes )],
1354 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1355 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1356 nInd[ iQ * ( (iCom+2)%nbNodes )],
1357 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1358 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1360 triaSplits.push_back( t012 );
1361 triaSplits.push_back( t023 );
1366 if ( !triaSplits.empty() )
1367 hasAdjacentSplits = true;
1370 // Among variants of split method select one compliant with adjacent volumes
1372 TSplitMethod method;
1373 if ( !vol.Element()->IsPoly() && !is24TetMode )
1375 int nbVariants = 2, nbTet = 0;
1376 const int** connVariants = 0;
1377 switch ( vol.Element()->GetEntityType() )
1379 case SMDSEntity_Hexa:
1380 case SMDSEntity_Quad_Hexa:
1381 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1382 connVariants = theHexTo5, nbTet = 5;
1384 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1386 case SMDSEntity_Pyramid:
1387 case SMDSEntity_Quad_Pyramid:
1388 connVariants = thePyraTo2; nbTet = 2;
1390 case SMDSEntity_Penta:
1391 case SMDSEntity_Quad_Penta:
1392 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1397 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1399 // check method compliancy with adjacent tetras,
1400 // all found splits must be among facets of tetras described by this method
1401 method = TSplitMethod( nbTet, connVariants[variant] );
1402 if ( hasAdjacentSplits && method._nbTetra > 0 )
1404 bool facetCreated = true;
1405 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1407 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1408 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1409 facetCreated = method.hasFacet( *facet );
1411 if ( !facetCreated )
1412 method = TSplitMethod(0); // incompatible method
1416 if ( method._nbTetra < 1 )
1418 // No standard method is applicable, use a generic solution:
1419 // each facet of a volume is split into triangles and
1420 // each of triangles and a volume barycenter form a tetrahedron.
1422 int* connectivity = new int[ maxTetConnSize + 1 ];
1423 method._connectivity = connectivity;
1424 method._ownConn = true;
1425 method._baryNode = true;
1428 int baryCenInd = vol.NbNodes();
1429 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1431 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1432 const int* nInd = vol.GetFaceNodesIndices( iF );
1433 // find common node of triangle facets of tetra to create
1434 int iCommon = 0; // index in linear numeration
1435 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1436 if ( !triaSplits.empty() )
1439 const TTriangleFacet* facet = &triaSplits.front();
1440 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1441 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1442 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1445 else if ( nbNodes > 3 && !is24TetMode )
1447 // find the best method of splitting into triangles by aspect ratio
1448 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1449 map< double, int > badness2iCommon;
1450 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1451 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1452 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1453 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1455 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1456 nodes[ iQ*((iLast-1)%nbNodes)],
1457 nodes[ iQ*((iLast )%nbNodes)]);
1458 double badness = getBadRate( &tria, aspectRatio );
1459 badness2iCommon.insert( make_pair( badness, iCommon ));
1461 // use iCommon with lowest badness
1462 iCommon = badness2iCommon.begin()->second;
1464 if ( iCommon >= nbNodes )
1465 iCommon = 0; // something wrong
1467 // fill connectivity of tetrahedra based on a current face
1468 int nbTet = nbNodes - 2;
1469 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1471 method._faceBaryNode.insert( make_pair( iF, (const SMDS_MeshNode*)0 ));
1472 int faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1474 for ( int i = 0; i < nbTet; ++i )
1476 int i1 = i, i2 = (i+1) % nbNodes;
1477 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1478 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1479 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1480 connectivity[ connSize++ ] = faceBaryCenInd;
1481 connectivity[ connSize++ ] = baryCenInd;
1486 for ( int i = 0; i < nbTet; ++i )
1488 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1489 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1490 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1491 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1492 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1493 connectivity[ connSize++ ] = baryCenInd;
1496 method._nbTetra += nbTet;
1498 connectivity[ connSize++ ] = -1;
1502 //================================================================================
1504 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1506 //================================================================================
1508 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1510 // find the tetrahedron including the three nodes of facet
1511 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1512 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1513 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1514 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1515 while ( volIt1->more() )
1517 const SMDS_MeshElement* v = volIt1->next();
1518 if ( v->GetEntityType() != ( v->IsQuadratic() ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra ))
1520 SMDS_ElemIteratorPtr volIt2 = n2->GetInverseElementIterator(SMDSAbs_Volume);
1521 while ( volIt2->more() )
1522 if ( v != volIt2->next() )
1524 SMDS_ElemIteratorPtr volIt3 = n3->GetInverseElementIterator(SMDSAbs_Volume);
1525 while ( volIt3->more() )
1526 if ( v == volIt3->next() )
1532 //=======================================================================
1534 * \brief A key of a face of volume
1536 //=======================================================================
1538 struct TVolumeFaceKey: pair< int, pair< int, int> >
1540 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1542 TIDSortedNodeSet sortedNodes;
1543 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1544 int nbNodes = vol.NbFaceNodes( iF );
1545 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1546 for ( int i = 0; i < nbNodes; i += iQ )
1547 sortedNodes.insert( fNodes[i] );
1548 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1549 first = (*(n++))->GetID();
1550 second.first = (*(n++))->GetID();
1551 second.second = (*(n++))->GetID();
1556 //=======================================================================
1557 //function : SplitVolumesIntoTetra
1558 //purpose : Split volumic elements into tetrahedra.
1559 //=======================================================================
1561 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1562 const int theMethodFlags)
1564 // std-like iterator on coordinates of nodes of mesh element
1565 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1566 NXyzIterator xyzEnd;
1568 SMDS_VolumeTool volTool;
1569 SMESH_MesherHelper helper( *GetMesh());
1571 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1572 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1574 SMESH_SequenceOfElemPtr newNodes, newElems;
1576 // map face of volume to it's baricenrtic node
1577 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1580 TIDSortedElemSet::const_iterator elem = theElems.begin();
1581 for ( ; elem != theElems.end(); ++elem )
1583 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1584 if ( geomType <= SMDSEntity_Quad_Tetra )
1585 continue; // tetra or face or ...
1587 if ( !volTool.Set( *elem )) continue; // not volume? strange...
1589 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1590 if ( splitMethod._nbTetra < 1 ) continue;
1592 // find submesh to add new tetras to
1593 if ( !subMesh || !subMesh->Contains( *elem ))
1595 int shapeID = FindShape( *elem );
1596 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1597 subMesh = GetMeshDS()->MeshElements( shapeID );
1600 if ( (*elem)->IsQuadratic() )
1603 // add quadratic links to the helper
1604 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1606 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1607 for ( int iN = 0; iN < volTool.NbFaceNodes( iF ); iN += iQ )
1608 helper.AddTLinkNode( fNodes[iF], fNodes[iF+2], fNodes[iF+1] );
1610 helper.SetIsQuadratic( true );
1615 helper.SetIsQuadratic( false );
1617 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1618 helper.SetElementsOnShape( true );
1619 if ( splitMethod._baryNode )
1621 // make a node at barycenter
1622 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1623 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1624 nodes.push_back( gcNode );
1625 newNodes.Append( gcNode );
1627 if ( !splitMethod._faceBaryNode.empty() )
1629 // make or find baricentric nodes of faces
1630 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1631 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1633 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1634 volFace2BaryNode.insert
1635 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), (const SMDS_MeshNode*)0) ).first;
1638 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1639 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1641 nodes.push_back( iF_n->second = f_n->second );
1646 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1647 const int* tetConn = splitMethod._connectivity;
1648 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1649 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1650 nodes[ tetConn[1] ],
1651 nodes[ tetConn[2] ],
1652 nodes[ tetConn[3] ]));
1654 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1656 // Split faces on sides of the split volume
1658 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1659 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1661 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1662 if ( nbNodes < 4 ) continue;
1664 // find an existing face
1665 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1666 volTool.GetFaceNodes( iF ) + nbNodes*iQ );
1667 while ( const SMDS_MeshElement* face = GetMeshDS()->FindFace( fNodes ))
1670 helper.SetElementsOnShape( false );
1671 vector< const SMDS_MeshElement* > triangles;
1673 // find submesh to add new triangles in
1674 if ( !fSubMesh || !fSubMesh->Contains( face ))
1676 int shapeID = FindShape( face );
1677 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1679 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1680 if ( iF_n != splitMethod._faceBaryNode.end() )
1682 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1684 const SMDS_MeshNode* n1 = fNodes[iN];
1685 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%nbNodes*iQ];
1686 const SMDS_MeshNode *n3 = iF_n->second;
1687 if ( !volTool.IsFaceExternal( iF ))
1689 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1691 if ( fSubMesh && n3->getshapeId() < 1 )
1692 fSubMesh->AddNode( n3 );
1697 // among possible triangles create ones discribed by split method
1698 const int* nInd = volTool.GetFaceNodesIndices( iF );
1699 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1700 int iCom = 0; // common node of triangle faces to split into
1701 list< TTriangleFacet > facets;
1702 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1704 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1705 nInd[ iQ * ( (iCom+1)%nbNodes )],
1706 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1707 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1708 nInd[ iQ * ( (iCom+2)%nbNodes )],
1709 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1710 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1712 facets.push_back( t012 );
1713 facets.push_back( t023 );
1714 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1715 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1716 nInd[ iQ * ((iLast-1)%nbNodes )],
1717 nInd[ iQ * ((iLast )%nbNodes )]));
1721 list< TTriangleFacet >::iterator facet = facets.begin();
1722 for ( ; facet != facets.end(); ++facet )
1724 if ( !volTool.IsFaceExternal( iF ))
1725 swap( facet->_n2, facet->_n3 );
1726 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1727 volNodes[ facet->_n2 ],
1728 volNodes[ facet->_n3 ]));
1731 for ( int i = 0; i < triangles.size(); ++i )
1733 if ( !triangles[i] ) continue;
1735 fSubMesh->AddElement( triangles[i]);
1736 newElems.Append( triangles[i] );
1738 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1739 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1742 } // loop on volume faces to split them into triangles
1744 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1746 } // loop on volumes to split
1748 myLastCreatedNodes = newNodes;
1749 myLastCreatedElems = newElems;
1752 //=======================================================================
1753 //function : AddToSameGroups
1754 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1755 //=======================================================================
1757 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1758 const SMDS_MeshElement* elemInGroups,
1759 SMESHDS_Mesh * aMesh)
1761 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1762 if (!groups.empty()) {
1763 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1764 for ( ; grIt != groups.end(); grIt++ ) {
1765 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1766 if ( group && group->Contains( elemInGroups ))
1767 group->SMDSGroup().Add( elemToAdd );
1773 //=======================================================================
1774 //function : RemoveElemFromGroups
1775 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1776 //=======================================================================
1777 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1778 SMESHDS_Mesh * aMesh)
1780 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1781 if (!groups.empty())
1783 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1784 for (; GrIt != groups.end(); GrIt++)
1786 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1787 if (!grp || grp->IsEmpty()) continue;
1788 grp->SMDSGroup().Remove(removeelem);
1793 //================================================================================
1795 * \brief Replace elemToRm by elemToAdd in the all groups
1797 //================================================================================
1799 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1800 const SMDS_MeshElement* elemToAdd,
1801 SMESHDS_Mesh * aMesh)
1803 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1804 if (!groups.empty()) {
1805 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1806 for ( ; grIt != groups.end(); grIt++ ) {
1807 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1808 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1809 group->SMDSGroup().Add( elemToAdd );
1814 //================================================================================
1816 * \brief Replace elemToRm by elemToAdd in the all groups
1818 //================================================================================
1820 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1821 const vector<const SMDS_MeshElement*>& elemToAdd,
1822 SMESHDS_Mesh * aMesh)
1824 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1825 if (!groups.empty())
1827 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1828 for ( ; grIt != groups.end(); grIt++ ) {
1829 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1830 if ( group && group->SMDSGroup().Remove( elemToRm ) )
1831 for ( int i = 0; i < elemToAdd.size(); ++i )
1832 group->SMDSGroup().Add( elemToAdd[ i ] );
1837 //=======================================================================
1838 //function : QuadToTri
1839 //purpose : Cut quadrangles into triangles.
1840 // theCrit is used to select a diagonal to cut
1841 //=======================================================================
1843 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1844 const bool the13Diag)
1846 myLastCreatedElems.Clear();
1847 myLastCreatedNodes.Clear();
1849 MESSAGE( "::QuadToTri()" );
1851 SMESHDS_Mesh * aMesh = GetMeshDS();
1853 Handle(Geom_Surface) surface;
1854 SMESH_MesherHelper helper( *GetMesh() );
1856 TIDSortedElemSet::iterator itElem;
1857 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1858 const SMDS_MeshElement* elem = *itElem;
1859 if ( !elem || elem->GetType() != SMDSAbs_Face )
1861 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1862 if(!isquad) continue;
1864 if(elem->NbNodes()==4) {
1865 // retrieve element nodes
1866 const SMDS_MeshNode* aNodes [4];
1867 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1869 while ( itN->more() )
1870 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1872 int aShapeId = FindShape( elem );
1873 const SMDS_MeshElement* newElem1 = 0;
1874 const SMDS_MeshElement* newElem2 = 0;
1876 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1877 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1880 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1881 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1883 myLastCreatedElems.Append(newElem1);
1884 myLastCreatedElems.Append(newElem2);
1885 // put a new triangle on the same shape and add to the same groups
1888 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1889 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1891 AddToSameGroups( newElem1, elem, aMesh );
1892 AddToSameGroups( newElem2, elem, aMesh );
1893 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
1894 aMesh->RemoveElement( elem );
1897 // Quadratic quadrangle
1899 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1901 // get surface elem is on
1902 int aShapeId = FindShape( elem );
1903 if ( aShapeId != helper.GetSubShapeID() ) {
1907 shape = aMesh->IndexToShape( aShapeId );
1908 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1909 TopoDS_Face face = TopoDS::Face( shape );
1910 surface = BRep_Tool::Surface( face );
1911 if ( !surface.IsNull() )
1912 helper.SetSubShape( shape );
1916 const SMDS_MeshNode* aNodes [8];
1917 const SMDS_MeshNode* inFaceNode = 0;
1918 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1920 while ( itN->more() ) {
1921 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1922 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
1923 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1925 inFaceNode = aNodes[ i-1 ];
1929 // find middle point for (0,1,2,3)
1930 // and create a node in this point;
1932 if ( surface.IsNull() ) {
1934 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1938 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
1941 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
1943 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1945 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1946 myLastCreatedNodes.Append(newN);
1948 // create a new element
1949 const SMDS_MeshElement* newElem1 = 0;
1950 const SMDS_MeshElement* newElem2 = 0;
1952 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1953 aNodes[6], aNodes[7], newN );
1954 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1955 newN, aNodes[4], aNodes[5] );
1958 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1959 aNodes[7], aNodes[4], newN );
1960 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1961 newN, aNodes[5], aNodes[6] );
1963 myLastCreatedElems.Append(newElem1);
1964 myLastCreatedElems.Append(newElem2);
1965 // put a new triangle on the same shape and add to the same groups
1968 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1969 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1971 AddToSameGroups( newElem1, elem, aMesh );
1972 AddToSameGroups( newElem2, elem, aMesh );
1973 aMesh->RemoveElement( elem );
1980 //=======================================================================
1981 //function : getAngle
1983 //=======================================================================
1985 double getAngle(const SMDS_MeshElement * tr1,
1986 const SMDS_MeshElement * tr2,
1987 const SMDS_MeshNode * n1,
1988 const SMDS_MeshNode * n2)
1990 double angle = 2*PI; // bad angle
1993 SMESH::Controls::TSequenceOfXYZ P1, P2;
1994 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
1995 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
1998 if(!tr1->IsQuadratic())
1999 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2001 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2002 if ( N1.SquareMagnitude() <= gp::Resolution() )
2004 if(!tr2->IsQuadratic())
2005 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2007 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2008 if ( N2.SquareMagnitude() <= gp::Resolution() )
2011 // find the first diagonal node n1 in the triangles:
2012 // take in account a diagonal link orientation
2013 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2014 for ( int t = 0; t < 2; t++ ) {
2015 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2016 int i = 0, iDiag = -1;
2017 while ( it->more()) {
2018 const SMDS_MeshElement *n = it->next();
2019 if ( n == n1 || n == n2 ) {
2023 if ( i - iDiag == 1 )
2024 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2033 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2036 angle = N1.Angle( N2 );
2041 // =================================================
2042 // class generating a unique ID for a pair of nodes
2043 // and able to return nodes by that ID
2044 // =================================================
2048 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2049 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2052 long GetLinkID (const SMDS_MeshNode * n1,
2053 const SMDS_MeshNode * n2) const
2055 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2058 bool GetNodes (const long theLinkID,
2059 const SMDS_MeshNode* & theNode1,
2060 const SMDS_MeshNode* & theNode2) const
2062 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2063 if ( !theNode1 ) return false;
2064 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2065 if ( !theNode2 ) return false;
2071 const SMESHDS_Mesh* myMesh;
2076 //=======================================================================
2077 //function : TriToQuad
2078 //purpose : Fuse neighbour triangles into quadrangles.
2079 // theCrit is used to select a neighbour to fuse with.
2080 // theMaxAngle is a max angle between element normals at which
2081 // fusion is still performed.
2082 //=======================================================================
2084 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2085 SMESH::Controls::NumericalFunctorPtr theCrit,
2086 const double theMaxAngle)
2088 myLastCreatedElems.Clear();
2089 myLastCreatedNodes.Clear();
2091 MESSAGE( "::TriToQuad()" );
2093 if ( !theCrit.get() )
2096 SMESHDS_Mesh * aMesh = GetMeshDS();
2098 // Prepare data for algo: build
2099 // 1. map of elements with their linkIDs
2100 // 2. map of linkIDs with their elements
2102 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2103 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2104 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2105 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2107 TIDSortedElemSet::iterator itElem;
2108 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2109 const SMDS_MeshElement* elem = *itElem;
2110 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2111 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2112 if(!IsTria) continue;
2114 // retrieve element nodes
2115 const SMDS_MeshNode* aNodes [4];
2116 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2119 aNodes[ i++ ] = cast2Node( itN->next() );
2120 aNodes[ 3 ] = aNodes[ 0 ];
2123 for ( i = 0; i < 3; i++ ) {
2124 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2125 // check if elements sharing a link can be fused
2126 itLE = mapLi_listEl.find( link );
2127 if ( itLE != mapLi_listEl.end() ) {
2128 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2130 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2131 //if ( FindShape( elem ) != FindShape( elem2 ))
2132 // continue; // do not fuse triangles laying on different shapes
2133 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2134 continue; // avoid making badly shaped quads
2135 (*itLE).second.push_back( elem );
2138 mapLi_listEl[ link ].push_back( elem );
2140 mapEl_setLi [ elem ].insert( link );
2143 // Clean the maps from the links shared by a sole element, ie
2144 // links to which only one element is bound in mapLi_listEl
2146 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2147 int nbElems = (*itLE).second.size();
2148 if ( nbElems < 2 ) {
2149 const SMDS_MeshElement* elem = (*itLE).second.front();
2150 SMESH_TLink link = (*itLE).first;
2151 mapEl_setLi[ elem ].erase( link );
2152 if ( mapEl_setLi[ elem ].empty() )
2153 mapEl_setLi.erase( elem );
2157 // Algo: fuse triangles into quadrangles
2159 while ( ! mapEl_setLi.empty() ) {
2160 // Look for the start element:
2161 // the element having the least nb of shared links
2162 const SMDS_MeshElement* startElem = 0;
2164 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2165 int nbLinks = (*itEL).second.size();
2166 if ( nbLinks < minNbLinks ) {
2167 startElem = (*itEL).first;
2168 minNbLinks = nbLinks;
2169 if ( minNbLinks == 1 )
2174 // search elements to fuse starting from startElem or links of elements
2175 // fused earlyer - startLinks
2176 list< SMESH_TLink > startLinks;
2177 while ( startElem || !startLinks.empty() ) {
2178 while ( !startElem && !startLinks.empty() ) {
2179 // Get an element to start, by a link
2180 SMESH_TLink linkId = startLinks.front();
2181 startLinks.pop_front();
2182 itLE = mapLi_listEl.find( linkId );
2183 if ( itLE != mapLi_listEl.end() ) {
2184 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2185 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2186 for ( ; itE != listElem.end() ; itE++ )
2187 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2189 mapLi_listEl.erase( itLE );
2194 // Get candidates to be fused
2195 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2196 const SMESH_TLink *link12, *link13;
2198 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2199 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2200 ASSERT( !setLi.empty() );
2201 set< SMESH_TLink >::iterator itLi;
2202 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2204 const SMESH_TLink & link = (*itLi);
2205 itLE = mapLi_listEl.find( link );
2206 if ( itLE == mapLi_listEl.end() )
2209 const SMDS_MeshElement* elem = (*itLE).second.front();
2211 elem = (*itLE).second.back();
2212 mapLi_listEl.erase( itLE );
2213 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2224 // add other links of elem to list of links to re-start from
2225 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2226 set< SMESH_TLink >::iterator it;
2227 for ( it = links.begin(); it != links.end(); it++ ) {
2228 const SMESH_TLink& link2 = (*it);
2229 if ( link2 != link )
2230 startLinks.push_back( link2 );
2234 // Get nodes of possible quadrangles
2235 const SMDS_MeshNode *n12 [4], *n13 [4];
2236 bool Ok12 = false, Ok13 = false;
2237 const SMDS_MeshNode *linkNode1, *linkNode2;
2239 linkNode1 = link12->first;
2240 linkNode2 = link12->second;
2241 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2245 linkNode1 = link13->first;
2246 linkNode2 = link13->second;
2247 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2251 // Choose a pair to fuse
2252 if ( Ok12 && Ok13 ) {
2253 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2254 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2255 double aBadRate12 = getBadRate( &quad12, theCrit );
2256 double aBadRate13 = getBadRate( &quad13, theCrit );
2257 if ( aBadRate13 < aBadRate12 )
2264 // and remove fused elems and removed links from the maps
2265 mapEl_setLi.erase( tr1 );
2267 mapEl_setLi.erase( tr2 );
2268 mapLi_listEl.erase( *link12 );
2269 if(tr1->NbNodes()==3) {
2270 const SMDS_MeshElement* newElem = 0;
2271 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2272 myLastCreatedElems.Append(newElem);
2273 AddToSameGroups( newElem, tr1, aMesh );
2274 int aShapeId = tr1->getshapeId();
2277 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2279 aMesh->RemoveElement( tr1 );
2280 aMesh->RemoveElement( tr2 );
2283 const SMDS_MeshNode* N1 [6];
2284 const SMDS_MeshNode* N2 [6];
2285 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2286 // now we receive following N1 and N2 (using numeration as above image)
2287 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2288 // i.e. first nodes from both arrays determ new diagonal
2289 const SMDS_MeshNode* aNodes[8];
2298 const SMDS_MeshElement* newElem = 0;
2299 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2300 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2301 myLastCreatedElems.Append(newElem);
2302 AddToSameGroups( newElem, tr1, aMesh );
2303 int aShapeId = tr1->getshapeId();
2306 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2308 aMesh->RemoveElement( tr1 );
2309 aMesh->RemoveElement( tr2 );
2310 // remove middle node (9)
2311 GetMeshDS()->RemoveNode( N1[4] );
2315 mapEl_setLi.erase( tr3 );
2316 mapLi_listEl.erase( *link13 );
2317 if(tr1->NbNodes()==3) {
2318 const SMDS_MeshElement* newElem = 0;
2319 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2320 myLastCreatedElems.Append(newElem);
2321 AddToSameGroups( newElem, tr1, aMesh );
2322 int aShapeId = tr1->getshapeId();
2325 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2327 aMesh->RemoveElement( tr1 );
2328 aMesh->RemoveElement( tr3 );
2331 const SMDS_MeshNode* N1 [6];
2332 const SMDS_MeshNode* N2 [6];
2333 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2334 // now we receive following N1 and N2 (using numeration as above image)
2335 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2336 // i.e. first nodes from both arrays determ new diagonal
2337 const SMDS_MeshNode* aNodes[8];
2346 const SMDS_MeshElement* newElem = 0;
2347 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2348 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2349 myLastCreatedElems.Append(newElem);
2350 AddToSameGroups( newElem, tr1, aMesh );
2351 int aShapeId = tr1->getshapeId();
2354 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2356 aMesh->RemoveElement( tr1 );
2357 aMesh->RemoveElement( tr3 );
2358 // remove middle node (9)
2359 GetMeshDS()->RemoveNode( N1[4] );
2363 // Next element to fuse: the rejected one
2365 startElem = Ok12 ? tr3 : tr2;
2367 } // if ( startElem )
2368 } // while ( startElem || !startLinks.empty() )
2369 } // while ( ! mapEl_setLi.empty() )
2375 /*#define DUMPSO(txt) \
2376 // cout << txt << endl;
2377 //=============================================================================
2381 //=============================================================================
2382 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2386 int tmp = idNodes[ i1 ];
2387 idNodes[ i1 ] = idNodes[ i2 ];
2388 idNodes[ i2 ] = tmp;
2389 gp_Pnt Ptmp = P[ i1 ];
2392 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2395 //=======================================================================
2396 //function : SortQuadNodes
2397 //purpose : Set 4 nodes of a quadrangle face in a good order.
2398 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2400 //=======================================================================
2402 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2407 for ( i = 0; i < 4; i++ ) {
2408 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2410 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2413 gp_Vec V1(P[0], P[1]);
2414 gp_Vec V2(P[0], P[2]);
2415 gp_Vec V3(P[0], P[3]);
2417 gp_Vec Cross1 = V1 ^ V2;
2418 gp_Vec Cross2 = V2 ^ V3;
2421 if (Cross1.Dot(Cross2) < 0)
2426 if (Cross1.Dot(Cross2) < 0)
2430 swap ( i, i + 1, idNodes, P );
2432 // for ( int ii = 0; ii < 4; ii++ ) {
2433 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2434 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2440 //=======================================================================
2441 //function : SortHexaNodes
2442 //purpose : Set 8 nodes of a hexahedron in a good order.
2443 // Return success status
2444 //=======================================================================
2446 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2451 DUMPSO( "INPUT: ========================================");
2452 for ( i = 0; i < 8; i++ ) {
2453 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2454 if ( !n ) return false;
2455 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2456 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2458 DUMPSO( "========================================");
2461 set<int> faceNodes; // ids of bottom face nodes, to be found
2462 set<int> checkedId1; // ids of tried 2-nd nodes
2463 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2464 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2465 int iMin, iLoop1 = 0;
2467 // Loop to try the 2-nd nodes
2469 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2471 // Find not checked 2-nd node
2472 for ( i = 1; i < 8; i++ )
2473 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2474 int id1 = idNodes[i];
2475 swap ( 1, i, idNodes, P );
2476 checkedId1.insert ( id1 );
2480 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2481 // ie that all but meybe one (id3 which is on the same face) nodes
2482 // lay on the same side from the triangle plane.
2484 bool manyInPlane = false; // more than 4 nodes lay in plane
2486 while ( ++iLoop2 < 6 ) {
2488 // get 1-2-3 plane coeffs
2489 Standard_Real A, B, C, D;
2490 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2491 if ( N.SquareMagnitude() > gp::Resolution() )
2493 gp_Pln pln ( P[0], N );
2494 pln.Coefficients( A, B, C, D );
2496 // find the node (iMin) closest to pln
2497 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2499 for ( i = 3; i < 8; i++ ) {
2500 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2501 if ( fabs( dist[i] ) < minDist ) {
2502 minDist = fabs( dist[i] );
2505 if ( fabs( dist[i] ) <= tol )
2506 idInPln.insert( idNodes[i] );
2509 // there should not be more than 4 nodes in bottom plane
2510 if ( idInPln.size() > 1 )
2512 DUMPSO( "### idInPln.size() = " << idInPln.size());
2513 // idInPlane does not contain the first 3 nodes
2514 if ( manyInPlane || idInPln.size() == 5)
2515 return false; // all nodes in one plane
2518 // set the 1-st node to be not in plane
2519 for ( i = 3; i < 8; i++ ) {
2520 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2521 DUMPSO( "### Reset 0-th node");
2522 swap( 0, i, idNodes, P );
2527 // reset to re-check second nodes
2528 leastDist = DBL_MAX;
2532 break; // from iLoop2;
2535 // check that the other 4 nodes are on the same side
2536 bool sameSide = true;
2537 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2538 for ( i = 3; sameSide && i < 8; i++ ) {
2540 sameSide = ( isNeg == dist[i] <= 0.);
2543 // keep best solution
2544 if ( sameSide && minDist < leastDist ) {
2545 leastDist = minDist;
2547 faceNodes.insert( idNodes[ 1 ] );
2548 faceNodes.insert( idNodes[ 2 ] );
2549 faceNodes.insert( idNodes[ iMin ] );
2550 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2551 << " leastDist = " << leastDist);
2552 if ( leastDist <= DBL_MIN )
2557 // set next 3-d node to check
2558 int iNext = 2 + iLoop2;
2560 DUMPSO( "Try 2-nd");
2561 swap ( 2, iNext, idNodes, P );
2563 } // while ( iLoop2 < 6 )
2566 if ( faceNodes.empty() ) return false;
2568 // Put the faceNodes in proper places
2569 for ( i = 4; i < 8; i++ ) {
2570 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2571 // find a place to put
2573 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2575 DUMPSO( "Set faceNodes");
2576 swap ( iTo, i, idNodes, P );
2581 // Set nodes of the found bottom face in good order
2582 DUMPSO( " Found bottom face: ");
2583 i = SortQuadNodes( theMesh, idNodes );
2585 gp_Pnt Ptmp = P[ i ];
2590 // for ( int ii = 0; ii < 4; ii++ ) {
2591 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2592 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2595 // Gravity center of the top and bottom faces
2596 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2597 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2599 // Get direction from the bottom to the top face
2600 gp_Vec upDir ( aGCb, aGCt );
2601 Standard_Real upDirSize = upDir.Magnitude();
2602 if ( upDirSize <= gp::Resolution() ) return false;
2605 // Assure that the bottom face normal points up
2606 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2607 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2608 if ( Nb.Dot( upDir ) < 0 ) {
2609 DUMPSO( "Reverse bottom face");
2610 swap( 1, 3, idNodes, P );
2613 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2614 Standard_Real minDist = DBL_MAX;
2615 for ( i = 4; i < 8; i++ ) {
2616 // projection of P[i] to the plane defined by P[0] and upDir
2617 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2618 Standard_Real sqDist = P[0].SquareDistance( Pp );
2619 if ( sqDist < minDist ) {
2624 DUMPSO( "Set 4-th");
2625 swap ( 4, iMin, idNodes, P );
2627 // Set nodes of the top face in good order
2628 DUMPSO( "Sort top face");
2629 i = SortQuadNodes( theMesh, &idNodes[4] );
2632 gp_Pnt Ptmp = P[ i ];
2637 // Assure that direction of the top face normal is from the bottom face
2638 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2639 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2640 if ( Nt.Dot( upDir ) < 0 ) {
2641 DUMPSO( "Reverse top face");
2642 swap( 5, 7, idNodes, P );
2645 // DUMPSO( "OUTPUT: ========================================");
2646 // for ( i = 0; i < 8; i++ ) {
2647 // float *p = ugrid->GetPoint(idNodes[i]);
2648 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2654 //================================================================================
2656 * \brief Return nodes linked to the given one
2657 * \param theNode - the node
2658 * \param linkedNodes - the found nodes
2659 * \param type - the type of elements to check
2661 * Medium nodes are ignored
2663 //================================================================================
2665 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2666 TIDSortedElemSet & linkedNodes,
2667 SMDSAbs_ElementType type )
2669 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2670 while ( elemIt->more() )
2672 const SMDS_MeshElement* elem = elemIt->next();
2673 if(elem->GetType() == SMDSAbs_0DElement)
2676 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2677 if ( elem->GetType() == SMDSAbs_Volume )
2679 SMDS_VolumeTool vol( elem );
2680 while ( nodeIt->more() ) {
2681 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2682 if ( theNode != n && vol.IsLinked( theNode, n ))
2683 linkedNodes.insert( n );
2688 for ( int i = 0; nodeIt->more(); ++i ) {
2689 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2690 if ( n == theNode ) {
2691 int iBefore = i - 1;
2693 if ( elem->IsQuadratic() ) {
2694 int nb = elem->NbNodes() / 2;
2695 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2696 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2698 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2699 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2706 //=======================================================================
2707 //function : laplacianSmooth
2708 //purpose : pulls theNode toward the center of surrounding nodes directly
2709 // connected to that node along an element edge
2710 //=======================================================================
2712 void laplacianSmooth(const SMDS_MeshNode* theNode,
2713 const Handle(Geom_Surface)& theSurface,
2714 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2716 // find surrounding nodes
2718 TIDSortedElemSet nodeSet;
2719 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2721 // compute new coodrs
2723 double coord[] = { 0., 0., 0. };
2724 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2725 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2726 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2727 if ( theSurface.IsNull() ) { // smooth in 3D
2728 coord[0] += node->X();
2729 coord[1] += node->Y();
2730 coord[2] += node->Z();
2732 else { // smooth in 2D
2733 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2734 gp_XY* uv = theUVMap[ node ];
2735 coord[0] += uv->X();
2736 coord[1] += uv->Y();
2739 int nbNodes = nodeSet.size();
2742 coord[0] /= nbNodes;
2743 coord[1] /= nbNodes;
2745 if ( !theSurface.IsNull() ) {
2746 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2747 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2748 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2754 coord[2] /= nbNodes;
2758 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2761 //=======================================================================
2762 //function : centroidalSmooth
2763 //purpose : pulls theNode toward the element-area-weighted centroid of the
2764 // surrounding elements
2765 //=======================================================================
2767 void centroidalSmooth(const SMDS_MeshNode* theNode,
2768 const Handle(Geom_Surface)& theSurface,
2769 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2771 gp_XYZ aNewXYZ(0.,0.,0.);
2772 SMESH::Controls::Area anAreaFunc;
2773 double totalArea = 0.;
2778 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2779 while ( elemIt->more() )
2781 const SMDS_MeshElement* elem = elemIt->next();
2784 gp_XYZ elemCenter(0.,0.,0.);
2785 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2786 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2787 int nn = elem->NbNodes();
2788 if(elem->IsQuadratic()) nn = nn/2;
2790 //while ( itN->more() ) {
2792 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2794 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2795 aNodePoints.push_back( aP );
2796 if ( !theSurface.IsNull() ) { // smooth in 2D
2797 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2798 gp_XY* uv = theUVMap[ aNode ];
2799 aP.SetCoord( uv->X(), uv->Y(), 0. );
2803 double elemArea = anAreaFunc.GetValue( aNodePoints );
2804 totalArea += elemArea;
2806 aNewXYZ += elemCenter * elemArea;
2808 aNewXYZ /= totalArea;
2809 if ( !theSurface.IsNull() ) {
2810 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2811 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2816 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2819 //=======================================================================
2820 //function : getClosestUV
2821 //purpose : return UV of closest projection
2822 //=======================================================================
2824 static bool getClosestUV (Extrema_GenExtPS& projector,
2825 const gp_Pnt& point,
2828 projector.Perform( point );
2829 if ( projector.IsDone() ) {
2830 double u, v, minVal = DBL_MAX;
2831 for ( int i = projector.NbExt(); i > 0; i-- )
2832 if ( projector.Value( i ) < minVal ) {
2833 minVal = projector.Value( i );
2834 projector.Point( i ).Parameter( u, v );
2836 result.SetCoord( u, v );
2842 //=======================================================================
2844 //purpose : Smooth theElements during theNbIterations or until a worst
2845 // element has aspect ratio <= theTgtAspectRatio.
2846 // Aspect Ratio varies in range [1.0, inf].
2847 // If theElements is empty, the whole mesh is smoothed.
2848 // theFixedNodes contains additionally fixed nodes. Nodes built
2849 // on edges and boundary nodes are always fixed.
2850 //=======================================================================
2852 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
2853 set<const SMDS_MeshNode*> & theFixedNodes,
2854 const SmoothMethod theSmoothMethod,
2855 const int theNbIterations,
2856 double theTgtAspectRatio,
2859 myLastCreatedElems.Clear();
2860 myLastCreatedNodes.Clear();
2862 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2864 if ( theTgtAspectRatio < 1.0 )
2865 theTgtAspectRatio = 1.0;
2867 const double disttol = 1.e-16;
2869 SMESH::Controls::AspectRatio aQualityFunc;
2871 SMESHDS_Mesh* aMesh = GetMeshDS();
2873 if ( theElems.empty() ) {
2874 // add all faces to theElems
2875 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2876 while ( fIt->more() ) {
2877 const SMDS_MeshElement* face = fIt->next();
2878 theElems.insert( face );
2881 // get all face ids theElems are on
2882 set< int > faceIdSet;
2883 TIDSortedElemSet::iterator itElem;
2885 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2886 int fId = FindShape( *itElem );
2887 // check that corresponding submesh exists and a shape is face
2889 faceIdSet.find( fId ) == faceIdSet.end() &&
2890 aMesh->MeshElements( fId )) {
2891 TopoDS_Shape F = aMesh->IndexToShape( fId );
2892 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2893 faceIdSet.insert( fId );
2896 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2898 // ===============================================
2899 // smooth elements on each TopoDS_Face separately
2900 // ===============================================
2902 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2903 for ( ; fId != faceIdSet.rend(); ++fId ) {
2904 // get face surface and submesh
2905 Handle(Geom_Surface) surface;
2906 SMESHDS_SubMesh* faceSubMesh = 0;
2908 double fToler2 = 0, f,l;
2909 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2910 bool isUPeriodic = false, isVPeriodic = false;
2912 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
2913 surface = BRep_Tool::Surface( face );
2914 faceSubMesh = aMesh->MeshElements( *fId );
2915 fToler2 = BRep_Tool::Tolerance( face );
2916 fToler2 *= fToler2 * 10.;
2917 isUPeriodic = surface->IsUPeriodic();
2920 isVPeriodic = surface->IsVPeriodic();
2923 surface->Bounds( u1, u2, v1, v2 );
2925 // ---------------------------------------------------------
2926 // for elements on a face, find movable and fixed nodes and
2927 // compute UV for them
2928 // ---------------------------------------------------------
2929 bool checkBoundaryNodes = false;
2930 bool isQuadratic = false;
2931 set<const SMDS_MeshNode*> setMovableNodes;
2932 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
2933 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
2934 list< const SMDS_MeshElement* > elemsOnFace;
2936 Extrema_GenExtPS projector;
2937 GeomAdaptor_Surface surfAdaptor;
2938 if ( !surface.IsNull() ) {
2939 surfAdaptor.Load( surface );
2940 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
2942 int nbElemOnFace = 0;
2943 itElem = theElems.begin();
2944 // loop on not yet smoothed elements: look for elems on a face
2945 while ( itElem != theElems.end() ) {
2946 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
2947 break; // all elements found
2949 const SMDS_MeshElement* elem = *itElem;
2950 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
2951 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
2955 elemsOnFace.push_back( elem );
2956 theElems.erase( itElem++ );
2960 isQuadratic = elem->IsQuadratic();
2962 // get movable nodes of elem
2963 const SMDS_MeshNode* node;
2964 SMDS_TypeOfPosition posType;
2965 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2966 int nn = 0, nbn = elem->NbNodes();
2967 if(elem->IsQuadratic())
2969 while ( nn++ < nbn ) {
2970 node = static_cast<const SMDS_MeshNode*>( itN->next() );
2971 const SMDS_PositionPtr& pos = node->GetPosition();
2972 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
2973 if (posType != SMDS_TOP_EDGE &&
2974 posType != SMDS_TOP_VERTEX &&
2975 theFixedNodes.find( node ) == theFixedNodes.end())
2977 // check if all faces around the node are on faceSubMesh
2978 // because a node on edge may be bound to face
2979 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
2981 if ( faceSubMesh ) {
2982 while ( eIt->more() && all ) {
2983 const SMDS_MeshElement* e = eIt->next();
2984 all = faceSubMesh->Contains( e );
2988 setMovableNodes.insert( node );
2990 checkBoundaryNodes = true;
2992 if ( posType == SMDS_TOP_3DSPACE )
2993 checkBoundaryNodes = true;
2996 if ( surface.IsNull() )
2999 // get nodes to check UV
3000 list< const SMDS_MeshNode* > uvCheckNodes;
3001 itN = elem->nodesIterator();
3002 nn = 0; nbn = elem->NbNodes();
3003 if(elem->IsQuadratic())
3005 while ( nn++ < nbn ) {
3006 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3007 if ( uvMap.find( node ) == uvMap.end() )
3008 uvCheckNodes.push_back( node );
3009 // add nodes of elems sharing node
3010 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3011 // while ( eIt->more() ) {
3012 // const SMDS_MeshElement* e = eIt->next();
3013 // if ( e != elem ) {
3014 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3015 // while ( nIt->more() ) {
3016 // const SMDS_MeshNode* n =
3017 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3018 // if ( uvMap.find( n ) == uvMap.end() )
3019 // uvCheckNodes.push_back( n );
3025 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3026 for ( ; n != uvCheckNodes.end(); ++n ) {
3029 const SMDS_PositionPtr& pos = node->GetPosition();
3030 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3032 switch ( posType ) {
3033 case SMDS_TOP_FACE: {
3034 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3035 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3038 case SMDS_TOP_EDGE: {
3039 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3040 Handle(Geom2d_Curve) pcurve;
3041 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3042 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3043 if ( !pcurve.IsNull() ) {
3044 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3045 uv = pcurve->Value( u ).XY();
3049 case SMDS_TOP_VERTEX: {
3050 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3051 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3052 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3057 // check existing UV
3058 bool project = true;
3059 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3060 double dist1 = DBL_MAX, dist2 = 0;
3061 if ( posType != SMDS_TOP_3DSPACE ) {
3062 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3063 project = dist1 > fToler2;
3065 if ( project ) { // compute new UV
3067 if ( !getClosestUV( projector, pNode, newUV )) {
3068 MESSAGE("Node Projection Failed " << node);
3072 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3074 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3076 if ( posType != SMDS_TOP_3DSPACE )
3077 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3078 if ( dist2 < dist1 )
3082 // store UV in the map
3083 listUV.push_back( uv );
3084 uvMap.insert( make_pair( node, &listUV.back() ));
3086 } // loop on not yet smoothed elements
3088 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3089 checkBoundaryNodes = true;
3091 // fix nodes on mesh boundary
3093 if ( checkBoundaryNodes ) {
3094 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3095 map< SMESH_TLink, int >::iterator link_nb;
3096 // put all elements links to linkNbMap
3097 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3098 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3099 const SMDS_MeshElement* elem = (*elemIt);
3100 int nbn = elem->NbCornerNodes();
3101 // loop on elem links: insert them in linkNbMap
3102 for ( int iN = 0; iN < nbn; ++iN ) {
3103 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3104 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3105 SMESH_TLink link( n1, n2 );
3106 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3110 // remove nodes that are in links encountered only once from setMovableNodes
3111 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3112 if ( link_nb->second == 1 ) {
3113 setMovableNodes.erase( link_nb->first.node1() );
3114 setMovableNodes.erase( link_nb->first.node2() );
3119 // -----------------------------------------------------
3120 // for nodes on seam edge, compute one more UV ( uvMap2 );
3121 // find movable nodes linked to nodes on seam and which
3122 // are to be smoothed using the second UV ( uvMap2 )
3123 // -----------------------------------------------------
3125 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3126 if ( !surface.IsNull() ) {
3127 TopExp_Explorer eExp( face, TopAbs_EDGE );
3128 for ( ; eExp.More(); eExp.Next() ) {
3129 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3130 if ( !BRep_Tool::IsClosed( edge, face ))
3132 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3133 if ( !sm ) continue;
3134 // find out which parameter varies for a node on seam
3137 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3138 if ( pcurve.IsNull() ) continue;
3139 uv1 = pcurve->Value( f );
3141 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3142 if ( pcurve.IsNull() ) continue;
3143 uv2 = pcurve->Value( f );
3144 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3146 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3147 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3149 // get nodes on seam and its vertices
3150 list< const SMDS_MeshNode* > seamNodes;
3151 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3152 while ( nSeamIt->more() ) {
3153 const SMDS_MeshNode* node = nSeamIt->next();
3154 if ( !isQuadratic || !IsMedium( node ))
3155 seamNodes.push_back( node );
3157 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3158 for ( ; vExp.More(); vExp.Next() ) {
3159 sm = aMesh->MeshElements( vExp.Current() );
3161 nSeamIt = sm->GetNodes();
3162 while ( nSeamIt->more() )
3163 seamNodes.push_back( nSeamIt->next() );
3166 // loop on nodes on seam
3167 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3168 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3169 const SMDS_MeshNode* nSeam = *noSeIt;
3170 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3171 if ( n_uv == uvMap.end() )
3174 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3175 // set the second UV
3176 listUV.push_back( *n_uv->second );
3177 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3178 if ( uvMap2.empty() )
3179 uvMap2 = uvMap; // copy the uvMap contents
3180 uvMap2[ nSeam ] = &listUV.back();
3182 // collect movable nodes linked to ones on seam in nodesNearSeam
3183 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3184 while ( eIt->more() ) {
3185 const SMDS_MeshElement* e = eIt->next();
3186 int nbUseMap1 = 0, nbUseMap2 = 0;
3187 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3188 int nn = 0, nbn = e->NbNodes();
3189 if(e->IsQuadratic()) nbn = nbn/2;
3190 while ( nn++ < nbn )
3192 const SMDS_MeshNode* n =
3193 static_cast<const SMDS_MeshNode*>( nIt->next() );
3195 setMovableNodes.find( n ) == setMovableNodes.end() )
3197 // add only nodes being closer to uv2 than to uv1
3198 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3199 0.5 * ( n->Y() + nSeam->Y() ),
3200 0.5 * ( n->Z() + nSeam->Z() ));
3202 getClosestUV( projector, pMid, uv );
3203 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3204 nodesNearSeam.insert( n );
3210 // for centroidalSmooth all element nodes must
3211 // be on one side of a seam
3212 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3213 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3215 while ( nn++ < nbn ) {
3216 const SMDS_MeshNode* n =
3217 static_cast<const SMDS_MeshNode*>( nIt->next() );
3218 setMovableNodes.erase( n );
3222 } // loop on nodes on seam
3223 } // loop on edge of a face
3224 } // if ( !face.IsNull() )
3226 if ( setMovableNodes.empty() ) {
3227 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3228 continue; // goto next face
3236 double maxRatio = -1., maxDisplacement = -1.;
3237 set<const SMDS_MeshNode*>::iterator nodeToMove;
3238 for ( it = 0; it < theNbIterations; it++ ) {
3239 maxDisplacement = 0.;
3240 nodeToMove = setMovableNodes.begin();
3241 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3242 const SMDS_MeshNode* node = (*nodeToMove);
3243 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3246 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3247 if ( theSmoothMethod == LAPLACIAN )
3248 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3250 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3252 // node displacement
3253 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3254 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3255 if ( aDispl > maxDisplacement )
3256 maxDisplacement = aDispl;
3258 // no node movement => exit
3259 //if ( maxDisplacement < 1.e-16 ) {
3260 if ( maxDisplacement < disttol ) {
3261 MESSAGE("-- no node movement --");
3265 // check elements quality
3267 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3268 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3269 const SMDS_MeshElement* elem = (*elemIt);
3270 if ( !elem || elem->GetType() != SMDSAbs_Face )
3272 SMESH::Controls::TSequenceOfXYZ aPoints;
3273 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3274 double aValue = aQualityFunc.GetValue( aPoints );
3275 if ( aValue > maxRatio )
3279 if ( maxRatio <= theTgtAspectRatio ) {
3280 MESSAGE("-- quality achived --");
3283 if (it+1 == theNbIterations) {
3284 MESSAGE("-- Iteration limit exceeded --");
3286 } // smoothing iterations
3288 MESSAGE(" Face id: " << *fId <<
3289 " Nb iterstions: " << it <<
3290 " Displacement: " << maxDisplacement <<
3291 " Aspect Ratio " << maxRatio);
3293 // ---------------------------------------
3294 // new nodes positions are computed,
3295 // record movement in DS and set new UV
3296 // ---------------------------------------
3297 nodeToMove = setMovableNodes.begin();
3298 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3299 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3300 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3301 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3302 if ( node_uv != uvMap.end() ) {
3303 gp_XY* uv = node_uv->second;
3305 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3309 // move medium nodes of quadratic elements
3312 SMESH_MesherHelper helper( *GetMesh() );
3313 if ( !face.IsNull() )
3314 helper.SetSubShape( face );
3315 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3316 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3317 const SMDS_VtkFace* QF =
3318 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3319 if(QF && QF->IsQuadratic()) {
3320 vector<const SMDS_MeshNode*> Ns;
3321 Ns.reserve(QF->NbNodes()+1);
3322 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3323 while ( anIter->more() )
3324 Ns.push_back( cast2Node(anIter->next()) );
3325 Ns.push_back( Ns[0] );
3327 for(int i=0; i<QF->NbNodes(); i=i+2) {
3328 if ( !surface.IsNull() ) {
3329 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3330 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3331 gp_XY uv = ( uv1 + uv2 ) / 2.;
3332 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3333 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3336 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3337 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3338 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3340 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3341 fabs( Ns[i+1]->Y() - y ) > disttol ||
3342 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3343 // we have to move i+1 node
3344 aMesh->MoveNode( Ns[i+1], x, y, z );
3351 } // loop on face ids
3355 //=======================================================================
3356 //function : isReverse
3357 //purpose : Return true if normal of prevNodes is not co-directied with
3358 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3359 // iNotSame is where prevNodes and nextNodes are different
3360 //=======================================================================
3362 static bool isReverse(vector<const SMDS_MeshNode*> prevNodes,
3363 vector<const SMDS_MeshNode*> nextNodes,
3367 int iBeforeNotSame = ( iNotSame == 0 ? nbNodes - 1 : iNotSame - 1 );
3368 int iAfterNotSame = ( iNotSame + 1 == nbNodes ? 0 : iNotSame + 1 );
3370 const SMDS_MeshNode* nB = prevNodes[ iBeforeNotSame ];
3371 const SMDS_MeshNode* nA = prevNodes[ iAfterNotSame ];
3372 const SMDS_MeshNode* nP = prevNodes[ iNotSame ];
3373 const SMDS_MeshNode* nN = nextNodes[ iNotSame ];
3375 gp_Pnt pB ( nB->X(), nB->Y(), nB->Z() );
3376 gp_Pnt pA ( nA->X(), nA->Y(), nA->Z() );
3377 gp_Pnt pP ( nP->X(), nP->Y(), nP->Z() );
3378 gp_Pnt pN ( nN->X(), nN->Y(), nN->Z() );
3380 gp_Vec vB ( pP, pB ), vA ( pP, pA ), vN ( pP, pN );
3382 return (vA ^ vB) * vN < 0.0;
3385 //=======================================================================
3387 * \brief Create elements by sweeping an element
3388 * \param elem - element to sweep
3389 * \param newNodesItVec - nodes generated from each node of the element
3390 * \param newElems - generated elements
3391 * \param nbSteps - number of sweeping steps
3392 * \param srcElements - to append elem for each generated element
3394 //=======================================================================
3396 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3397 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3398 list<const SMDS_MeshElement*>& newElems,
3400 SMESH_SequenceOfElemPtr& srcElements)
3402 //MESSAGE("sweepElement " << nbSteps);
3403 SMESHDS_Mesh* aMesh = GetMeshDS();
3405 // Loop on elem nodes:
3406 // find new nodes and detect same nodes indices
3407 int nbNodes = elem->NbNodes();
3408 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3409 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3410 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3411 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3413 int iNode, nbSame = 0, iNotSameNode = 0, iSameNode = 0;
3414 vector<int> sames(nbNodes);
3415 vector<bool> issimple(nbNodes);
3417 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3418 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3419 const SMDS_MeshNode* node = nnIt->first;
3420 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3421 if ( listNewNodes.empty() ) {
3425 issimple[iNode] = (listNewNodes.size()==nbSteps); // is node medium
3427 itNN[ iNode ] = listNewNodes.begin();
3428 prevNod[ iNode ] = node;
3429 nextNod[ iNode ] = listNewNodes.front();
3430 if( !elem->IsQuadratic() || !issimple[iNode] ) {
3431 if ( prevNod[ iNode ] != nextNod [ iNode ])
3432 iNotSameNode = iNode;
3436 sames[nbSame++] = iNode;
3441 //cerr<<" nbSame = "<<nbSame<<endl;
3442 if ( nbSame == nbNodes || nbSame > 2) {
3443 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3444 //INFOS( " Too many same nodes of element " << elem->GetID() );
3448 // if( elem->IsQuadratic() && nbSame>0 ) {
3449 // MESSAGE( "Can not rotate quadratic element " << elem->GetID() );
3453 int iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3454 int nbBaseNodes = ( elem->IsQuadratic() ? nbNodes/2 : nbNodes );
3456 iBeforeSame = ( iSameNode == 0 ? nbBaseNodes - 1 : iSameNode - 1 );
3457 iAfterSame = ( iSameNode + 1 == nbBaseNodes ? 0 : iSameNode + 1 );
3458 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3462 //cout<<" prevNod[0]="<< prevNod[0]<<" prevNod[1]="<< prevNod[1]
3463 // <<" prevNod[2]="<< prevNod[2]<<" prevNod[3]="<< prevNod[4]
3464 // <<" prevNod[4]="<< prevNod[4]<<" prevNod[5]="<< prevNod[5]
3465 // <<" prevNod[6]="<< prevNod[6]<<" prevNod[7]="<< prevNod[7]<<endl;
3467 // check element orientation
3469 if ( nbNodes > 2 && !isReverse( prevNod, nextNod, nbNodes, iNotSameNode )) {
3470 //MESSAGE("Reversed elem " << elem );
3474 std::swap( iBeforeSame, iAfterSame );
3477 // make new elements
3478 const SMDS_MeshElement* lastElem = elem;
3479 for (int iStep = 0; iStep < nbSteps; iStep++ ) {
3481 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3482 if(issimple[iNode]) {
3483 nextNod[ iNode ] = *itNN[ iNode ];
3487 if( elem->GetType()==SMDSAbs_Node ) {
3488 // we have to use two nodes
3489 midlNod[ iNode ] = *itNN[ iNode ];
3491 nextNod[ iNode ] = *itNN[ iNode ];
3494 else if(!elem->IsQuadratic() || lastElem->IsMediumNode(prevNod[iNode]) ) {
3495 // we have to use each second node
3497 nextNod[ iNode ] = *itNN[ iNode ];
3501 // we have to use two nodes
3502 midlNod[ iNode ] = *itNN[ iNode ];
3504 nextNod[ iNode ] = *itNN[ iNode ];
3509 SMDS_MeshElement* aNewElem = 0;
3510 if(!elem->IsPoly()) {
3511 switch ( nbNodes ) {
3515 if ( nbSame == 0 ) {
3517 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3519 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3525 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3526 nextNod[ 1 ], nextNod[ 0 ] );
3528 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3529 nextNod[ iNotSameNode ] );
3533 case 3: { // TRIANGLE or quadratic edge
3534 if(elem->GetType() == SMDSAbs_Face) { // TRIANGLE
3536 if ( nbSame == 0 ) // --- pentahedron
3537 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3538 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ] );
3540 else if ( nbSame == 1 ) // --- pyramid
3541 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3542 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3543 nextNod[ iSameNode ]);
3545 else // 2 same nodes: --- tetrahedron
3546 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3547 nextNod[ iNotSameNode ]);
3549 else { // quadratic edge
3550 if(nbSame==0) { // quadratic quadrangle
3551 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], nextNod[1], prevNod[1],
3552 midlNod[0], nextNod[2], midlNod[1], prevNod[2]);
3554 else if(nbSame==1) { // quadratic triangle
3556 return; // medium node on axis
3558 else if(sames[0]==0) {
3559 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3560 nextNod[2], midlNod[1], prevNod[2]);
3562 else { // sames[0]==1
3563 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3564 midlNod[0], nextNod[2], prevNod[2]);
3573 case 4: { // QUADRANGLE
3575 if ( nbSame == 0 ) // --- hexahedron
3576 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], prevNod[ 3 ],
3577 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ], nextNod[ 3 ]);
3579 else if ( nbSame == 1 ) { // --- pyramid + pentahedron
3580 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3581 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3582 nextNod[ iSameNode ]);
3583 newElems.push_back( aNewElem );
3584 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3585 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3586 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3588 else if ( nbSame == 2 ) { // pentahedron
3589 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3590 // iBeforeSame is same too
3591 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3592 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3593 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3595 // iAfterSame is same too
3596 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3597 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3598 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3602 case 6: { // quadratic triangle
3603 // create pentahedron with 15 nodes
3605 if(i0>0) { // reversed case
3606 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[2], prevNod[1],
3607 nextNod[0], nextNod[2], nextNod[1],
3608 prevNod[5], prevNod[4], prevNod[3],
3609 nextNod[5], nextNod[4], nextNod[3],
3610 midlNod[0], midlNod[2], midlNod[1]);
3612 else { // not reversed case
3613 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3614 nextNod[0], nextNod[1], nextNod[2],
3615 prevNod[3], prevNod[4], prevNod[5],
3616 nextNod[3], nextNod[4], nextNod[5],
3617 midlNod[0], midlNod[1], midlNod[2]);
3620 else if(nbSame==1) {
3621 // 2d order pyramid of 13 nodes
3622 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5,
3623 // int n12,int n23,int n34,int n41,
3624 // int n15,int n25,int n35,int n45, int ID);
3626 int n1,n4,n41,n15,n45;
3627 if(i0>0) { // reversed case
3628 n1 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3629 n4 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3635 n1 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3636 n4 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3641 aNewElem = aMesh->AddVolume(prevNod[n1], nextNod[n1],
3642 nextNod[n4], prevNod[n4], prevNod[n5],
3643 midlNod[n1], nextNod[n41],
3644 midlNod[n4], prevNod[n41],
3645 prevNod[n15], nextNod[n15],
3646 nextNod[n45], prevNod[n45]);
3648 else if(nbSame==2) {
3649 // 2d order tetrahedron of 10 nodes
3650 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4,
3651 // int n12,int n23,int n31,
3652 // int n14,int n24,int n34, int ID);
3653 int n1 = iNotSameNode;
3654 int n2,n3,n12,n23,n31;
3655 if(i0>0) { // reversed case
3656 n2 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3657 n3 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3663 n2 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3664 n3 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3669 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3670 prevNod[n12], prevNod[n23], prevNod[n31],
3671 midlNod[n1], nextNod[n12], nextNod[n31]);
3675 case 8: { // quadratic quadrangle
3677 // create hexahedron with 20 nodes
3678 if(i0>0) { // reversed case
3679 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[3], prevNod[2], prevNod[1],
3680 nextNod[0], nextNod[3], nextNod[2], nextNod[1],
3681 prevNod[7], prevNod[6], prevNod[5], prevNod[4],
3682 nextNod[7], nextNod[6], nextNod[5], nextNod[4],
3683 midlNod[0], midlNod[3], midlNod[2], midlNod[1]);
3685 else { // not reversed case
3686 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3687 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3688 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3689 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3690 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3693 else if(nbSame==1) {
3694 // --- pyramid + pentahedron - can not be created since it is needed
3695 // additional middle node ot the center of face
3696 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3699 else if(nbSame==2) {
3700 // 2d order Pentahedron with 15 nodes
3701 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6,
3702 // int n12,int n23,int n31,int n45,int n56,int n64,
3703 // int n14,int n25,int n36, int ID);
3705 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3706 // iBeforeSame is same too
3713 // iAfterSame is same too
3719 int n12,n45,n14,n25;
3720 if(i0>0) { //reversed case
3732 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3733 prevNod[n4], prevNod[n5], nextNod[n5],
3734 prevNod[n12], midlNod[n2], nextNod[n12],
3735 prevNod[n45], midlNod[n5], nextNod[n45],
3736 prevNod[n14], prevNod[n25], nextNod[n25]);
3741 // realized for extrusion only
3742 //vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3743 //vector<int> quantities (nbNodes + 2);
3745 //quantities[0] = nbNodes; // bottom of prism
3746 //for (int inode = 0; inode < nbNodes; inode++) {
3747 // polyedre_nodes[inode] = prevNod[inode];
3750 //quantities[1] = nbNodes; // top of prism
3751 //for (int inode = 0; inode < nbNodes; inode++) {
3752 // polyedre_nodes[nbNodes + inode] = nextNod[inode];
3755 //for (int iface = 0; iface < nbNodes; iface++) {
3756 // quantities[iface + 2] = 4;
3757 // int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3758 // polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3759 // polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3760 // polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3761 // polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3763 //aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3770 // realized for extrusion only
3771 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3772 vector<int> quantities (nbNodes + 2);
3774 quantities[0] = nbNodes; // bottom of prism
3775 for (int inode = 0; inode < nbNodes; inode++) {
3776 polyedre_nodes[inode] = prevNod[inode];
3779 quantities[1] = nbNodes; // top of prism
3780 for (int inode = 0; inode < nbNodes; inode++) {
3781 polyedre_nodes[nbNodes + inode] = nextNod[inode];
3784 for (int iface = 0; iface < nbNodes; iface++) {
3785 quantities[iface + 2] = 4;
3786 int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3787 polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3788 polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3789 polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3790 polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3792 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3796 newElems.push_back( aNewElem );
3797 myLastCreatedElems.Append(aNewElem);
3798 srcElements.Append( elem );
3799 lastElem = aNewElem;
3802 // set new prev nodes
3803 for ( iNode = 0; iNode < nbNodes; iNode++ )
3804 prevNod[ iNode ] = nextNod[ iNode ];
3809 //=======================================================================
3811 * \brief Create 1D and 2D elements around swept elements
3812 * \param mapNewNodes - source nodes and ones generated from them
3813 * \param newElemsMap - source elements and ones generated from them
3814 * \param elemNewNodesMap - nodes generated from each node of each element
3815 * \param elemSet - all swept elements
3816 * \param nbSteps - number of sweeping steps
3817 * \param srcElements - to append elem for each generated element
3819 //=======================================================================
3821 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3822 TElemOfElemListMap & newElemsMap,
3823 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3824 TIDSortedElemSet& elemSet,
3826 SMESH_SequenceOfElemPtr& srcElements)
3828 MESSAGE("makeWalls");
3829 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3830 SMESHDS_Mesh* aMesh = GetMeshDS();
3832 // Find nodes belonging to only one initial element - sweep them to get edges.
3834 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3835 for ( ; nList != mapNewNodes.end(); nList++ ) {
3836 const SMDS_MeshNode* node =
3837 static_cast<const SMDS_MeshNode*>( nList->first );
3838 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3839 int nbInitElems = 0;
3840 const SMDS_MeshElement* el = 0;
3841 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3842 while ( eIt->more() && nbInitElems < 2 ) {
3844 SMDSAbs_ElementType type = el->GetType();
3845 if ( type == SMDSAbs_Volume || type < highType ) continue;
3846 if ( type > highType ) {
3850 if ( elemSet.find(el) != elemSet.end() )
3853 if ( nbInitElems < 2 ) {
3854 bool NotCreateEdge = el && el->IsQuadratic() && el->IsMediumNode(node);
3855 if(!NotCreateEdge) {
3856 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3857 list<const SMDS_MeshElement*> newEdges;
3858 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3863 // Make a ceiling for each element ie an equal element of last new nodes.
3864 // Find free links of faces - make edges and sweep them into faces.
3866 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3867 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3868 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) {
3869 const SMDS_MeshElement* elem = itElem->first;
3870 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3872 if(itElem->second.size()==0) continue;
3874 if ( elem->GetType() == SMDSAbs_Edge ) {
3875 // create a ceiling edge
3876 if (!elem->IsQuadratic()) {
3877 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3878 vecNewNodes[ 1 ]->second.back())) {
3879 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3880 vecNewNodes[ 1 ]->second.back()));
3881 srcElements.Append( myLastCreatedElems.Last() );
3885 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3886 vecNewNodes[ 1 ]->second.back(),
3887 vecNewNodes[ 2 ]->second.back())) {
3888 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3889 vecNewNodes[ 1 ]->second.back(),
3890 vecNewNodes[ 2 ]->second.back()));
3891 srcElements.Append( myLastCreatedElems.Last() );
3895 if ( elem->GetType() != SMDSAbs_Face )
3898 bool hasFreeLinks = false;
3900 TIDSortedElemSet avoidSet;
3901 avoidSet.insert( elem );
3903 set<const SMDS_MeshNode*> aFaceLastNodes;
3904 int iNode, nbNodes = vecNewNodes.size();
3905 if(!elem->IsQuadratic()) {
3906 // loop on the face nodes
3907 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3908 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3909 // look for free links of the face
3910 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
3911 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3912 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3913 // check if a link is free
3914 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3915 hasFreeLinks = true;
3916 // make an edge and a ceiling for a new edge
3917 if ( !aMesh->FindEdge( n1, n2 )) {
3918 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
3919 srcElements.Append( myLastCreatedElems.Last() );
3921 n1 = vecNewNodes[ iNode ]->second.back();
3922 n2 = vecNewNodes[ iNext ]->second.back();
3923 if ( !aMesh->FindEdge( n1, n2 )) {
3924 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
3925 srcElements.Append( myLastCreatedElems.Last() );
3930 else { // elem is quadratic face
3931 int nbn = nbNodes/2;
3932 for ( iNode = 0; iNode < nbn; iNode++ ) {
3933 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3934 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
3935 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3936 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3937 // check if a link is free
3938 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3939 hasFreeLinks = true;
3940 // make an edge and a ceiling for a new edge
3942 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
3943 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3944 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
3945 srcElements.Append( myLastCreatedElems.Last() );
3947 n1 = vecNewNodes[ iNode ]->second.back();
3948 n2 = vecNewNodes[ iNext ]->second.back();
3949 n3 = vecNewNodes[ iNode+nbn ]->second.back();
3950 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3951 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
3952 srcElements.Append( myLastCreatedElems.Last() );
3956 for ( iNode = nbn; iNode < 2*nbn; iNode++ ) {
3957 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3961 // sweep free links into faces
3963 if ( hasFreeLinks ) {
3964 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
3965 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
3967 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
3968 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3969 initNodeSet.insert( vecNewNodes[ iNode ]->first );
3970 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
3972 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
3973 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
3975 while ( iVol++ < volNb ) v++;
3976 // find indices of free faces of a volume and their source edges
3977 list< int > freeInd;
3978 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
3979 SMDS_VolumeTool vTool( *v );
3980 int iF, nbF = vTool.NbFaces();
3981 for ( iF = 0; iF < nbF; iF ++ ) {
3982 if (vTool.IsFreeFace( iF ) &&
3983 vTool.GetFaceNodes( iF, faceNodeSet ) &&
3984 initNodeSet != faceNodeSet) // except an initial face
3986 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
3988 freeInd.push_back( iF );
3989 // find source edge of a free face iF
3990 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
3991 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
3992 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
3993 initNodeSet.begin(), initNodeSet.end(),
3994 commonNodes.begin());
3995 if ( (*v)->IsQuadratic() )
3996 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
3998 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4000 if ( !srcEdges.back() )
4002 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4003 << iF << " of volume #" << vTool.ID() << endl;
4008 if ( freeInd.empty() )
4011 // create faces for all steps;
4012 // if such a face has been already created by sweep of edge,
4013 // assure that its orientation is OK
4014 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4016 vTool.SetExternalNormal();
4017 const int nextShift = vTool.IsForward() ? +1 : -1;
4018 list< int >::iterator ind = freeInd.begin();
4019 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4020 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4022 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4023 int nbn = vTool.NbFaceNodes( *ind );
4024 if ( ! (*v)->IsPoly() )
4026 case 3: { ///// triangle
4027 const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4029 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4031 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4033 nodes[ 1 + nextShift ] };
4035 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4037 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4042 case 4: { ///// quadrangle
4043 const SMDS_MeshFace * f =
4044 aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4046 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4048 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4049 nodes[ 2 ], nodes[ 2+nextShift ] };
4051 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4053 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4054 newOrder[ 2 ], newOrder[ 3 ]));
4058 case 6: { /////// quadratic triangle
4059 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4],
4060 nodes[1], nodes[3], nodes[5] );
4062 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4064 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4066 nodes[2 + 2*nextShift],
4067 nodes[3 - 2*nextShift],
4069 nodes[3 + 2*nextShift]};
4071 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4073 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4082 default: /////// quadratic quadrangle
4083 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4084 nodes[1], nodes[3], nodes[5], nodes[7] );
4086 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4088 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4089 nodes[4 - 2*nextShift],
4091 nodes[4 + 2*nextShift],
4093 nodes[5 - 2*nextShift],
4095 nodes[5 + 2*nextShift] };
4097 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4099 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4100 newOrder[ 2 ], newOrder[ 3 ],
4101 newOrder[ 4 ], newOrder[ 5 ],
4102 newOrder[ 6 ], newOrder[ 7 ]));
4106 else { //////// polygon
4108 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
4109 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4111 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4113 if ( !vTool.IsForward() )
4114 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4116 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4118 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4122 while ( srcElements.Length() < myLastCreatedElems.Length() )
4123 srcElements.Append( *srcEdge );
4125 } // loop on free faces
4127 // go to the next volume
4129 while ( iVol++ < nbVolumesByStep ) v++;
4132 } // loop on volumes of one step
4133 } // sweep free links into faces
4135 // Make a ceiling face with a normal external to a volume
4137 SMDS_VolumeTool lastVol( itElem->second.back() );
4139 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4141 lastVol.SetExternalNormal();
4142 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4143 int nbn = lastVol.NbFaceNodes( iF );
4146 if (!hasFreeLinks ||
4147 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4148 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4151 if (!hasFreeLinks ||
4152 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4153 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ));
4156 if(itElem->second.back()->IsQuadratic()) {
4158 if (!hasFreeLinks ||
4159 !aMesh->FindFace(nodes[0], nodes[2], nodes[4],
4160 nodes[1], nodes[3], nodes[5]) ) {
4161 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4162 nodes[1], nodes[3], nodes[5]));
4166 if (!hasFreeLinks ||
4167 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4168 nodes[1], nodes[3], nodes[5], nodes[7]) )
4169 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4170 nodes[1], nodes[3], nodes[5], nodes[7]));
4174 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
4175 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4176 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4180 while ( srcElements.Length() < myLastCreatedElems.Length() )
4181 srcElements.Append( myLastCreatedElems.Last() );
4183 } // loop on swept elements
4186 //=======================================================================
4187 //function : RotationSweep
4189 //=======================================================================
4191 SMESH_MeshEditor::PGroupIDs
4192 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4193 const gp_Ax1& theAxis,
4194 const double theAngle,
4195 const int theNbSteps,
4196 const double theTol,
4197 const bool theMakeGroups,
4198 const bool theMakeWalls)
4200 myLastCreatedElems.Clear();
4201 myLastCreatedNodes.Clear();
4203 // source elements for each generated one
4204 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4206 MESSAGE( "RotationSweep()");
4208 aTrsf.SetRotation( theAxis, theAngle );
4210 aTrsf2.SetRotation( theAxis, theAngle/2. );
4212 gp_Lin aLine( theAxis );
4213 double aSqTol = theTol * theTol;
4215 SMESHDS_Mesh* aMesh = GetMeshDS();
4217 TNodeOfNodeListMap mapNewNodes;
4218 TElemOfVecOfNnlmiMap mapElemNewNodes;
4219 TElemOfElemListMap newElemsMap;
4222 TIDSortedElemSet::iterator itElem;
4223 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4224 const SMDS_MeshElement* elem = *itElem;
4225 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4227 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4228 newNodesItVec.reserve( elem->NbNodes() );
4230 // loop on elem nodes
4231 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4232 while ( itN->more() ) {
4233 // check if a node has been already sweeped
4234 const SMDS_MeshNode* node = cast2Node( itN->next() );
4236 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4238 aXYZ.Coord( coord[0], coord[1], coord[2] );
4239 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4241 TNodeOfNodeListMapItr nIt = mapNewNodes.find( node );
4242 if ( nIt == mapNewNodes.end() ) {
4243 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4244 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4247 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4249 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4250 //bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4251 const SMDS_MeshNode * newNode = node;
4252 for ( int i = 0; i < theNbSteps; i++ ) {
4254 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4256 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4257 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4258 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4259 myLastCreatedNodes.Append(newNode);
4260 srcNodes.Append( node );
4261 listNewNodes.push_back( newNode );
4262 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4263 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4266 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4268 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4269 myLastCreatedNodes.Append(newNode);
4270 srcNodes.Append( node );
4271 listNewNodes.push_back( newNode );
4274 listNewNodes.push_back( newNode );
4275 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4276 listNewNodes.push_back( newNode );
4283 // if current elem is quadratic and current node is not medium
4284 // we have to check - may be it is needed to insert additional nodes
4285 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4286 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4287 if(listNewNodes.size()==theNbSteps) {
4288 listNewNodes.clear();
4290 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4292 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4293 const SMDS_MeshNode * newNode = node;
4295 for(int i = 0; i<theNbSteps; i++) {
4296 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4297 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4298 cout<<" 3 AddNode: "<<newNode;
4299 myLastCreatedNodes.Append(newNode);
4300 listNewNodes.push_back( newNode );
4301 srcNodes.Append( node );
4302 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4303 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4304 cout<<" 4 AddNode: "<<newNode;
4305 myLastCreatedNodes.Append(newNode);
4306 srcNodes.Append( node );
4307 listNewNodes.push_back( newNode );
4311 listNewNodes.push_back( newNode );
4317 newNodesItVec.push_back( nIt );
4319 // make new elements
4320 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4324 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4326 PGroupIDs newGroupIDs;
4327 if ( theMakeGroups )
4328 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4334 //=======================================================================
4335 //function : CreateNode
4337 //=======================================================================
4338 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4341 const double tolnode,
4342 SMESH_SequenceOfNode& aNodes)
4344 myLastCreatedElems.Clear();
4345 myLastCreatedNodes.Clear();
4348 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4350 // try to search in sequence of existing nodes
4351 // if aNodes.Length()>0 we 'nave to use given sequence
4352 // else - use all nodes of mesh
4353 if(aNodes.Length()>0) {
4355 for(i=1; i<=aNodes.Length(); i++) {
4356 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4357 if(P1.Distance(P2)<tolnode)
4358 return aNodes.Value(i);
4362 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4363 while(itn->more()) {
4364 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4365 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4366 if(P1.Distance(P2)<tolnode)
4371 // create new node and return it
4372 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4373 myLastCreatedNodes.Append(NewNode);
4378 //=======================================================================
4379 //function : ExtrusionSweep
4381 //=======================================================================
4383 SMESH_MeshEditor::PGroupIDs
4384 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4385 const gp_Vec& theStep,
4386 const int theNbSteps,
4387 TElemOfElemListMap& newElemsMap,
4388 const bool theMakeGroups,
4390 const double theTolerance)
4392 ExtrusParam aParams;
4393 aParams.myDir = gp_Dir(theStep);
4394 aParams.myNodes.Clear();
4395 aParams.mySteps = new TColStd_HSequenceOfReal;
4397 for(i=1; i<=theNbSteps; i++)
4398 aParams.mySteps->Append(theStep.Magnitude());
4401 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4405 //=======================================================================
4406 //function : ExtrusionSweep
4408 //=======================================================================
4410 SMESH_MeshEditor::PGroupIDs
4411 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4412 ExtrusParam& theParams,
4413 TElemOfElemListMap& newElemsMap,
4414 const bool theMakeGroups,
4416 const double theTolerance)
4418 MESSAGE("ExtrusionSweep " << theMakeGroups << " " << theFlags << " " << theTolerance);
4419 myLastCreatedElems.Clear();
4420 myLastCreatedNodes.Clear();
4422 // source elements for each generated one
4423 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4425 SMESHDS_Mesh* aMesh = GetMeshDS();
4427 int nbsteps = theParams.mySteps->Length();
4429 TNodeOfNodeListMap mapNewNodes;
4430 //TNodeOfNodeVecMap mapNewNodes;
4431 TElemOfVecOfNnlmiMap mapElemNewNodes;
4432 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4435 TIDSortedElemSet::iterator itElem;
4436 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4437 // check element type
4438 const SMDS_MeshElement* elem = *itElem;
4439 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4442 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4443 //vector<TNodeOfNodeVecMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4444 newNodesItVec.reserve( elem->NbNodes() );
4446 // loop on elem nodes
4447 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4448 while ( itN->more() )
4450 // check if a node has been already sweeped
4451 const SMDS_MeshNode* node = cast2Node( itN->next() );
4452 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
4453 //TNodeOfNodeVecMap::iterator nIt = mapNewNodes.find( node );
4454 if ( nIt == mapNewNodes.end() ) {
4455 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4456 //nIt = mapNewNodes.insert( make_pair( node, vector<const SMDS_MeshNode*>() )).first;
4457 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4458 //vector<const SMDS_MeshNode*>& vecNewNodes = nIt->second;
4459 //vecNewNodes.reserve(nbsteps);
4462 double coord[] = { node->X(), node->Y(), node->Z() };
4463 //int nbsteps = theParams.mySteps->Length();
4464 for ( int i = 0; i < nbsteps; i++ ) {
4465 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4466 // create additional node
4467 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4468 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4469 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4470 if( theFlags & EXTRUSION_FLAG_SEW ) {
4471 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4472 theTolerance, theParams.myNodes);
4473 listNewNodes.push_back( newNode );
4476 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4477 myLastCreatedNodes.Append(newNode);
4478 srcNodes.Append( node );
4479 listNewNodes.push_back( newNode );
4482 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4483 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4484 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4485 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4486 if( theFlags & EXTRUSION_FLAG_SEW ) {
4487 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4488 theTolerance, theParams.myNodes);
4489 listNewNodes.push_back( newNode );
4490 //vecNewNodes[i]=newNode;
4493 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4494 myLastCreatedNodes.Append(newNode);
4495 srcNodes.Append( node );
4496 listNewNodes.push_back( newNode );
4497 //vecNewNodes[i]=newNode;
4502 // if current elem is quadratic and current node is not medium
4503 // we have to check - may be it is needed to insert additional nodes
4504 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4505 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4506 if(listNewNodes.size()==nbsteps) {
4507 listNewNodes.clear();
4508 double coord[] = { node->X(), node->Y(), node->Z() };
4509 for ( int i = 0; i < nbsteps; i++ ) {
4510 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4511 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4512 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4513 if( theFlags & EXTRUSION_FLAG_SEW ) {
4514 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4515 theTolerance, theParams.myNodes);
4516 listNewNodes.push_back( newNode );
4519 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4520 myLastCreatedNodes.Append(newNode);
4521 srcNodes.Append( node );
4522 listNewNodes.push_back( newNode );
4524 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4525 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4526 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4527 if( theFlags & EXTRUSION_FLAG_SEW ) {
4528 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4529 theTolerance, theParams.myNodes);
4530 listNewNodes.push_back( newNode );
4533 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4534 myLastCreatedNodes.Append(newNode);
4535 srcNodes.Append( node );
4536 listNewNodes.push_back( newNode );
4542 newNodesItVec.push_back( nIt );
4544 // make new elements
4545 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4548 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4549 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4551 PGroupIDs newGroupIDs;
4552 if ( theMakeGroups )
4553 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4559 //=======================================================================
4560 //class : SMESH_MeshEditor_PathPoint
4561 //purpose : auxiliary class
4562 //=======================================================================
4563 class SMESH_MeshEditor_PathPoint {
4565 SMESH_MeshEditor_PathPoint() {
4566 myPnt.SetCoord(99., 99., 99.);
4567 myTgt.SetCoord(1.,0.,0.);
4571 void SetPnt(const gp_Pnt& aP3D){
4574 void SetTangent(const gp_Dir& aTgt){
4577 void SetAngle(const double& aBeta){
4580 void SetParameter(const double& aPrm){
4583 const gp_Pnt& Pnt()const{
4586 const gp_Dir& Tangent()const{
4589 double Angle()const{
4592 double Parameter()const{
4604 //=======================================================================
4605 //function : ExtrusionAlongTrack
4607 //=======================================================================
4608 SMESH_MeshEditor::Extrusion_Error
4609 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4610 SMESH_subMesh* theTrack,
4611 const SMDS_MeshNode* theN1,
4612 const bool theHasAngles,
4613 list<double>& theAngles,
4614 const bool theLinearVariation,
4615 const bool theHasRefPoint,
4616 const gp_Pnt& theRefPoint,
4617 const bool theMakeGroups)
4619 MESSAGE("ExtrusionAlongTrack");
4620 myLastCreatedElems.Clear();
4621 myLastCreatedNodes.Clear();
4624 std::list<double> aPrms;
4625 TIDSortedElemSet::iterator itElem;
4628 TopoDS_Edge aTrackEdge;
4629 TopoDS_Vertex aV1, aV2;
4631 SMDS_ElemIteratorPtr aItE;
4632 SMDS_NodeIteratorPtr aItN;
4633 SMDSAbs_ElementType aTypeE;
4635 TNodeOfNodeListMap mapNewNodes;
4638 aNbE = theElements.size();
4641 return EXTR_NO_ELEMENTS;
4643 // 1.1 Track Pattern
4646 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4648 aItE = pSubMeshDS->GetElements();
4649 while ( aItE->more() ) {
4650 const SMDS_MeshElement* pE = aItE->next();
4651 aTypeE = pE->GetType();
4652 // Pattern must contain links only
4653 if ( aTypeE != SMDSAbs_Edge )
4654 return EXTR_PATH_NOT_EDGE;
4657 list<SMESH_MeshEditor_PathPoint> fullList;
4659 const TopoDS_Shape& aS = theTrack->GetSubShape();
4660 // Sub shape for the Pattern must be an Edge or Wire
4661 if( aS.ShapeType() == TopAbs_EDGE ) {
4662 aTrackEdge = TopoDS::Edge( aS );
4663 // the Edge must not be degenerated
4664 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4665 return EXTR_BAD_PATH_SHAPE;
4666 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4667 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4668 const SMDS_MeshNode* aN1 = aItN->next();
4669 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4670 const SMDS_MeshNode* aN2 = aItN->next();
4671 // starting node must be aN1 or aN2
4672 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4673 return EXTR_BAD_STARTING_NODE;
4674 aItN = pSubMeshDS->GetNodes();
4675 while ( aItN->more() ) {
4676 const SMDS_MeshNode* pNode = aItN->next();
4677 const SMDS_EdgePosition* pEPos =
4678 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4679 double aT = pEPos->GetUParameter();
4680 aPrms.push_back( aT );
4682 //Extrusion_Error err =
4683 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4685 else if( aS.ShapeType() == TopAbs_WIRE ) {
4686 list< SMESH_subMesh* > LSM;
4687 TopTools_SequenceOfShape Edges;
4688 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4689 while(itSM->more()) {
4690 SMESH_subMesh* SM = itSM->next();
4692 const TopoDS_Shape& aS = SM->GetSubShape();
4695 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4696 int startNid = theN1->GetID();
4697 TColStd_MapOfInteger UsedNums;
4698 int NbEdges = Edges.Length();
4700 for(; i<=NbEdges; i++) {
4702 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4703 for(; itLSM!=LSM.end(); itLSM++) {
4705 if(UsedNums.Contains(k)) continue;
4706 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4707 SMESH_subMesh* locTrack = *itLSM;
4708 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4709 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4710 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4711 const SMDS_MeshNode* aN1 = aItN->next();
4712 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4713 const SMDS_MeshNode* aN2 = aItN->next();
4714 // starting node must be aN1 or aN2
4715 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4716 // 2. Collect parameters on the track edge
4718 aItN = locMeshDS->GetNodes();
4719 while ( aItN->more() ) {
4720 const SMDS_MeshNode* pNode = aItN->next();
4721 const SMDS_EdgePosition* pEPos =
4722 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4723 double aT = pEPos->GetUParameter();
4724 aPrms.push_back( aT );
4726 list<SMESH_MeshEditor_PathPoint> LPP;
4727 //Extrusion_Error err =
4728 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4729 LLPPs.push_back(LPP);
4731 // update startN for search following egde
4732 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4733 else startNid = aN1->GetID();
4737 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4738 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4739 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4740 for(; itPP!=firstList.end(); itPP++) {
4741 fullList.push_back( *itPP );
4743 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4744 fullList.pop_back();
4746 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4747 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4748 itPP = currList.begin();
4749 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4750 gp_Dir D1 = PP1.Tangent();
4751 gp_Dir D2 = PP2.Tangent();
4752 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4753 (D1.Z()+D2.Z())/2 ) );
4754 PP1.SetTangent(Dnew);
4755 fullList.push_back(PP1);
4757 for(; itPP!=firstList.end(); itPP++) {
4758 fullList.push_back( *itPP );
4760 PP1 = fullList.back();
4761 fullList.pop_back();
4763 // if wire not closed
4764 fullList.push_back(PP1);
4768 return EXTR_BAD_PATH_SHAPE;
4771 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4772 theHasRefPoint, theRefPoint, theMakeGroups);
4776 //=======================================================================
4777 //function : ExtrusionAlongTrack
4779 //=======================================================================
4780 SMESH_MeshEditor::Extrusion_Error
4781 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4782 SMESH_Mesh* theTrack,
4783 const SMDS_MeshNode* theN1,
4784 const bool theHasAngles,
4785 list<double>& theAngles,
4786 const bool theLinearVariation,
4787 const bool theHasRefPoint,
4788 const gp_Pnt& theRefPoint,
4789 const bool theMakeGroups)
4791 myLastCreatedElems.Clear();
4792 myLastCreatedNodes.Clear();
4795 std::list<double> aPrms;
4796 TIDSortedElemSet::iterator itElem;
4799 TopoDS_Edge aTrackEdge;
4800 TopoDS_Vertex aV1, aV2;
4802 SMDS_ElemIteratorPtr aItE;
4803 SMDS_NodeIteratorPtr aItN;
4804 SMDSAbs_ElementType aTypeE;
4806 TNodeOfNodeListMap mapNewNodes;
4809 aNbE = theElements.size();
4812 return EXTR_NO_ELEMENTS;
4814 // 1.1 Track Pattern
4817 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4819 aItE = pMeshDS->elementsIterator();
4820 while ( aItE->more() ) {
4821 const SMDS_MeshElement* pE = aItE->next();
4822 aTypeE = pE->GetType();
4823 // Pattern must contain links only
4824 if ( aTypeE != SMDSAbs_Edge )
4825 return EXTR_PATH_NOT_EDGE;
4828 list<SMESH_MeshEditor_PathPoint> fullList;
4830 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4831 // Sub shape for the Pattern must be an Edge or Wire
4832 if( aS.ShapeType() == TopAbs_EDGE ) {
4833 aTrackEdge = TopoDS::Edge( aS );
4834 // the Edge must not be degenerated
4835 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4836 return EXTR_BAD_PATH_SHAPE;
4837 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4838 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4839 const SMDS_MeshNode* aN1 = aItN->next();
4840 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4841 const SMDS_MeshNode* aN2 = aItN->next();
4842 // starting node must be aN1 or aN2
4843 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4844 return EXTR_BAD_STARTING_NODE;
4845 aItN = pMeshDS->nodesIterator();
4846 while ( aItN->more() ) {
4847 const SMDS_MeshNode* pNode = aItN->next();
4848 if( pNode==aN1 || pNode==aN2 ) continue;
4849 const SMDS_EdgePosition* pEPos =
4850 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4851 double aT = pEPos->GetUParameter();
4852 aPrms.push_back( aT );
4854 //Extrusion_Error err =
4855 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4857 else if( aS.ShapeType() == TopAbs_WIRE ) {
4858 list< SMESH_subMesh* > LSM;
4859 TopTools_SequenceOfShape Edges;
4860 TopExp_Explorer eExp(aS, TopAbs_EDGE);
4861 for(; eExp.More(); eExp.Next()) {
4862 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
4863 if( BRep_Tool::Degenerated(E) ) continue;
4864 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
4870 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4871 int startNid = theN1->GetID();
4872 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!=currList.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 : MakeEdgePathPoints
4953 //purpose : auxilary for ExtrusionAlongTrack
4954 //=======================================================================
4955 SMESH_MeshEditor::Extrusion_Error
4956 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
4957 const TopoDS_Edge& aTrackEdge,
4959 list<SMESH_MeshEditor_PathPoint>& LPP)
4961 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
4963 aTolVec2=aTolVec*aTolVec;
4965 TopoDS_Vertex aV1, aV2;
4966 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4967 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
4968 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
4969 // 2. Collect parameters on the track edge
4970 aPrms.push_front( aT1 );
4971 aPrms.push_back( aT2 );
4974 if( FirstIsStart ) {
4985 SMESH_MeshEditor_PathPoint aPP;
4986 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
4987 std::list<double>::iterator aItD = aPrms.begin();
4988 for(; aItD != aPrms.end(); ++aItD) {
4992 aC3D->D1( aT, aP3D, aVec );
4993 aL2 = aVec.SquareMagnitude();
4994 if ( aL2 < aTolVec2 )
4995 return EXTR_CANT_GET_TANGENT;
4996 gp_Dir aTgt( aVec );
4998 aPP.SetTangent( aTgt );
4999 aPP.SetParameter( aT );
5006 //=======================================================================
5007 //function : MakeExtrElements
5008 //purpose : auxilary for ExtrusionAlongTrack
5009 //=======================================================================
5010 SMESH_MeshEditor::Extrusion_Error
5011 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5012 list<SMESH_MeshEditor_PathPoint>& fullList,
5013 const bool theHasAngles,
5014 list<double>& theAngles,
5015 const bool theLinearVariation,
5016 const bool theHasRefPoint,
5017 const gp_Pnt& theRefPoint,
5018 const bool theMakeGroups)
5020 MESSAGE("MakeExtrElements");
5021 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5022 int aNbTP = fullList.size();
5023 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5025 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5026 LinearAngleVariation(aNbTP-1, theAngles);
5028 vector<double> aAngles( aNbTP );
5030 for(; j<aNbTP; ++j) {
5033 if ( theHasAngles ) {
5035 std::list<double>::iterator aItD = theAngles.begin();
5036 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5038 aAngles[j] = anAngle;
5041 // fill vector of path points with angles
5042 //aPPs.resize(fullList.size());
5044 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5045 for(; itPP!=fullList.end(); itPP++) {
5047 SMESH_MeshEditor_PathPoint PP = *itPP;
5048 PP.SetAngle(aAngles[j]);
5052 TNodeOfNodeListMap mapNewNodes;
5053 TElemOfVecOfNnlmiMap mapElemNewNodes;
5054 TElemOfElemListMap newElemsMap;
5055 TIDSortedElemSet::iterator itElem;
5058 SMDSAbs_ElementType aTypeE;
5059 // source elements for each generated one
5060 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5062 // 3. Center of rotation aV0
5063 gp_Pnt aV0 = theRefPoint;
5065 if ( !theHasRefPoint ) {
5067 aGC.SetCoord( 0.,0.,0. );
5069 itElem = theElements.begin();
5070 for ( ; itElem != theElements.end(); itElem++ ) {
5071 const SMDS_MeshElement* elem = *itElem;
5073 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5074 while ( itN->more() ) {
5075 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5080 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5081 list<const SMDS_MeshNode*> aLNx;
5082 mapNewNodes[node] = aLNx;
5084 gp_XYZ aXYZ( aX, aY, aZ );
5092 } // if (!theHasRefPoint) {
5093 mapNewNodes.clear();
5095 // 4. Processing the elements
5096 SMESHDS_Mesh* aMesh = GetMeshDS();
5098 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5099 // check element type
5100 const SMDS_MeshElement* elem = *itElem;
5101 aTypeE = elem->GetType();
5102 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5105 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5106 newNodesItVec.reserve( elem->NbNodes() );
5108 // loop on elem nodes
5110 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5111 while ( itN->more() )
5114 // check if a node has been already processed
5115 const SMDS_MeshNode* node =
5116 static_cast<const SMDS_MeshNode*>( itN->next() );
5117 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5118 if ( nIt == mapNewNodes.end() ) {
5119 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5120 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5123 aX = node->X(); aY = node->Y(); aZ = node->Z();
5125 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5126 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5127 gp_Ax1 anAx1, anAxT1T0;
5128 gp_Dir aDT1x, aDT0x, aDT1T0;
5133 aPN0.SetCoord(aX, aY, aZ);
5135 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5137 aDT0x= aPP0.Tangent();
5138 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5140 for ( j = 1; j < aNbTP; ++j ) {
5141 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5143 aDT1x = aPP1.Tangent();
5144 aAngle1x = aPP1.Angle();
5146 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5148 gp_Vec aV01x( aP0x, aP1x );
5149 aTrsf.SetTranslation( aV01x );
5152 aV1x = aV0x.Transformed( aTrsf );
5153 aPN1 = aPN0.Transformed( aTrsf );
5155 // rotation 1 [ T1,T0 ]
5156 aAngleT1T0=-aDT1x.Angle( aDT0x );
5157 if (fabs(aAngleT1T0) > aTolAng) {
5159 anAxT1T0.SetLocation( aV1x );
5160 anAxT1T0.SetDirection( aDT1T0 );
5161 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5163 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5167 if ( theHasAngles ) {
5168 anAx1.SetLocation( aV1x );
5169 anAx1.SetDirection( aDT1x );
5170 aTrsfRot.SetRotation( anAx1, aAngle1x );
5172 aPN1 = aPN1.Transformed( aTrsfRot );
5176 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5177 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5178 // create additional node
5179 double x = ( aPN1.X() + aPN0.X() )/2.;
5180 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5181 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5182 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5183 myLastCreatedNodes.Append(newNode);
5184 srcNodes.Append( node );
5185 listNewNodes.push_back( newNode );
5190 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5191 myLastCreatedNodes.Append(newNode);
5192 srcNodes.Append( node );
5193 listNewNodes.push_back( newNode );
5203 // if current elem is quadratic and current node is not medium
5204 // we have to check - may be it is needed to insert additional nodes
5205 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5206 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5207 if(listNewNodes.size()==aNbTP-1) {
5208 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5209 gp_XYZ P(node->X(), node->Y(), node->Z());
5210 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5212 for(i=0; i<aNbTP-1; i++) {
5213 const SMDS_MeshNode* N = *it;
5214 double x = ( N->X() + P.X() )/2.;
5215 double y = ( N->Y() + P.Y() )/2.;
5216 double z = ( N->Z() + P.Z() )/2.;
5217 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5218 srcNodes.Append( node );
5219 myLastCreatedNodes.Append(newN);
5222 P = gp_XYZ(N->X(),N->Y(),N->Z());
5224 listNewNodes.clear();
5225 for(i=0; i<2*(aNbTP-1); i++) {
5226 listNewNodes.push_back(aNodes[i]);
5232 newNodesItVec.push_back( nIt );
5234 // make new elements
5235 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5236 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5237 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5240 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5242 if ( theMakeGroups )
5243 generateGroups( srcNodes, srcElems, "extruded");
5249 //=======================================================================
5250 //function : LinearAngleVariation
5251 //purpose : auxilary for ExtrusionAlongTrack
5252 //=======================================================================
5253 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5254 list<double>& Angles)
5256 int nbAngles = Angles.size();
5257 if( nbSteps > nbAngles ) {
5258 vector<double> theAngles(nbAngles);
5259 list<double>::iterator it = Angles.begin();
5261 for(; it!=Angles.end(); it++) {
5263 theAngles[i] = (*it);
5266 double rAn2St = double( nbAngles ) / double( nbSteps );
5267 double angPrev = 0, angle;
5268 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5269 double angCur = rAn2St * ( iSt+1 );
5270 double angCurFloor = floor( angCur );
5271 double angPrevFloor = floor( angPrev );
5272 if ( angPrevFloor == angCurFloor )
5273 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5275 int iP = int( angPrevFloor );
5276 double angPrevCeil = ceil(angPrev);
5277 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5279 int iC = int( angCurFloor );
5280 if ( iC < nbAngles )
5281 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5283 iP = int( angPrevCeil );
5285 angle += theAngles[ iC ];
5287 res.push_back(angle);
5292 for(; it!=res.end(); it++)
5293 Angles.push_back( *it );
5298 //================================================================================
5300 * \brief Move or copy theElements applying theTrsf to their nodes
5301 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5302 * \param theTrsf - transformation to apply
5303 * \param theCopy - if true, create translated copies of theElems
5304 * \param theMakeGroups - if true and theCopy, create translated groups
5305 * \param theTargetMesh - mesh to copy translated elements into
5306 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5308 //================================================================================
5310 SMESH_MeshEditor::PGroupIDs
5311 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5312 const gp_Trsf& theTrsf,
5314 const bool theMakeGroups,
5315 SMESH_Mesh* theTargetMesh)
5317 myLastCreatedElems.Clear();
5318 myLastCreatedNodes.Clear();
5320 bool needReverse = false;
5321 string groupPostfix;
5322 switch ( theTrsf.Form() ) {
5324 MESSAGE("gp_PntMirror");
5326 groupPostfix = "mirrored";
5329 MESSAGE("gp_Ax1Mirror");
5330 groupPostfix = "mirrored";
5333 MESSAGE("gp_Ax2Mirror");
5335 groupPostfix = "mirrored";
5338 MESSAGE("gp_Rotation");
5339 groupPostfix = "rotated";
5341 case gp_Translation:
5342 MESSAGE("gp_Translation");
5343 groupPostfix = "translated";
5346 MESSAGE("gp_Scale");
5347 groupPostfix = "scaled";
5349 case gp_CompoundTrsf: // different scale by axis
5350 MESSAGE("gp_CompoundTrsf");
5351 groupPostfix = "scaled";
5355 needReverse = false;
5356 groupPostfix = "transformed";
5359 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5360 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5361 SMESHDS_Mesh* aMesh = GetMeshDS();
5364 // map old node to new one
5365 TNodeNodeMap nodeMap;
5367 // elements sharing moved nodes; those of them which have all
5368 // nodes mirrored but are not in theElems are to be reversed
5369 TIDSortedElemSet inverseElemSet;
5371 // source elements for each generated one
5372 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5374 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5375 TIDSortedElemSet orphanNode;
5377 if ( theElems.empty() ) // transform the whole mesh
5380 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5381 while ( eIt->more() ) theElems.insert( eIt->next() );
5383 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5384 while ( nIt->more() )
5386 const SMDS_MeshNode* node = nIt->next();
5387 if ( node->NbInverseElements() == 0)
5388 orphanNode.insert( node );
5392 // loop on elements to transform nodes : first orphan nodes then elems
5393 TIDSortedElemSet::iterator itElem;
5394 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5395 for (int i=0; i<2; i++)
5396 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5397 const SMDS_MeshElement* elem = *itElem;
5401 // loop on elem nodes
5402 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5403 while ( itN->more() ) {
5405 const SMDS_MeshNode* node = cast2Node( itN->next() );
5406 // check if a node has been already transformed
5407 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5408 nodeMap.insert( make_pair ( node, node ));
5409 if ( !n2n_isnew.second )
5413 coord[0] = node->X();
5414 coord[1] = node->Y();
5415 coord[2] = node->Z();
5416 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5417 if ( theTargetMesh ) {
5418 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5419 n2n_isnew.first->second = newNode;
5420 myLastCreatedNodes.Append(newNode);
5421 srcNodes.Append( node );
5423 else if ( theCopy ) {
5424 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5425 n2n_isnew.first->second = newNode;
5426 myLastCreatedNodes.Append(newNode);
5427 srcNodes.Append( node );
5430 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5431 // node position on shape becomes invalid
5432 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5433 ( SMDS_SpacePosition::originSpacePosition() );
5436 // keep inverse elements
5437 if ( !theCopy && !theTargetMesh && needReverse ) {
5438 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5439 while ( invElemIt->more() ) {
5440 const SMDS_MeshElement* iel = invElemIt->next();
5441 inverseElemSet.insert( iel );
5447 // either create new elements or reverse mirrored ones
5448 if ( !theCopy && !needReverse && !theTargetMesh )
5451 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5452 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5453 theElems.insert( *invElemIt );
5455 // replicate or reverse elements
5456 // TODO revoir ordre reverse vtk
5458 REV_TETRA = 0, // = nbNodes - 4
5459 REV_PYRAMID = 1, // = nbNodes - 4
5460 REV_PENTA = 2, // = nbNodes - 4
5462 REV_HEXA = 4, // = nbNodes - 4
5466 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA
5467 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID
5468 { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA
5469 { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE
5470 { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA
5471 { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD
5474 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5476 const SMDS_MeshElement* elem = *itElem;
5477 if ( !elem || elem->GetType() == SMDSAbs_Node )
5480 int nbNodes = elem->NbNodes();
5481 int elemType = elem->GetType();
5483 if (elem->IsPoly()) {
5484 // Polygon or Polyhedral Volume
5485 switch ( elemType ) {
5488 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5490 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5491 while (itN->more()) {
5492 const SMDS_MeshNode* node =
5493 static_cast<const SMDS_MeshNode*>(itN->next());
5494 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5495 if (nodeMapIt == nodeMap.end())
5496 break; // not all nodes transformed
5498 // reverse mirrored faces and volumes
5499 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5501 poly_nodes[iNode] = (*nodeMapIt).second;
5505 if ( iNode != nbNodes )
5506 continue; // not all nodes transformed
5508 if ( theTargetMesh ) {
5509 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5510 srcElems.Append( elem );
5512 else if ( theCopy ) {
5513 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5514 srcElems.Append( elem );
5517 aMesh->ChangePolygonNodes(elem, poly_nodes);
5521 case SMDSAbs_Volume:
5523 // ATTENTION: Reversing is not yet done!!!
5524 const SMDS_VtkVolume* aPolyedre =
5525 dynamic_cast<const SMDS_VtkVolume*>( elem );
5527 MESSAGE("Warning: bad volumic element");
5531 vector<const SMDS_MeshNode*> poly_nodes;
5532 vector<int> quantities;
5534 bool allTransformed = true;
5535 int nbFaces = aPolyedre->NbFaces();
5536 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5537 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5538 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5539 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5540 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5541 if (nodeMapIt == nodeMap.end()) {
5542 allTransformed = false; // not all nodes transformed
5544 poly_nodes.push_back((*nodeMapIt).second);
5547 quantities.push_back(nbFaceNodes);
5549 if ( !allTransformed )
5550 continue; // not all nodes transformed
5552 if ( theTargetMesh ) {
5553 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5554 srcElems.Append( elem );
5556 else if ( theCopy ) {
5557 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5558 srcElems.Append( elem );
5561 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5571 int* i = index[ FORWARD ];
5572 if ( needReverse && nbNodes > 2) {// reverse mirrored faces and volumes
5573 if ( elemType == SMDSAbs_Face )
5574 i = index[ REV_FACE ];
5576 i = index[ nbNodes - 4 ];
5578 if(elem->IsQuadratic()) {
5579 static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
5582 if(nbNodes==3) { // quadratic edge
5583 static int anIds[] = {1,0,2};
5586 else if(nbNodes==6) { // quadratic triangle
5587 static int anIds[] = {0,2,1,5,4,3};
5590 else if(nbNodes==8) { // quadratic quadrangle
5591 static int anIds[] = {0,3,2,1,7,6,5,4};
5594 else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes
5595 static int anIds[] = {0,2,1,3,6,5,4,7,9,8};
5598 else if(nbNodes==13) { // quadratic pyramid of 13 nodes
5599 static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10};
5602 else if(nbNodes==15) { // quadratic pentahedron with 15 nodes
5603 static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13};
5606 else { // nbNodes==20 - quadratic hexahedron with 20 nodes
5607 static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17};
5613 // find transformed nodes
5614 vector<const SMDS_MeshNode*> nodes(nbNodes);
5616 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5617 while ( itN->more() ) {
5618 const SMDS_MeshNode* node =
5619 static_cast<const SMDS_MeshNode*>( itN->next() );
5620 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5621 if ( nodeMapIt == nodeMap.end() )
5622 break; // not all nodes transformed
5623 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5625 if ( iNode != nbNodes )
5626 continue; // not all nodes transformed
5628 if ( theTargetMesh ) {
5629 if ( SMDS_MeshElement* copy =
5630 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5631 myLastCreatedElems.Append( copy );
5632 srcElems.Append( elem );
5635 else if ( theCopy ) {
5636 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5637 srcElems.Append( elem );
5640 // reverse element as it was reversed by transformation
5642 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5646 PGroupIDs newGroupIDs;
5648 if ( ( theMakeGroups && theCopy ) ||
5649 ( theMakeGroups && theTargetMesh ) )
5650 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5656 ////=======================================================================
5657 ////function : Scale
5659 ////=======================================================================
5661 //SMESH_MeshEditor::PGroupIDs
5662 //SMESH_MeshEditor::Scale (TIDSortedElemSet & theElems,
5663 // const gp_Pnt& thePoint,
5664 // const std::list<double>& theScaleFact,
5665 // const bool theCopy,
5666 // const bool theMakeGroups,
5667 // SMESH_Mesh* theTargetMesh)
5669 // MESSAGE("Scale");
5670 // myLastCreatedElems.Clear();
5671 // myLastCreatedNodes.Clear();
5673 // SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5674 // SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5675 // SMESHDS_Mesh* aMesh = GetMeshDS();
5677 // double scaleX=1.0, scaleY=1.0, scaleZ=1.0;
5678 // std::list<double>::const_iterator itS = theScaleFact.begin();
5680 // if(theScaleFact.size()==1) {
5684 // if(theScaleFact.size()==2) {
5689 // if(theScaleFact.size()>2) {
5696 // // map old node to new one
5697 // TNodeNodeMap nodeMap;
5699 // // elements sharing moved nodes; those of them which have all
5700 // // nodes mirrored but are not in theElems are to be reversed
5701 // TIDSortedElemSet inverseElemSet;
5703 // // source elements for each generated one
5704 // SMESH_SequenceOfElemPtr srcElems, srcNodes;
5706 // // loop on theElems
5707 // TIDSortedElemSet::iterator itElem;
5708 // for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5709 // const SMDS_MeshElement* elem = *itElem;
5713 // // loop on elem nodes
5714 // SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5715 // while ( itN->more() ) {
5717 // // check if a node has been already transformed
5718 // const SMDS_MeshNode* node = cast2Node( itN->next() );
5719 // pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5720 // nodeMap.insert( make_pair ( node, node ));
5721 // if ( !n2n_isnew.second )
5724 // //double coord[3];
5725 // //coord[0] = node->X();
5726 // //coord[1] = node->Y();
5727 // //coord[2] = node->Z();
5728 // //theTrsf.Transforms( coord[0], coord[1], coord[2] );
5729 // double dx = (node->X() - thePoint.X()) * scaleX;
5730 // double dy = (node->Y() - thePoint.Y()) * scaleY;
5731 // double dz = (node->Z() - thePoint.Z()) * scaleZ;
5732 // if ( theTargetMesh ) {
5733 // //const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5734 // const SMDS_MeshNode * newNode =
5735 // aTgtMesh->AddNode( thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz );
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 // const SMDS_MeshNode * newNode =
5743 // aMesh->AddNode( thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz );
5744 // n2n_isnew.first->second = newNode;
5745 // myLastCreatedNodes.Append(newNode);
5746 // srcNodes.Append( node );
5749 // //aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5750 // aMesh->MoveNode( node, thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz );
5751 // // node position on shape becomes invalid
5752 // const_cast< SMDS_MeshNode* > ( node )->SetPosition
5753 // ( SMDS_SpacePosition::originSpacePosition() );
5756 // // keep inverse elements
5757 // //if ( !theCopy && !theTargetMesh && needReverse ) {
5758 // // SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5759 // // while ( invElemIt->more() ) {
5760 // // const SMDS_MeshElement* iel = invElemIt->next();
5761 // // inverseElemSet.insert( iel );
5767 // // either create new elements or reverse mirrored ones
5768 // //if ( !theCopy && !needReverse && !theTargetMesh )
5769 // if ( !theCopy && !theTargetMesh )
5770 // return PGroupIDs();
5772 // TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5773 // for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5774 // theElems.insert( *invElemIt );
5776 // // replicate or reverse elements
5779 // REV_TETRA = 0, // = nbNodes - 4
5780 // REV_PYRAMID = 1, // = nbNodes - 4
5781 // REV_PENTA = 2, // = nbNodes - 4
5783 // REV_HEXA = 4, // = nbNodes - 4
5786 // int index[][8] = {
5787 // { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA
5788 // { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID
5789 // { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA
5790 // { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE
5791 // { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA
5792 // { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD
5795 // for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5797 // const SMDS_MeshElement* elem = *itElem;
5798 // if ( !elem || elem->GetType() == SMDSAbs_Node )
5801 // int nbNodes = elem->NbNodes();
5802 // int elemType = elem->GetType();
5804 // if (elem->IsPoly()) {
5805 // // Polygon or Polyhedral Volume
5806 // switch ( elemType ) {
5807 // case SMDSAbs_Face:
5809 // vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5811 // SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5812 // while (itN->more()) {
5813 // const SMDS_MeshNode* node =
5814 // static_cast<const SMDS_MeshNode*>(itN->next());
5815 // TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5816 // if (nodeMapIt == nodeMap.end())
5817 // break; // not all nodes transformed
5818 // //if (needReverse) {
5819 // // // reverse mirrored faces and volumes
5820 // // poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5822 // poly_nodes[iNode] = (*nodeMapIt).second;
5826 // if ( iNode != nbNodes )
5827 // continue; // not all nodes transformed
5829 // if ( theTargetMesh ) {
5830 // myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5831 // srcElems.Append( elem );
5833 // else if ( theCopy ) {
5834 // myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5835 // srcElems.Append( elem );
5838 // aMesh->ChangePolygonNodes(elem, poly_nodes);
5842 // case SMDSAbs_Volume:
5844 // // ATTENTION: Reversing is not yet done!!!
5845 // const SMDS_VtkVolume* aPolyedre =
5846 // dynamic_cast<const SMDS_VtkVolume*>( elem );
5847 // if (!aPolyedre) {
5848 // MESSAGE("Warning: bad volumic element");
5852 // vector<const SMDS_MeshNode*> poly_nodes;
5853 // vector<int> quantities;
5855 // bool allTransformed = true;
5856 // int nbFaces = aPolyedre->NbFaces();
5857 // for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5858 // int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5859 // for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5860 // const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5861 // TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5862 // if (nodeMapIt == nodeMap.end()) {
5863 // allTransformed = false; // not all nodes transformed
5865 // poly_nodes.push_back((*nodeMapIt).second);
5868 // quantities.push_back(nbFaceNodes);
5870 // if ( !allTransformed )
5871 // continue; // not all nodes transformed
5873 // if ( theTargetMesh ) {
5874 // myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5875 // srcElems.Append( elem );
5877 // else if ( theCopy ) {
5878 // myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5879 // srcElems.Append( elem );
5882 // aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5891 // // Regular elements
5892 // int* i = index[ FORWARD ];
5893 // //if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes
5894 // // if ( elemType == SMDSAbs_Face )
5895 // // i = index[ REV_FACE ];
5897 // // i = index[ nbNodes - 4 ];
5899 // if(elem->IsQuadratic()) {
5900 // static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
5902 // //if(needReverse) {
5903 // // if(nbNodes==3) { // quadratic edge
5904 // // static int anIds[] = {1,0,2};
5907 // // else if(nbNodes==6) { // quadratic triangle
5908 // // static int anIds[] = {0,2,1,5,4,3};
5911 // // else if(nbNodes==8) { // quadratic quadrangle
5912 // // static int anIds[] = {0,3,2,1,7,6,5,4};
5915 // // else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes
5916 // // static int anIds[] = {0,2,1,3,6,5,4,7,9,8};
5919 // // else if(nbNodes==13) { // quadratic pyramid of 13 nodes
5920 // // static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10};
5923 // // else if(nbNodes==15) { // quadratic pentahedron with 15 nodes
5924 // // static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13};
5927 // // else { // nbNodes==20 - quadratic hexahedron with 20 nodes
5928 // // static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17};
5934 // // find transformed nodes
5935 // vector<const SMDS_MeshNode*> nodes(nbNodes);
5937 // SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5938 // while ( itN->more() ) {
5939 // const SMDS_MeshNode* node =
5940 // static_cast<const SMDS_MeshNode*>( itN->next() );
5941 // TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5942 // if ( nodeMapIt == nodeMap.end() )
5943 // break; // not all nodes transformed
5944 // nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5946 // if ( iNode != nbNodes )
5947 // continue; // not all nodes transformed
5949 // if ( theTargetMesh ) {
5950 // if ( SMDS_MeshElement* copy =
5951 // targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5952 // myLastCreatedElems.Append( copy );
5953 // srcElems.Append( elem );
5956 // else if ( theCopy ) {
5957 // if ( SMDS_MeshElement* copy = AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5958 // myLastCreatedElems.Append( copy );
5959 // srcElems.Append( elem );
5963 // // reverse element as it was reversed by transformation
5964 // if ( nbNodes > 2 )
5965 // aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5969 // PGroupIDs newGroupIDs;
5971 // if ( theMakeGroups && theCopy ||
5972 // theMakeGroups && theTargetMesh ) {
5973 // string groupPostfix = "scaled";
5974 // newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5977 // return newGroupIDs;
5981 //=======================================================================
5983 * \brief Create groups of elements made during transformation
5984 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5985 * \param elemGens - elements making corresponding myLastCreatedElems
5986 * \param postfix - to append to names of new groups
5988 //=======================================================================
5990 SMESH_MeshEditor::PGroupIDs
5991 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5992 const SMESH_SequenceOfElemPtr& elemGens,
5993 const std::string& postfix,
5994 SMESH_Mesh* targetMesh)
5996 PGroupIDs newGroupIDs( new list<int> );
5997 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5999 // Sort existing groups by types and collect their names
6001 // to store an old group and a generated new one
6002 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
6003 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6005 set< string > groupNames;
6007 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
6008 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6009 while ( groupIt->more() ) {
6010 SMESH_Group * group = groupIt->next();
6011 if ( !group ) continue;
6012 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6013 if ( !groupDS || groupDS->IsEmpty() ) continue;
6014 groupNames.insert( group->GetName() );
6015 groupDS->SetStoreName( group->GetName() );
6016 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
6021 // loop on nodes and elements
6022 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6024 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6025 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6026 if ( gens.Length() != elems.Length() )
6027 throw SALOME_Exception(LOCALIZED("invalid args"));
6029 // loop on created elements
6030 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6032 const SMDS_MeshElement* sourceElem = gens( iElem );
6033 if ( !sourceElem ) {
6034 MESSAGE("generateGroups(): NULL source element");
6037 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6038 if ( groupsOldNew.empty() ) {
6039 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6040 ++iElem; // skip all elements made by sourceElem
6043 // collect all elements made by sourceElem
6044 list< const SMDS_MeshElement* > resultElems;
6045 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6046 if ( resElem != sourceElem )
6047 resultElems.push_back( resElem );
6048 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6049 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6050 if ( resElem != sourceElem )
6051 resultElems.push_back( resElem );
6052 // do not generate element groups from node ones
6053 if ( sourceElem->GetType() == SMDSAbs_Node &&
6054 elems( iElem )->GetType() != SMDSAbs_Node )
6057 // add resultElems to groups made by ones the sourceElem belongs to
6058 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6059 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6061 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6062 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
6064 SMDS_MeshGroup* & newGroup = gOldNew->second;
6065 if ( !newGroup )// create a new group
6068 string name = oldGroup->GetStoreName();
6069 if ( !targetMesh ) {
6073 while ( !groupNames.insert( name ).second ) // name exists
6079 TCollection_AsciiString nbStr(nb+1);
6080 name.resize( name.rfind('_')+1 );
6081 name += nbStr.ToCString();
6088 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
6090 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
6091 newGroup = & groupDS->SMDSGroup();
6092 newGroupIDs->push_back( id );
6095 // fill in a new group
6096 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6097 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6098 newGroup->Add( *resElemIt );
6101 } // loop on created elements
6102 }// loop on nodes and elements
6107 //================================================================================
6109 * \brief Return list of group of nodes close to each other within theTolerance
6110 * Search among theNodes or in the whole mesh if theNodes is empty using
6111 * an Octree algorithm
6113 //================================================================================
6115 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6116 const double theTolerance,
6117 TListOfListOfNodes & theGroupsOfNodes)
6119 myLastCreatedElems.Clear();
6120 myLastCreatedNodes.Clear();
6122 if ( theNodes.empty() )
6123 { // get all nodes in the mesh
6124 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6125 while ( nIt->more() )
6126 theNodes.insert( theNodes.end(),nIt->next());
6129 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6133 //=======================================================================
6135 * \brief Implementation of search for the node closest to point
6137 //=======================================================================
6139 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6141 //---------------------------------------------------------------------
6143 * \brief Constructor
6145 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6147 myMesh = ( SMESHDS_Mesh* ) theMesh;
6149 TIDSortedNodeSet nodes;
6151 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6152 while ( nIt->more() )
6153 nodes.insert( nodes.end(), nIt->next() );
6155 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6157 // get max size of a leaf box
6158 SMESH_OctreeNode* tree = myOctreeNode;
6159 while ( !tree->isLeaf() )
6161 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6165 myHalfLeafSize = tree->maxSize() / 2.;
6168 //---------------------------------------------------------------------
6170 * \brief Move node and update myOctreeNode accordingly
6172 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6174 myOctreeNode->UpdateByMoveNode( node, toPnt );
6175 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6178 //---------------------------------------------------------------------
6180 * \brief Do it's job
6182 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6184 map<double, const SMDS_MeshNode*> dist2Nodes;
6185 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6186 if ( !dist2Nodes.empty() )
6187 return dist2Nodes.begin()->second;
6188 list<const SMDS_MeshNode*> nodes;
6189 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6191 double minSqDist = DBL_MAX;
6192 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6194 // sort leafs by their distance from thePnt
6195 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6196 TDistTreeMap treeMap;
6197 list< SMESH_OctreeNode* > treeList;
6198 list< SMESH_OctreeNode* >::iterator trIt;
6199 treeList.push_back( myOctreeNode );
6201 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6202 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6203 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6205 SMESH_OctreeNode* tree = *trIt;
6206 if ( !tree->isLeaf() ) // put children to the queue
6208 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6209 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6210 while ( cIt->more() )
6211 treeList.push_back( cIt->next() );
6213 else if ( tree->NbNodes() ) // put a tree to the treeMap
6215 const Bnd_B3d& box = tree->getBox();
6216 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6217 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6218 if ( !it_in.second ) // not unique distance to box center
6219 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6222 // find distance after which there is no sense to check tree's
6223 double sqLimit = DBL_MAX;
6224 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6225 if ( treeMap.size() > 5 ) {
6226 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6227 const Bnd_B3d& box = closestTree->getBox();
6228 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6229 sqLimit = limit * limit;
6231 // get all nodes from trees
6232 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6233 if ( sqDist_tree->first > sqLimit )
6235 SMESH_OctreeNode* tree = sqDist_tree->second;
6236 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6239 // find closest among nodes
6240 minSqDist = DBL_MAX;
6241 const SMDS_MeshNode* closestNode = 0;
6242 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6243 for ( ; nIt != nodes.end(); ++nIt ) {
6244 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6245 if ( minSqDist > sqDist ) {
6253 //---------------------------------------------------------------------
6257 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6259 //---------------------------------------------------------------------
6261 * \brief Return the node tree
6263 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6266 SMESH_OctreeNode* myOctreeNode;
6267 SMESHDS_Mesh* myMesh;
6268 double myHalfLeafSize; // max size of a leaf box
6271 //=======================================================================
6273 * \brief Return SMESH_NodeSearcher
6275 //=======================================================================
6277 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6279 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6282 // ========================================================================
6283 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6285 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6286 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6287 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6289 //=======================================================================
6291 * \brief Octal tree of bounding boxes of elements
6293 //=======================================================================
6295 class ElementBndBoxTree : public SMESH_Octree
6299 ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(), double tolerance = NodeRadius );
6300 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
6301 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6302 ~ElementBndBoxTree();
6305 ElementBndBoxTree() {}
6306 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6307 void buildChildrenData();
6308 Bnd_B3d* buildRootBox();
6310 //!< Bounding box of element
6311 struct ElementBox : public Bnd_B3d
6313 const SMDS_MeshElement* _element;
6314 int _refCount; // an ElementBox can be included in several tree branches
6315 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6317 vector< ElementBox* > _elements;
6320 //================================================================================
6322 * \brief ElementBndBoxTree creation
6324 //================================================================================
6326 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6327 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6329 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6330 _elements.reserve( nbElems );
6332 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6333 while ( elemIt->more() )
6334 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6336 if ( _elements.size() > MaxNbElemsInLeaf )
6342 //================================================================================
6346 //================================================================================
6348 ElementBndBoxTree::~ElementBndBoxTree()
6350 for ( int i = 0; i < _elements.size(); ++i )
6351 if ( --_elements[i]->_refCount <= 0 )
6352 delete _elements[i];
6355 //================================================================================
6357 * \brief Return the maximal box
6359 //================================================================================
6361 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6363 Bnd_B3d* box = new Bnd_B3d;
6364 for ( int i = 0; i < _elements.size(); ++i )
6365 box->Add( *_elements[i] );
6369 //================================================================================
6371 * \brief Redistrubute element boxes among children
6373 //================================================================================
6375 void ElementBndBoxTree::buildChildrenData()
6377 for ( int i = 0; i < _elements.size(); ++i )
6379 for (int j = 0; j < 8; j++)
6381 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6383 _elements[i]->_refCount++;
6384 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6387 _elements[i]->_refCount--;
6391 for (int j = 0; j < 8; j++)
6393 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6394 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6395 child->myIsLeaf = true;
6397 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6398 child->_elements.resize( child->_elements.size() ); // compact
6402 //================================================================================
6404 * \brief Return elements which can include the point
6406 //================================================================================
6408 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6409 TIDSortedElemSet& foundElems)
6411 if ( level() && getBox().IsOut( point.XYZ() ))
6416 for ( int i = 0; i < _elements.size(); ++i )
6417 if ( !_elements[i]->IsOut( point.XYZ() ))
6418 foundElems.insert( _elements[i]->_element );
6422 for (int i = 0; i < 8; i++)
6423 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6427 //================================================================================
6429 * \brief Return elements which can be intersected by the line
6431 //================================================================================
6433 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6434 TIDSortedElemSet& foundElems)
6436 if ( level() && getBox().IsOut( line ))
6441 for ( int i = 0; i < _elements.size(); ++i )
6442 if ( !_elements[i]->IsOut( line ))
6443 foundElems.insert( _elements[i]->_element );
6447 for (int i = 0; i < 8; i++)
6448 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6452 //================================================================================
6454 * \brief Construct the element box
6456 //================================================================================
6458 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6462 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6463 while ( nIt->more() )
6464 Add( SMESH_TNodeXYZ( cast2Node( nIt->next() )));
6465 Enlarge( tolerance );
6470 //=======================================================================
6472 * \brief Implementation of search for the elements by point and
6473 * of classification of point in 2D mesh
6475 //=======================================================================
6477 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6479 SMESHDS_Mesh* _mesh;
6480 SMDS_ElemIteratorPtr _meshPartIt;
6481 ElementBndBoxTree* _ebbTree;
6482 SMESH_NodeSearcherImpl* _nodeSearcher;
6483 SMDSAbs_ElementType _elementType;
6485 bool _outerFacesFound;
6486 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6488 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6489 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6490 ~SMESH_ElementSearcherImpl()
6492 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6493 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6495 virtual int FindElementsByPoint(const gp_Pnt& point,
6496 SMDSAbs_ElementType type,
6497 vector< const SMDS_MeshElement* >& foundElements);
6498 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6500 void GetElementsNearLine( const gp_Ax1& line,
6501 SMDSAbs_ElementType type,
6502 vector< const SMDS_MeshElement* >& foundElems);
6503 double getTolerance();
6504 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6505 const double tolerance, double & param);
6506 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6507 bool isOuterBoundary(const SMDS_MeshElement* face) const
6509 return _outerFaces.empty() || _outerFaces.count(face);
6511 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6513 const SMDS_MeshElement* _face;
6515 bool _coincides; //!< the line lays in face plane
6516 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6517 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6519 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6522 TIDSortedElemSet _faces;
6523 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6524 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6528 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6530 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6531 << ", _coincides="<<i._coincides << ")";
6534 //=======================================================================
6536 * \brief define tolerance for search
6538 //=======================================================================
6540 double SMESH_ElementSearcherImpl::getTolerance()
6542 if ( _tolerance < 0 )
6544 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6547 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6549 double boxSize = _nodeSearcher->getTree()->maxSize();
6550 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6552 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6554 double boxSize = _ebbTree->maxSize();
6555 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6557 if ( _tolerance == 0 )
6559 // define tolerance by size of a most complex element
6560 int complexType = SMDSAbs_Volume;
6561 while ( complexType > SMDSAbs_All &&
6562 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6564 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6566 if ( complexType == int( SMDSAbs_Node ))
6568 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6570 if ( meshInfo.NbNodes() > 2 )
6571 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6575 SMDS_ElemIteratorPtr elemIt =
6576 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6577 const SMDS_MeshElement* elem = elemIt->next();
6578 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6579 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6581 while ( nodeIt->more() )
6583 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6584 elemSize = max( dist, elemSize );
6587 _tolerance = 1e-4 * elemSize;
6593 //================================================================================
6595 * \brief Find intersection of the line and an edge of face and return parameter on line
6597 //================================================================================
6599 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6600 const SMDS_MeshElement* face,
6607 GeomAPI_ExtremaCurveCurve anExtCC;
6608 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6610 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6611 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6613 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6614 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6615 anExtCC.Init( lineCurve, edge);
6616 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6618 Quantity_Parameter pl, pe;
6619 anExtCC.LowerDistanceParameters( pl, pe );
6621 if ( ++nbInts == 2 )
6625 if ( nbInts > 0 ) param /= nbInts;
6628 //================================================================================
6630 * \brief Find all faces belonging to the outer boundary of mesh
6632 //================================================================================
6634 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6636 if ( _outerFacesFound ) return;
6638 // Collect all outer faces by passing from one outer face to another via their links
6639 // and BTW find out if there are internal faces at all.
6641 // checked links and links where outer boundary meets internal one
6642 set< SMESH_TLink > visitedLinks, seamLinks;
6644 // links to treat with already visited faces sharing them
6645 list < TFaceLink > startLinks;
6647 // load startLinks with the first outerFace
6648 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6649 _outerFaces.insert( outerFace );
6651 TIDSortedElemSet emptySet;
6652 while ( !startLinks.empty() )
6654 const SMESH_TLink& link = startLinks.front()._link;
6655 TIDSortedElemSet& faces = startLinks.front()._faces;
6657 outerFace = *faces.begin();
6658 // find other faces sharing the link
6659 const SMDS_MeshElement* f;
6660 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6663 // select another outer face among the found
6664 const SMDS_MeshElement* outerFace2 = 0;
6665 if ( faces.size() == 2 )
6667 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6669 else if ( faces.size() > 2 )
6671 seamLinks.insert( link );
6673 // link direction within the outerFace
6674 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6675 SMESH_TNodeXYZ( link.node2()));
6676 int i1 = outerFace->GetNodeIndex( link.node1() );
6677 int i2 = outerFace->GetNodeIndex( link.node2() );
6678 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6679 if ( rev ) n1n2.Reverse();
6681 gp_XYZ ofNorm, fNorm;
6682 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6684 // direction from the link inside outerFace
6685 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6686 // sort all other faces by angle with the dirInOF
6687 map< double, const SMDS_MeshElement* > angle2Face;
6688 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6689 for ( ; face != faces.end(); ++face )
6691 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6693 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6694 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6695 if ( angle < 0 ) angle += 2*PI;
6696 angle2Face.insert( make_pair( angle, *face ));
6698 if ( !angle2Face.empty() )
6699 outerFace2 = angle2Face.begin()->second;
6702 // store the found outer face and add its links to continue seaching from
6705 _outerFaces.insert( outerFace );
6706 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6707 for ( int i = 0; i < nbNodes; ++i )
6709 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6710 if ( visitedLinks.insert( link2 ).second )
6711 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6714 startLinks.pop_front();
6716 _outerFacesFound = true;
6718 if ( !seamLinks.empty() )
6720 // There are internal boundaries touching the outher one,
6721 // find all faces of internal boundaries in order to find
6722 // faces of boundaries of holes, if any.
6727 _outerFaces.clear();
6731 //=======================================================================
6733 * \brief Find elements of given type where the given point is IN or ON.
6734 * Returns nb of found elements and elements them-selves.
6736 * 'ALL' type means elements of any type excluding nodes and 0D elements
6738 //=======================================================================
6740 int SMESH_ElementSearcherImpl::
6741 FindElementsByPoint(const gp_Pnt& point,
6742 SMDSAbs_ElementType type,
6743 vector< const SMDS_MeshElement* >& foundElements)
6745 foundElements.clear();
6747 double tolerance = getTolerance();
6749 // =================================================================================
6750 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6752 if ( !_nodeSearcher )
6753 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6755 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6756 if ( !closeNode ) return foundElements.size();
6758 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6759 return foundElements.size(); // to far from any node
6761 if ( type == SMDSAbs_Node )
6763 foundElements.push_back( closeNode );
6767 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6768 while ( elemIt->more() )
6769 foundElements.push_back( elemIt->next() );
6772 // =================================================================================
6773 else // elements more complex than 0D
6775 if ( !_ebbTree || _elementType != type )
6777 if ( _ebbTree ) delete _ebbTree;
6778 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6780 TIDSortedElemSet suspectElems;
6781 _ebbTree->getElementsNearPoint( point, suspectElems );
6782 TIDSortedElemSet::iterator elem = suspectElems.begin();
6783 for ( ; elem != suspectElems.end(); ++elem )
6784 if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6785 foundElements.push_back( *elem );
6787 return foundElements.size();
6790 //================================================================================
6792 * \brief Classify the given point in the closed 2D mesh
6794 //================================================================================
6796 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6798 double tolerance = getTolerance();
6799 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6801 if ( _ebbTree ) delete _ebbTree;
6802 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6804 // Algo: analyse transition of a line starting at the point through mesh boundary;
6805 // try three lines parallel to axis of the coordinate system and perform rough
6806 // analysis. If solution is not clear perform thorough analysis.
6808 const int nbAxes = 3;
6809 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6810 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6811 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6812 multimap< int, int > nbInt2Axis; // to find the simplest case
6813 for ( int axis = 0; axis < nbAxes; ++axis )
6815 gp_Ax1 lineAxis( point, axisDir[axis]);
6816 gp_Lin line ( lineAxis );
6818 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6819 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6821 // Intersect faces with the line
6823 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6824 TIDSortedElemSet::iterator face = suspectFaces.begin();
6825 for ( ; face != suspectFaces.end(); ++face )
6829 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6830 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6832 // perform intersection
6833 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6834 if ( !intersection.IsDone() )
6836 if ( intersection.IsInQuadric() )
6838 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6840 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6842 gp_Pnt intersectionPoint = intersection.Point(1);
6843 if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6844 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6847 // Analyse intersections roughly
6849 int nbInter = u2inters.size();
6853 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6854 if ( nbInter == 1 ) // not closed mesh
6855 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6857 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6860 if ( (f<0) == (l<0) )
6863 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6864 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6865 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6868 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6870 if ( _outerFacesFound ) break; // pass to thorough analysis
6872 } // three attempts - loop on CS axes
6874 // Analyse intersections thoroughly.
6875 // We make two loops maximum, on the first one we only exclude touching intersections,
6876 // on the second, if situation is still unclear, we gather and use information on
6877 // position of faces (internal or outer). If faces position is already gathered,
6878 // we make the second loop right away.
6880 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6882 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6883 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6885 int axis = nb_axis->second;
6886 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6888 gp_Ax1 lineAxis( point, axisDir[axis]);
6889 gp_Lin line ( lineAxis );
6891 // add tangent intersections to u2inters
6893 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6894 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6895 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6896 u2inters.insert(make_pair( param, *tgtInt ));
6897 tangentInters[ axis ].clear();
6899 // Count intersections before and after the point excluding touching ones.
6900 // If hasPositionInfo we count intersections of outer boundary only
6902 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6903 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6904 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6905 bool ok = ! u_int1->second._coincides;
6906 while ( ok && u_int1 != u2inters.end() )
6908 double u = u_int1->first;
6909 bool touchingInt = false;
6910 if ( ++u_int2 != u2inters.end() )
6912 // skip intersections at the same point (if the line passes through edge or node)
6914 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6920 // skip tangent intersections
6922 const SMDS_MeshElement* prevFace = u_int1->second._face;
6923 while ( ok && u_int2->second._coincides )
6925 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6931 ok = ( u_int2 != u2inters.end() );
6936 // skip intersections at the same point after tangent intersections
6939 double u2 = u_int2->first;
6941 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6947 // decide if we skipped a touching intersection
6948 if ( nbSamePnt + nbTgt > 0 )
6950 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6951 map< double, TInters >::iterator u_int = u_int1;
6952 for ( ; u_int != u_int2; ++u_int )
6954 if ( u_int->second._coincides ) continue;
6955 double dot = u_int->second._faceNorm * line.Direction();
6956 if ( dot > maxDot ) maxDot = dot;
6957 if ( dot < minDot ) minDot = dot;
6959 touchingInt = ( minDot*maxDot < 0 );
6964 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6975 u_int1 = u_int2; // to next intersection
6977 } // loop on intersections with one line
6981 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6984 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6987 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6988 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6990 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6993 if ( (f<0) == (l<0) )
6996 if ( hasPositionInfo )
6997 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
6999 } // loop on intersections of the tree lines - thorough analysis
7001 if ( !hasPositionInfo )
7003 // gather info on faces position - is face in the outer boundary or not
7004 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7005 findOuterBoundary( u2inters.begin()->second._face );
7008 } // two attempts - with and w/o faces position info in the mesh
7010 return TopAbs_UNKNOWN;
7013 //=======================================================================
7015 * \brief Return elements possibly intersecting the line
7017 //=======================================================================
7019 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7020 SMDSAbs_ElementType type,
7021 vector< const SMDS_MeshElement* >& foundElems)
7023 if ( !_ebbTree || _elementType != type )
7025 if ( _ebbTree ) delete _ebbTree;
7026 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7028 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7029 _ebbTree->getElementsNearLine( line, suspectFaces );
7030 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7033 //=======================================================================
7035 * \brief Return SMESH_ElementSearcher
7037 //=======================================================================
7039 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7041 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7044 //=======================================================================
7046 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7048 //=======================================================================
7050 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7052 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7055 //=======================================================================
7057 * \brief Return true if the point is IN or ON of the element
7059 //=======================================================================
7061 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7063 if ( element->GetType() == SMDSAbs_Volume)
7065 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7068 // get ordered nodes
7070 vector< gp_XYZ > xyz;
7071 vector<const SMDS_MeshNode*> nodeList;
7073 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7074 if ( element->IsQuadratic() ) {
7075 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7076 nodeIt = f->interlacedNodesElemIterator();
7077 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7078 nodeIt = e->interlacedNodesElemIterator();
7080 while ( nodeIt->more() )
7082 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7083 xyz.push_back( SMESH_TNodeXYZ(node) );
7084 nodeList.push_back(node);
7087 int i, nbNodes = element->NbNodes();
7089 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7091 // compute face normal
7092 gp_Vec faceNorm(0,0,0);
7093 xyz.push_back( xyz.front() );
7094 nodeList.push_back( nodeList.front() );
7095 for ( i = 0; i < nbNodes; ++i )
7097 gp_Vec edge1( xyz[i+1], xyz[i]);
7098 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7099 faceNorm += edge1 ^ edge2;
7101 double normSize = faceNorm.Magnitude();
7102 if ( normSize <= tol )
7104 // degenerated face: point is out if it is out of all face edges
7105 for ( i = 0; i < nbNodes; ++i )
7107 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7108 if ( !isOut( &edge, point, tol ))
7113 faceNorm /= normSize;
7115 // check if the point lays on face plane
7116 gp_Vec n2p( xyz[0], point );
7117 if ( fabs( n2p * faceNorm ) > tol )
7118 return true; // not on face plane
7120 // check if point is out of face boundary:
7121 // define it by closest transition of a ray point->infinity through face boundary
7122 // on the face plane.
7123 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7124 // to find intersections of the ray with the boundary.
7126 gp_Vec plnNorm = ray ^ faceNorm;
7127 normSize = plnNorm.Magnitude();
7128 if ( normSize <= tol ) return false; // point coincides with the first node
7129 plnNorm /= normSize;
7130 // for each node of the face, compute its signed distance to the plane
7131 vector<double> dist( nbNodes + 1);
7132 for ( i = 0; i < nbNodes; ++i )
7134 gp_Vec n2p( xyz[i], point );
7135 dist[i] = n2p * plnNorm;
7137 dist.back() = dist.front();
7138 // find the closest intersection
7140 double rClosest, distClosest = 1e100;;
7142 for ( i = 0; i < nbNodes; ++i )
7145 if ( fabs( dist[i]) < tol )
7147 else if ( fabs( dist[i+1]) < tol )
7149 else if ( dist[i] * dist[i+1] < 0 )
7150 r = dist[i] / ( dist[i] - dist[i+1] );
7152 continue; // no intersection
7153 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7154 gp_Vec p2int ( point, pInt);
7155 if ( p2int * ray > -tol ) // right half-space
7157 double intDist = p2int.SquareMagnitude();
7158 if ( intDist < distClosest )
7163 distClosest = intDist;
7168 return true; // no intesections - out
7170 // analyse transition
7171 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7172 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7173 gp_Vec p2int ( point, pClosest );
7174 bool out = (edgeNorm * p2int) < -tol;
7175 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7178 // ray pass through a face node; analyze transition through an adjacent edge
7179 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7180 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7181 gp_Vec edgeAdjacent( p1, p2 );
7182 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7183 bool out2 = (edgeNorm2 * p2int) < -tol;
7185 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7186 return covexCorner ? (out || out2) : (out && out2);
7188 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7190 // point is out of edge if it is NOT ON any straight part of edge
7191 // (we consider quadratic edge as being composed of two straight parts)
7192 for ( i = 1; i < nbNodes; ++i )
7194 gp_Vec edge( xyz[i-1], xyz[i]);
7195 gp_Vec n1p ( xyz[i-1], point);
7196 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7199 gp_Vec n2p( xyz[i], point );
7200 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7202 return false; // point is ON this part
7206 // Node or 0D element -------------------------------------------------------------------------
7208 gp_Vec n2p ( xyz[0], point );
7209 return n2p.Magnitude() <= tol;
7214 //=======================================================================
7215 //function : SimplifyFace
7217 //=======================================================================
7218 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7219 vector<const SMDS_MeshNode *>& poly_nodes,
7220 vector<int>& quantities) const
7222 int nbNodes = faceNodes.size();
7227 set<const SMDS_MeshNode*> nodeSet;
7229 // get simple seq of nodes
7230 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7231 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7232 int iSimple = 0, nbUnique = 0;
7234 simpleNodes[iSimple++] = faceNodes[0];
7236 for (int iCur = 1; iCur < nbNodes; iCur++) {
7237 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7238 simpleNodes[iSimple++] = faceNodes[iCur];
7239 if (nodeSet.insert( faceNodes[iCur] ).second)
7243 int nbSimple = iSimple;
7244 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7254 bool foundLoop = (nbSimple > nbUnique);
7257 set<const SMDS_MeshNode*> loopSet;
7258 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7259 const SMDS_MeshNode* n = simpleNodes[iSimple];
7260 if (!loopSet.insert( n ).second) {
7264 int iC = 0, curLast = iSimple;
7265 for (; iC < curLast; iC++) {
7266 if (simpleNodes[iC] == n) break;
7268 int loopLen = curLast - iC;
7270 // create sub-element
7272 quantities.push_back(loopLen);
7273 for (; iC < curLast; iC++) {
7274 poly_nodes.push_back(simpleNodes[iC]);
7277 // shift the rest nodes (place from the first loop position)
7278 for (iC = curLast + 1; iC < nbSimple; iC++) {
7279 simpleNodes[iC - loopLen] = simpleNodes[iC];
7281 nbSimple -= loopLen;
7284 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7285 } // while (foundLoop)
7289 quantities.push_back(iSimple);
7290 for (int i = 0; i < iSimple; i++)
7291 poly_nodes.push_back(simpleNodes[i]);
7297 //=======================================================================
7298 //function : MergeNodes
7299 //purpose : In each group, the cdr of nodes are substituted by the first one
7301 //=======================================================================
7303 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7305 MESSAGE("MergeNodes");
7306 myLastCreatedElems.Clear();
7307 myLastCreatedNodes.Clear();
7309 SMESHDS_Mesh* aMesh = GetMeshDS();
7311 TNodeNodeMap nodeNodeMap; // node to replace - new node
7312 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7313 list< int > rmElemIds, rmNodeIds;
7315 // Fill nodeNodeMap and elems
7317 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7318 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7319 list<const SMDS_MeshNode*>& nodes = *grIt;
7320 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7321 const SMDS_MeshNode* nToKeep = *nIt;
7322 //MESSAGE("node to keep " << nToKeep->GetID());
7323 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7324 const SMDS_MeshNode* nToRemove = *nIt;
7325 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7326 if ( nToRemove != nToKeep ) {
7327 //MESSAGE(" node to remove " << nToRemove->GetID());
7328 rmNodeIds.push_back( nToRemove->GetID() );
7329 AddToSameGroups( nToKeep, nToRemove, aMesh );
7332 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7333 while ( invElemIt->more() ) {
7334 const SMDS_MeshElement* elem = invElemIt->next();
7339 // Change element nodes or remove an element
7341 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7342 for ( ; eIt != elems.end(); eIt++ ) {
7343 const SMDS_MeshElement* elem = *eIt;
7344 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7345 int nbNodes = elem->NbNodes();
7346 int aShapeId = FindShape( elem );
7348 set<const SMDS_MeshNode*> nodeSet;
7349 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7350 int iUnique = 0, iCur = 0, nbRepl = 0;
7351 vector<int> iRepl( nbNodes );
7353 // get new seq of nodes
7354 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7355 while ( itN->more() ) {
7356 const SMDS_MeshNode* n =
7357 static_cast<const SMDS_MeshNode*>( itN->next() );
7359 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7360 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7362 // BUG 0020185: begin
7364 bool stopRecur = false;
7365 set<const SMDS_MeshNode*> nodesRecur;
7366 nodesRecur.insert(n);
7367 while (!stopRecur) {
7368 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7369 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7370 n = (*nnIt_i).second;
7371 if (!nodesRecur.insert(n).second) {
7372 // error: recursive dependancy
7381 iRepl[ nbRepl++ ] = iCur;
7383 curNodes[ iCur ] = n;
7384 bool isUnique = nodeSet.insert( n ).second;
7386 uniqueNodes[ iUnique++ ] = n;
7387 if ( nbRepl && iRepl[ nbRepl-1 ] == iCur )
7388 --nbRepl; // n do not stick to a node of the elem
7393 // Analyse element topology after replacement
7396 int nbUniqueNodes = nodeSet.size();
7397 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7398 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7399 // Polygons and Polyhedral volumes
7400 if (elem->IsPoly()) {
7402 if (elem->GetType() == SMDSAbs_Face) {
7404 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7406 for (; inode < nbNodes; inode++) {
7407 face_nodes[inode] = curNodes[inode];
7410 vector<const SMDS_MeshNode *> polygons_nodes;
7411 vector<int> quantities;
7412 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7415 for (int iface = 0; iface < nbNew; iface++) {
7416 int nbNodes = quantities[iface];
7417 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7418 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7419 poly_nodes[ii] = polygons_nodes[inode];
7421 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7422 myLastCreatedElems.Append(newElem);
7424 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7427 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7428 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7429 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7431 if (nbNew > 0) quid = nbNew - 1;
7432 vector<int> newquant(quantities.begin()+quid, quantities.end());
7433 const SMDS_MeshElement* newElem = 0;
7434 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7435 myLastCreatedElems.Append(newElem);
7436 if ( aShapeId && newElem )
7437 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7438 rmElemIds.push_back(elem->GetID());
7441 rmElemIds.push_back(elem->GetID());
7445 else if (elem->GetType() == SMDSAbs_Volume) {
7446 // Polyhedral volume
7447 if (nbUniqueNodes < 4) {
7448 rmElemIds.push_back(elem->GetID());
7451 // each face has to be analyzed in order to check volume validity
7452 const SMDS_VtkVolume* aPolyedre =
7453 dynamic_cast<const SMDS_VtkVolume*>( elem );
7455 int nbFaces = aPolyedre->NbFaces();
7457 vector<const SMDS_MeshNode *> poly_nodes;
7458 vector<int> quantities;
7460 for (int iface = 1; iface <= nbFaces; iface++) {
7461 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7462 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7464 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7465 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7466 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7467 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7468 faceNode = (*nnIt).second;
7470 faceNodes[inode - 1] = faceNode;
7473 SimplifyFace(faceNodes, poly_nodes, quantities);
7476 if (quantities.size() > 3) {
7477 // to be done: remove coincident faces
7480 if (quantities.size() > 3)
7482 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7483 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7484 const SMDS_MeshElement* newElem = 0;
7485 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7486 myLastCreatedElems.Append(newElem);
7487 if ( aShapeId && newElem )
7488 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7489 rmElemIds.push_back(elem->GetID());
7493 rmElemIds.push_back(elem->GetID());
7504 // TODO not all the possible cases are solved. Find something more generic?
7505 switch ( nbNodes ) {
7506 case 2: ///////////////////////////////////// EDGE
7507 isOk = false; break;
7508 case 3: ///////////////////////////////////// TRIANGLE
7509 isOk = false; break;
7511 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7513 else { //////////////////////////////////// QUADRANGLE
7514 if ( nbUniqueNodes < 3 )
7516 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7517 isOk = false; // opposite nodes stick
7518 //MESSAGE("isOk " << isOk);
7521 case 6: ///////////////////////////////////// PENTAHEDRON
7522 if ( nbUniqueNodes == 4 ) {
7523 // ---------------------------------> tetrahedron
7525 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7526 // all top nodes stick: reverse a bottom
7527 uniqueNodes[ 0 ] = curNodes [ 1 ];
7528 uniqueNodes[ 1 ] = curNodes [ 0 ];
7530 else if (nbRepl == 3 &&
7531 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7532 // all bottom nodes stick: set a top before
7533 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7534 uniqueNodes[ 0 ] = curNodes [ 3 ];
7535 uniqueNodes[ 1 ] = curNodes [ 4 ];
7536 uniqueNodes[ 2 ] = curNodes [ 5 ];
7538 else if (nbRepl == 4 &&
7539 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7540 // a lateral face turns into a line: reverse a bottom
7541 uniqueNodes[ 0 ] = curNodes [ 1 ];
7542 uniqueNodes[ 1 ] = curNodes [ 0 ];
7547 else if ( nbUniqueNodes == 5 ) {
7548 // PENTAHEDRON --------------------> 2 tetrahedrons
7549 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7550 // a bottom node sticks with a linked top one
7552 SMDS_MeshElement* newElem =
7553 aMesh->AddVolume(curNodes[ 3 ],
7556 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7557 myLastCreatedElems.Append(newElem);
7559 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7560 // 2. : reverse a bottom
7561 uniqueNodes[ 0 ] = curNodes [ 1 ];
7562 uniqueNodes[ 1 ] = curNodes [ 0 ];
7572 if(elem->IsQuadratic()) { // Quadratic quadrangle
7584 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7587 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7589 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7590 uniqueNodes[0] = curNodes[0];
7591 uniqueNodes[1] = curNodes[2];
7592 uniqueNodes[2] = curNodes[3];
7593 uniqueNodes[3] = curNodes[5];
7594 uniqueNodes[4] = curNodes[6];
7595 uniqueNodes[5] = curNodes[7];
7598 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7599 uniqueNodes[0] = curNodes[0];
7600 uniqueNodes[1] = curNodes[1];
7601 uniqueNodes[2] = curNodes[2];
7602 uniqueNodes[3] = curNodes[4];
7603 uniqueNodes[4] = curNodes[5];
7604 uniqueNodes[5] = curNodes[6];
7607 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7608 uniqueNodes[0] = curNodes[1];
7609 uniqueNodes[1] = curNodes[2];
7610 uniqueNodes[2] = curNodes[3];
7611 uniqueNodes[3] = curNodes[5];
7612 uniqueNodes[4] = curNodes[6];
7613 uniqueNodes[5] = curNodes[0];
7616 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7617 uniqueNodes[0] = curNodes[0];
7618 uniqueNodes[1] = curNodes[1];
7619 uniqueNodes[2] = curNodes[3];
7620 uniqueNodes[3] = curNodes[4];
7621 uniqueNodes[4] = curNodes[6];
7622 uniqueNodes[5] = curNodes[7];
7625 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7626 uniqueNodes[0] = curNodes[0];
7627 uniqueNodes[1] = curNodes[2];
7628 uniqueNodes[2] = curNodes[3];
7629 uniqueNodes[3] = curNodes[1];
7630 uniqueNodes[4] = curNodes[6];
7631 uniqueNodes[5] = curNodes[7];
7634 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7635 uniqueNodes[0] = curNodes[0];
7636 uniqueNodes[1] = curNodes[1];
7637 uniqueNodes[2] = curNodes[2];
7638 uniqueNodes[3] = curNodes[4];
7639 uniqueNodes[4] = curNodes[5];
7640 uniqueNodes[5] = curNodes[7];
7643 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7644 uniqueNodes[0] = curNodes[0];
7645 uniqueNodes[1] = curNodes[1];
7646 uniqueNodes[2] = curNodes[3];
7647 uniqueNodes[3] = curNodes[4];
7648 uniqueNodes[4] = curNodes[2];
7649 uniqueNodes[5] = curNodes[7];
7652 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7653 uniqueNodes[0] = curNodes[0];
7654 uniqueNodes[1] = curNodes[1];
7655 uniqueNodes[2] = curNodes[2];
7656 uniqueNodes[3] = curNodes[4];
7657 uniqueNodes[4] = curNodes[5];
7658 uniqueNodes[5] = curNodes[3];
7663 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7666 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7670 //////////////////////////////////// HEXAHEDRON
7672 SMDS_VolumeTool hexa (elem);
7673 hexa.SetExternalNormal();
7674 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7675 //////////////////////// HEX ---> 1 tetrahedron
7676 for ( int iFace = 0; iFace < 6; iFace++ ) {
7677 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7678 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7679 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7680 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7681 // one face turns into a point ...
7682 int iOppFace = hexa.GetOppFaceIndex( iFace );
7683 ind = hexa.GetFaceNodesIndices( iOppFace );
7685 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7686 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7689 if ( nbStick == 1 ) {
7690 // ... and the opposite one - into a triangle.
7692 ind = hexa.GetFaceNodesIndices( iFace );
7693 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7700 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7701 //////////////////////// HEX ---> 1 prism
7702 int nbTria = 0, iTria[3];
7703 const int *ind; // indices of face nodes
7704 // look for triangular faces
7705 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7706 ind = hexa.GetFaceNodesIndices( iFace );
7707 TIDSortedNodeSet faceNodes;
7708 for ( iCur = 0; iCur < 4; iCur++ )
7709 faceNodes.insert( curNodes[ind[iCur]] );
7710 if ( faceNodes.size() == 3 )
7711 iTria[ nbTria++ ] = iFace;
7713 // check if triangles are opposite
7714 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7717 // set nodes of the bottom triangle
7718 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7720 for ( iCur = 0; iCur < 4; iCur++ )
7721 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7722 indB.push_back( ind[iCur] );
7723 if ( !hexa.IsForward() )
7724 std::swap( indB[0], indB[2] );
7725 for ( iCur = 0; iCur < 3; iCur++ )
7726 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7727 // set nodes of the top triangle
7728 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7729 for ( iCur = 0; iCur < 3; ++iCur )
7730 for ( int j = 0; j < 4; ++j )
7731 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7733 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7739 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7740 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7741 for ( int iFace = 0; iFace < 6; iFace++ ) {
7742 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7743 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7744 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7745 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7746 // one face turns into a point ...
7747 int iOppFace = hexa.GetOppFaceIndex( iFace );
7748 ind = hexa.GetFaceNodesIndices( iOppFace );
7750 iUnique = 2; // reverse a tetrahedron 1 bottom
7751 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7752 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7754 else if ( iUnique >= 0 )
7755 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7757 if ( nbStick == 0 ) {
7758 // ... and the opposite one is a quadrangle
7760 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7761 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7764 SMDS_MeshElement* newElem =
7765 aMesh->AddVolume(curNodes[ind[ 0 ]],
7768 curNodes[indTop[ 0 ]]);
7769 myLastCreatedElems.Append(newElem);
7771 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7778 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7779 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7780 // find indices of quad and tri faces
7781 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7782 for ( iFace = 0; iFace < 6; iFace++ ) {
7783 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7785 for ( iCur = 0; iCur < 4; iCur++ )
7786 nodeSet.insert( curNodes[ind[ iCur ]] );
7787 nbUniqueNodes = nodeSet.size();
7788 if ( nbUniqueNodes == 3 )
7789 iTriFace[ nbTri++ ] = iFace;
7790 else if ( nbUniqueNodes == 4 )
7791 iQuadFace[ nbQuad++ ] = iFace;
7793 if (nbQuad == 2 && nbTri == 4 &&
7794 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7795 // 2 opposite quadrangles stuck with a diagonal;
7796 // sample groups of merged indices: (0-4)(2-6)
7797 // --------------------------------------------> 2 tetrahedrons
7798 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7799 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7800 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7801 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7802 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7803 // stuck with 0-2 diagonal
7811 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7812 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7813 // stuck with 1-3 diagonal
7825 uniqueNodes[ 0 ] = curNodes [ i0 ];
7826 uniqueNodes[ 1 ] = curNodes [ i1d ];
7827 uniqueNodes[ 2 ] = curNodes [ i3d ];
7828 uniqueNodes[ 3 ] = curNodes [ i0t ];
7831 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7835 myLastCreatedElems.Append(newElem);
7837 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7840 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7841 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7842 // --------------------------------------------> prism
7843 // find 2 opposite triangles
7845 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7846 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7847 // find indices of kept and replaced nodes
7848 // and fill unique nodes of 2 opposite triangles
7849 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7850 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7851 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7852 // fill unique nodes
7855 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7856 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7857 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7859 // iCur of a linked node of the opposite face (make normals co-directed):
7860 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7861 // check that correspondent corners of triangles are linked
7862 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7865 uniqueNodes[ iUnique ] = n;
7866 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7875 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7878 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7885 } // switch ( nbNodes )
7887 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7889 if ( isOk ) { // the elem remains valid after sticking nodes
7890 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7892 // Change nodes of polyedre
7893 const SMDS_VtkVolume* aPolyedre =
7894 dynamic_cast<const SMDS_VtkVolume*>( elem );
7896 int nbFaces = aPolyedre->NbFaces();
7898 vector<const SMDS_MeshNode *> poly_nodes;
7899 vector<int> quantities (nbFaces);
7901 for (int iface = 1; iface <= nbFaces; iface++) {
7902 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7903 quantities[iface - 1] = nbFaceNodes;
7905 for (inode = 1; inode <= nbFaceNodes; inode++) {
7906 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7908 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7909 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7910 curNode = (*nnIt).second;
7912 poly_nodes.push_back(curNode);
7915 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7918 else // replace non-polyhedron elements
7920 const SMDSAbs_ElementType etyp = elem->GetType();
7921 const int elemId = elem->GetID();
7922 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7923 uniqueNodes.resize(nbUniqueNodes);
7925 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7927 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7928 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7929 if ( sm && newElem )
7930 sm->AddElement( newElem );
7931 if ( elem != newElem )
7932 ReplaceElemInGroups( elem, newElem, aMesh );
7936 // Remove invalid regular element or invalid polygon
7937 rmElemIds.push_back( elem->GetID() );
7940 } // loop on elements
7942 // Remove bad elements, then equal nodes (order important)
7944 Remove( rmElemIds, false );
7945 Remove( rmNodeIds, true );
7950 // ========================================================
7951 // class : SortableElement
7952 // purpose : allow sorting elements basing on their nodes
7953 // ========================================================
7954 class SortableElement : public set <const SMDS_MeshElement*>
7958 SortableElement( const SMDS_MeshElement* theElem )
7961 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7962 while ( nodeIt->more() )
7963 this->insert( nodeIt->next() );
7966 const SMDS_MeshElement* Get() const
7969 void Set(const SMDS_MeshElement* e) const
7974 mutable const SMDS_MeshElement* myElem;
7977 //=======================================================================
7978 //function : FindEqualElements
7979 //purpose : Return list of group of elements built on the same nodes.
7980 // Search among theElements or in the whole mesh if theElements is empty
7981 //=======================================================================
7982 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7983 TListOfListOfElementsID & theGroupsOfElementsID)
7985 myLastCreatedElems.Clear();
7986 myLastCreatedNodes.Clear();
7988 typedef set<const SMDS_MeshElement*> TElemsSet;
7989 typedef map< SortableElement, int > TMapOfNodeSet;
7990 typedef list<int> TGroupOfElems;
7993 if ( theElements.empty() )
7994 { // get all elements in the mesh
7995 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7996 while ( eIt->more() )
7997 elems.insert( elems.end(), eIt->next());
8000 elems = theElements;
8002 vector< TGroupOfElems > arrayOfGroups;
8003 TGroupOfElems groupOfElems;
8004 TMapOfNodeSet mapOfNodeSet;
8006 TElemsSet::iterator elemIt = elems.begin();
8007 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
8008 const SMDS_MeshElement* curElem = *elemIt;
8009 SortableElement SE(curElem);
8012 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8013 if( !(pp.second) ) {
8014 TMapOfNodeSet::iterator& itSE = pp.first;
8015 ind = (*itSE).second;
8016 arrayOfGroups[ind].push_back(curElem->GetID());
8019 groupOfElems.clear();
8020 groupOfElems.push_back(curElem->GetID());
8021 arrayOfGroups.push_back(groupOfElems);
8026 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8027 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8028 groupOfElems = *groupIt;
8029 if ( groupOfElems.size() > 1 ) {
8030 groupOfElems.sort();
8031 theGroupsOfElementsID.push_back(groupOfElems);
8036 //=======================================================================
8037 //function : MergeElements
8038 //purpose : In each given group, substitute all elements by the first one.
8039 //=======================================================================
8041 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8043 myLastCreatedElems.Clear();
8044 myLastCreatedNodes.Clear();
8046 typedef list<int> TListOfIDs;
8047 TListOfIDs rmElemIds; // IDs of elems to remove
8049 SMESHDS_Mesh* aMesh = GetMeshDS();
8051 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8052 while ( groupsIt != theGroupsOfElementsID.end() ) {
8053 TListOfIDs& aGroupOfElemID = *groupsIt;
8054 aGroupOfElemID.sort();
8055 int elemIDToKeep = aGroupOfElemID.front();
8056 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8057 aGroupOfElemID.pop_front();
8058 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8059 while ( idIt != aGroupOfElemID.end() ) {
8060 int elemIDToRemove = *idIt;
8061 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8062 // add the kept element in groups of removed one (PAL15188)
8063 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8064 rmElemIds.push_back( elemIDToRemove );
8070 Remove( rmElemIds, false );
8073 //=======================================================================
8074 //function : MergeEqualElements
8075 //purpose : Remove all but one of elements built on the same nodes.
8076 //=======================================================================
8078 void SMESH_MeshEditor::MergeEqualElements()
8080 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
8081 to merge equal elements in the whole mesh */
8082 TListOfListOfElementsID aGroupsOfElementsID;
8083 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8084 MergeElements(aGroupsOfElementsID);
8087 //=======================================================================
8088 //function : FindFaceInSet
8089 //purpose : Return a face having linked nodes n1 and n2 and which is
8090 // - not in avoidSet,
8091 // - in elemSet provided that !elemSet.empty()
8092 // i1 and i2 optionally returns indices of n1 and n2
8093 //=======================================================================
8095 const SMDS_MeshElement*
8096 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8097 const SMDS_MeshNode* n2,
8098 const TIDSortedElemSet& elemSet,
8099 const TIDSortedElemSet& avoidSet,
8105 const SMDS_MeshElement* face = 0;
8107 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8108 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8109 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8111 //MESSAGE("in while ( invElemIt->more() && !face )");
8112 const SMDS_MeshElement* elem = invElemIt->next();
8113 if (avoidSet.count( elem ))
8115 if ( !elemSet.empty() && !elemSet.count( elem ))
8118 i1 = elem->GetNodeIndex( n1 );
8119 // find a n2 linked to n1
8120 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8121 for ( int di = -1; di < 2 && !face; di += 2 )
8123 i2 = (i1+di+nbN) % nbN;
8124 if ( elem->GetNode( i2 ) == n2 )
8127 if ( !face && elem->IsQuadratic())
8129 // analysis for quadratic elements using all nodes
8130 const SMDS_VtkFace* F =
8131 dynamic_cast<const SMDS_VtkFace*>(elem);
8132 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8133 // use special nodes iterator
8134 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8135 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8136 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8138 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8139 if ( n1 == prevN && n2 == n )
8143 else if ( n2 == prevN && n1 == n )
8145 face = elem; swap( i1, i2 );
8151 if ( n1ind ) *n1ind = i1;
8152 if ( n2ind ) *n2ind = i2;
8156 //=======================================================================
8157 //function : findAdjacentFace
8159 //=======================================================================
8161 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8162 const SMDS_MeshNode* n2,
8163 const SMDS_MeshElement* elem)
8165 TIDSortedElemSet elemSet, avoidSet;
8167 avoidSet.insert ( elem );
8168 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8171 //=======================================================================
8172 //function : FindFreeBorder
8174 //=======================================================================
8176 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8178 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8179 const SMDS_MeshNode* theSecondNode,
8180 const SMDS_MeshNode* theLastNode,
8181 list< const SMDS_MeshNode* > & theNodes,
8182 list< const SMDS_MeshElement* >& theFaces)
8184 if ( !theFirstNode || !theSecondNode )
8186 // find border face between theFirstNode and theSecondNode
8187 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8191 theFaces.push_back( curElem );
8192 theNodes.push_back( theFirstNode );
8193 theNodes.push_back( theSecondNode );
8195 //vector<const SMDS_MeshNode*> nodes;
8196 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8197 TIDSortedElemSet foundElems;
8198 bool needTheLast = ( theLastNode != 0 );
8200 while ( nStart != theLastNode ) {
8201 if ( nStart == theFirstNode )
8202 return !needTheLast;
8204 // find all free border faces sharing form nStart
8206 list< const SMDS_MeshElement* > curElemList;
8207 list< const SMDS_MeshNode* > nStartList;
8208 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8209 while ( invElemIt->more() ) {
8210 const SMDS_MeshElement* e = invElemIt->next();
8211 if ( e == curElem || foundElems.insert( e ).second ) {
8213 int iNode = 0, nbNodes = e->NbNodes();
8214 //const SMDS_MeshNode* nodes[nbNodes+1];
8215 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8217 if(e->IsQuadratic()) {
8218 const SMDS_VtkFace* F =
8219 dynamic_cast<const SMDS_VtkFace*>(e);
8220 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8221 // use special nodes iterator
8222 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8223 while( anIter->more() ) {
8224 nodes[ iNode++ ] = cast2Node(anIter->next());
8228 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8229 while ( nIt->more() )
8230 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8232 nodes[ iNode ] = nodes[ 0 ];
8234 for ( iNode = 0; iNode < nbNodes; iNode++ )
8235 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8236 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8237 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8239 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8240 curElemList.push_back( e );
8244 // analyse the found
8246 int nbNewBorders = curElemList.size();
8247 if ( nbNewBorders == 0 ) {
8248 // no free border furthermore
8249 return !needTheLast;
8251 else if ( nbNewBorders == 1 ) {
8252 // one more element found
8254 nStart = nStartList.front();
8255 curElem = curElemList.front();
8256 theFaces.push_back( curElem );
8257 theNodes.push_back( nStart );
8260 // several continuations found
8261 list< const SMDS_MeshElement* >::iterator curElemIt;
8262 list< const SMDS_MeshNode* >::iterator nStartIt;
8263 // check if one of them reached the last node
8264 if ( needTheLast ) {
8265 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8266 curElemIt!= curElemList.end();
8267 curElemIt++, nStartIt++ )
8268 if ( *nStartIt == theLastNode ) {
8269 theFaces.push_back( *curElemIt );
8270 theNodes.push_back( *nStartIt );
8274 // find the best free border by the continuations
8275 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8276 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8277 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8278 curElemIt!= curElemList.end();
8279 curElemIt++, nStartIt++ )
8281 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8282 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8283 // find one more free border
8284 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8288 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8289 // choice: clear a worse one
8290 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8291 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8292 contNodes[ iWorse ].clear();
8293 contFaces[ iWorse ].clear();
8296 if ( contNodes[0].empty() && contNodes[1].empty() )
8299 // append the best free border
8300 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8301 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8302 theNodes.pop_back(); // remove nIgnore
8303 theNodes.pop_back(); // remove nStart
8304 theFaces.pop_back(); // remove curElem
8305 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8306 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8307 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8308 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8311 } // several continuations found
8312 } // while ( nStart != theLastNode )
8317 //=======================================================================
8318 //function : CheckFreeBorderNodes
8319 //purpose : Return true if the tree nodes are on a free border
8320 //=======================================================================
8322 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8323 const SMDS_MeshNode* theNode2,
8324 const SMDS_MeshNode* theNode3)
8326 list< const SMDS_MeshNode* > nodes;
8327 list< const SMDS_MeshElement* > faces;
8328 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8331 //=======================================================================
8332 //function : SewFreeBorder
8334 //=======================================================================
8336 SMESH_MeshEditor::Sew_Error
8337 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8338 const SMDS_MeshNode* theBordSecondNode,
8339 const SMDS_MeshNode* theBordLastNode,
8340 const SMDS_MeshNode* theSideFirstNode,
8341 const SMDS_MeshNode* theSideSecondNode,
8342 const SMDS_MeshNode* theSideThirdNode,
8343 const bool theSideIsFreeBorder,
8344 const bool toCreatePolygons,
8345 const bool toCreatePolyedrs)
8347 myLastCreatedElems.Clear();
8348 myLastCreatedNodes.Clear();
8350 MESSAGE("::SewFreeBorder()");
8351 Sew_Error aResult = SEW_OK;
8353 // ====================================
8354 // find side nodes and elements
8355 // ====================================
8357 list< const SMDS_MeshNode* > nSide[ 2 ];
8358 list< const SMDS_MeshElement* > eSide[ 2 ];
8359 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8360 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8364 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8365 nSide[0], eSide[0])) {
8366 MESSAGE(" Free Border 1 not found " );
8367 aResult = SEW_BORDER1_NOT_FOUND;
8369 if (theSideIsFreeBorder) {
8372 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8373 nSide[1], eSide[1])) {
8374 MESSAGE(" Free Border 2 not found " );
8375 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8378 if ( aResult != SEW_OK )
8381 if (!theSideIsFreeBorder) {
8385 // -------------------------------------------------------------------------
8387 // 1. If nodes to merge are not coincident, move nodes of the free border
8388 // from the coord sys defined by the direction from the first to last
8389 // nodes of the border to the correspondent sys of the side 2
8390 // 2. On the side 2, find the links most co-directed with the correspondent
8391 // links of the free border
8392 // -------------------------------------------------------------------------
8394 // 1. Since sewing may break if there are volumes to split on the side 2,
8395 // we wont move nodes but just compute new coordinates for them
8396 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8397 TNodeXYZMap nBordXYZ;
8398 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8399 list< const SMDS_MeshNode* >::iterator nBordIt;
8401 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8402 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8403 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8404 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8405 double tol2 = 1.e-8;
8406 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8407 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8408 // Need node movement.
8410 // find X and Z axes to create trsf
8411 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8413 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8415 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8418 gp_Ax3 toBordAx( Pb1, Zb, X );
8419 gp_Ax3 fromSideAx( Ps1, Zs, X );
8420 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8422 gp_Trsf toBordSys, fromSide2Sys;
8423 toBordSys.SetTransformation( toBordAx );
8424 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8425 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8428 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8429 const SMDS_MeshNode* n = *nBordIt;
8430 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8431 toBordSys.Transforms( xyz );
8432 fromSide2Sys.Transforms( xyz );
8433 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8437 // just insert nodes XYZ in the nBordXYZ map
8438 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8439 const SMDS_MeshNode* n = *nBordIt;
8440 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8444 // 2. On the side 2, find the links most co-directed with the correspondent
8445 // links of the free border
8447 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8448 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8449 sideNodes.push_back( theSideFirstNode );
8451 bool hasVolumes = false;
8452 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8453 set<long> foundSideLinkIDs, checkedLinkIDs;
8454 SMDS_VolumeTool volume;
8455 //const SMDS_MeshNode* faceNodes[ 4 ];
8457 const SMDS_MeshNode* sideNode;
8458 const SMDS_MeshElement* sideElem;
8459 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8460 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8461 nBordIt = bordNodes.begin();
8463 // border node position and border link direction to compare with
8464 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8465 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8466 // choose next side node by link direction or by closeness to
8467 // the current border node:
8468 bool searchByDir = ( *nBordIt != theBordLastNode );
8470 // find the next node on the Side 2
8472 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8474 checkedLinkIDs.clear();
8475 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8477 // loop on inverse elements of current node (prevSideNode) on the Side 2
8478 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8479 while ( invElemIt->more() )
8481 const SMDS_MeshElement* elem = invElemIt->next();
8482 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8483 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8484 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8485 bool isVolume = volume.Set( elem );
8486 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8487 if ( isVolume ) // --volume
8489 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8490 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8491 if(elem->IsQuadratic()) {
8492 const SMDS_VtkFace* F =
8493 dynamic_cast<const SMDS_VtkFace*>(elem);
8494 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8495 // use special nodes iterator
8496 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8497 while( anIter->more() ) {
8498 nodes[ iNode ] = cast2Node(anIter->next());
8499 if ( nodes[ iNode++ ] == prevSideNode )
8500 iPrevNode = iNode - 1;
8504 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8505 while ( nIt->more() ) {
8506 nodes[ iNode ] = cast2Node( nIt->next() );
8507 if ( nodes[ iNode++ ] == prevSideNode )
8508 iPrevNode = iNode - 1;
8511 // there are 2 links to check
8516 // loop on links, to be precise, on the second node of links
8517 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8518 const SMDS_MeshNode* n = nodes[ iNode ];
8520 if ( !volume.IsLinked( n, prevSideNode ))
8524 if ( iNode ) // a node before prevSideNode
8525 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8526 else // a node after prevSideNode
8527 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8529 // check if this link was already used
8530 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8531 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8532 if (!isJustChecked &&
8533 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8535 // test a link geometrically
8536 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8537 bool linkIsBetter = false;
8538 double dot = 0.0, dist = 0.0;
8539 if ( searchByDir ) { // choose most co-directed link
8540 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8541 linkIsBetter = ( dot > maxDot );
8543 else { // choose link with the node closest to bordPos
8544 dist = ( nextXYZ - bordPos ).SquareModulus();
8545 linkIsBetter = ( dist < minDist );
8547 if ( linkIsBetter ) {
8556 } // loop on inverse elements of prevSideNode
8559 MESSAGE(" Cant find path by links of the Side 2 ");
8560 return SEW_BAD_SIDE_NODES;
8562 sideNodes.push_back( sideNode );
8563 sideElems.push_back( sideElem );
8564 foundSideLinkIDs.insert ( linkID );
8565 prevSideNode = sideNode;
8567 if ( *nBordIt == theBordLastNode )
8568 searchByDir = false;
8570 // find the next border link to compare with
8571 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8572 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8573 // move to next border node if sideNode is before forward border node (bordPos)
8574 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8575 prevBordNode = *nBordIt;
8577 bordPos = nBordXYZ[ *nBordIt ];
8578 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8579 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8583 while ( sideNode != theSideSecondNode );
8585 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8586 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8587 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8589 } // end nodes search on the side 2
8591 // ============================
8592 // sew the border to the side 2
8593 // ============================
8595 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8596 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8598 TListOfListOfNodes nodeGroupsToMerge;
8599 if ( nbNodes[0] == nbNodes[1] ||
8600 ( theSideIsFreeBorder && !theSideThirdNode)) {
8602 // all nodes are to be merged
8604 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8605 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8606 nIt[0]++, nIt[1]++ )
8608 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8609 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8610 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8615 // insert new nodes into the border and the side to get equal nb of segments
8617 // get normalized parameters of nodes on the borders
8618 //double param[ 2 ][ maxNbNodes ];
8620 param[0] = new double [ maxNbNodes ];
8621 param[1] = new double [ maxNbNodes ];
8623 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8624 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8625 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8626 const SMDS_MeshNode* nPrev = *nIt;
8627 double bordLength = 0;
8628 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8629 const SMDS_MeshNode* nCur = *nIt;
8630 gp_XYZ segment (nCur->X() - nPrev->X(),
8631 nCur->Y() - nPrev->Y(),
8632 nCur->Z() - nPrev->Z());
8633 double segmentLen = segment.Modulus();
8634 bordLength += segmentLen;
8635 param[ iBord ][ iNode ] = bordLength;
8638 // normalize within [0,1]
8639 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8640 param[ iBord ][ iNode ] /= bordLength;
8644 // loop on border segments
8645 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8646 int i[ 2 ] = { 0, 0 };
8647 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8648 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8650 TElemOfNodeListMap insertMap;
8651 TElemOfNodeListMap::iterator insertMapIt;
8653 // key: elem to insert nodes into
8654 // value: 2 nodes to insert between + nodes to be inserted
8656 bool next[ 2 ] = { false, false };
8658 // find min adjacent segment length after sewing
8659 double nextParam = 10., prevParam = 0;
8660 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8661 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8662 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8663 if ( i[ iBord ] > 0 )
8664 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8666 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8667 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8668 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8670 // choose to insert or to merge nodes
8671 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8672 if ( Abs( du ) <= minSegLen * 0.2 ) {
8675 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8676 const SMDS_MeshNode* n0 = *nIt[0];
8677 const SMDS_MeshNode* n1 = *nIt[1];
8678 nodeGroupsToMerge.back().push_back( n1 );
8679 nodeGroupsToMerge.back().push_back( n0 );
8680 // position of node of the border changes due to merge
8681 param[ 0 ][ i[0] ] += du;
8682 // move n1 for the sake of elem shape evaluation during insertion.
8683 // n1 will be removed by MergeNodes() anyway
8684 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8685 next[0] = next[1] = true;
8690 int intoBord = ( du < 0 ) ? 0 : 1;
8691 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8692 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8693 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8694 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8695 if ( intoBord == 1 ) {
8696 // move node of the border to be on a link of elem of the side
8697 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8698 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8699 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8700 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8701 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8703 insertMapIt = insertMap.find( elem );
8704 bool notFound = ( insertMapIt == insertMap.end() );
8705 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8707 // insert into another link of the same element:
8708 // 1. perform insertion into the other link of the elem
8709 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8710 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8711 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8712 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8713 // 2. perform insertion into the link of adjacent faces
8715 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8717 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8721 if (toCreatePolyedrs) {
8722 // perform insertion into the links of adjacent volumes
8723 UpdateVolumes(n12, n22, nodeList);
8725 // 3. find an element appeared on n1 and n2 after the insertion
8726 insertMap.erase( elem );
8727 elem = findAdjacentFace( n1, n2, 0 );
8729 if ( notFound || otherLink ) {
8730 // add element and nodes of the side into the insertMap
8731 insertMapIt = insertMap.insert
8732 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8733 (*insertMapIt).second.push_back( n1 );
8734 (*insertMapIt).second.push_back( n2 );
8736 // add node to be inserted into elem
8737 (*insertMapIt).second.push_back( nIns );
8738 next[ 1 - intoBord ] = true;
8741 // go to the next segment
8742 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8743 if ( next[ iBord ] ) {
8744 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8746 nPrev[ iBord ] = *nIt[ iBord ];
8747 nIt[ iBord ]++; i[ iBord ]++;
8751 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8753 // perform insertion of nodes into elements
8755 for (insertMapIt = insertMap.begin();
8756 insertMapIt != insertMap.end();
8759 const SMDS_MeshElement* elem = (*insertMapIt).first;
8760 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8761 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8762 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8764 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8766 if ( !theSideIsFreeBorder ) {
8767 // look for and insert nodes into the faces adjacent to elem
8769 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8771 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8776 if (toCreatePolyedrs) {
8777 // perform insertion into the links of adjacent volumes
8778 UpdateVolumes(n1, n2, nodeList);
8784 } // end: insert new nodes
8786 MergeNodes ( nodeGroupsToMerge );
8791 //=======================================================================
8792 //function : InsertNodesIntoLink
8793 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8794 // and theBetweenNode2 and split theElement
8795 //=======================================================================
8797 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8798 const SMDS_MeshNode* theBetweenNode1,
8799 const SMDS_MeshNode* theBetweenNode2,
8800 list<const SMDS_MeshNode*>& theNodesToInsert,
8801 const bool toCreatePoly)
8803 if ( theFace->GetType() != SMDSAbs_Face ) return;
8805 // find indices of 2 link nodes and of the rest nodes
8806 int iNode = 0, il1, il2, i3, i4;
8807 il1 = il2 = i3 = i4 = -1;
8808 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8809 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8811 if(theFace->IsQuadratic()) {
8812 const SMDS_VtkFace* F =
8813 dynamic_cast<const SMDS_VtkFace*>(theFace);
8814 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8815 // use special nodes iterator
8816 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8817 while( anIter->more() ) {
8818 const SMDS_MeshNode* n = cast2Node(anIter->next());
8819 if ( n == theBetweenNode1 )
8821 else if ( n == theBetweenNode2 )
8827 nodes[ iNode++ ] = n;
8831 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8832 while ( nodeIt->more() ) {
8833 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8834 if ( n == theBetweenNode1 )
8836 else if ( n == theBetweenNode2 )
8842 nodes[ iNode++ ] = n;
8845 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8848 // arrange link nodes to go one after another regarding the face orientation
8849 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8850 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8855 aNodesToInsert.reverse();
8857 // check that not link nodes of a quadrangles are in good order
8858 int nbFaceNodes = theFace->NbNodes();
8859 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8865 if (toCreatePoly || theFace->IsPoly()) {
8868 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8870 // add nodes of face up to first node of link
8873 if(theFace->IsQuadratic()) {
8874 const SMDS_VtkFace* F =
8875 dynamic_cast<const SMDS_VtkFace*>(theFace);
8876 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8877 // use special nodes iterator
8878 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8879 while( anIter->more() && !isFLN ) {
8880 const SMDS_MeshNode* n = cast2Node(anIter->next());
8881 poly_nodes[iNode++] = n;
8882 if (n == nodes[il1]) {
8886 // add nodes to insert
8887 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8888 for (; nIt != aNodesToInsert.end(); nIt++) {
8889 poly_nodes[iNode++] = *nIt;
8891 // add nodes of face starting from last node of link
8892 while ( anIter->more() ) {
8893 poly_nodes[iNode++] = cast2Node(anIter->next());
8897 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8898 while ( nodeIt->more() && !isFLN ) {
8899 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8900 poly_nodes[iNode++] = n;
8901 if (n == nodes[il1]) {
8905 // add nodes to insert
8906 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8907 for (; nIt != aNodesToInsert.end(); nIt++) {
8908 poly_nodes[iNode++] = *nIt;
8910 // add nodes of face starting from last node of link
8911 while ( nodeIt->more() ) {
8912 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8913 poly_nodes[iNode++] = n;
8917 // edit or replace the face
8918 SMESHDS_Mesh *aMesh = GetMeshDS();
8920 if (theFace->IsPoly()) {
8921 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8924 int aShapeId = FindShape( theFace );
8926 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8927 myLastCreatedElems.Append(newElem);
8928 if ( aShapeId && newElem )
8929 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8931 aMesh->RemoveElement(theFace);
8936 SMESHDS_Mesh *aMesh = GetMeshDS();
8937 if( !theFace->IsQuadratic() ) {
8939 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8940 int nbLinkNodes = 2 + aNodesToInsert.size();
8941 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8942 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8943 linkNodes[ 0 ] = nodes[ il1 ];
8944 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8945 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8946 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8947 linkNodes[ iNode++ ] = *nIt;
8949 // decide how to split a quadrangle: compare possible variants
8950 // and choose which of splits to be a quadrangle
8951 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8952 if ( nbFaceNodes == 3 ) {
8953 iBestQuad = nbSplits;
8956 else if ( nbFaceNodes == 4 ) {
8957 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8958 double aBestRate = DBL_MAX;
8959 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8961 double aBadRate = 0;
8962 // evaluate elements quality
8963 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8964 if ( iSplit == iQuad ) {
8965 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8969 aBadRate += getBadRate( &quad, aCrit );
8972 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8974 nodes[ iSplit < iQuad ? i4 : i3 ]);
8975 aBadRate += getBadRate( &tria, aCrit );
8979 if ( aBadRate < aBestRate ) {
8981 aBestRate = aBadRate;
8986 // create new elements
8987 int aShapeId = FindShape( theFace );
8990 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8991 SMDS_MeshElement* newElem = 0;
8992 if ( iSplit == iBestQuad )
8993 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8998 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9000 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9001 myLastCreatedElems.Append(newElem);
9002 if ( aShapeId && newElem )
9003 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9006 // change nodes of theFace
9007 const SMDS_MeshNode* newNodes[ 4 ];
9008 newNodes[ 0 ] = linkNodes[ i1 ];
9009 newNodes[ 1 ] = linkNodes[ i2 ];
9010 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9011 newNodes[ 3 ] = nodes[ i4 ];
9012 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9013 const SMDS_MeshElement* newElem = 0;
9014 if (iSplit == iBestQuad)
9015 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9017 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9018 myLastCreatedElems.Append(newElem);
9019 if ( aShapeId && newElem )
9020 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9021 } // end if(!theFace->IsQuadratic())
9022 else { // theFace is quadratic
9023 // we have to split theFace on simple triangles and one simple quadrangle
9025 int nbshift = tmp*2;
9026 // shift nodes in nodes[] by nbshift
9028 for(i=0; i<nbshift; i++) {
9029 const SMDS_MeshNode* n = nodes[0];
9030 for(j=0; j<nbFaceNodes-1; j++) {
9031 nodes[j] = nodes[j+1];
9033 nodes[nbFaceNodes-1] = n;
9035 il1 = il1 - nbshift;
9036 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9037 // n0 n1 n2 n0 n1 n2
9038 // +-----+-----+ +-----+-----+
9047 // create new elements
9048 int aShapeId = FindShape( theFace );
9051 if(nbFaceNodes==6) { // quadratic triangle
9052 SMDS_MeshElement* newElem =
9053 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9054 myLastCreatedElems.Append(newElem);
9055 if ( aShapeId && newElem )
9056 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9057 if(theFace->IsMediumNode(nodes[il1])) {
9058 // create quadrangle
9059 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9060 myLastCreatedElems.Append(newElem);
9061 if ( aShapeId && newElem )
9062 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9068 // create quadrangle
9069 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9070 myLastCreatedElems.Append(newElem);
9071 if ( aShapeId && newElem )
9072 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9078 else { // nbFaceNodes==8 - quadratic quadrangle
9079 SMDS_MeshElement* newElem =
9080 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9081 myLastCreatedElems.Append(newElem);
9082 if ( aShapeId && newElem )
9083 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9084 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9085 myLastCreatedElems.Append(newElem);
9086 if ( aShapeId && newElem )
9087 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9088 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9089 myLastCreatedElems.Append(newElem);
9090 if ( aShapeId && newElem )
9091 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9092 if(theFace->IsMediumNode(nodes[il1])) {
9093 // create quadrangle
9094 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9095 myLastCreatedElems.Append(newElem);
9096 if ( aShapeId && newElem )
9097 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9103 // create quadrangle
9104 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9105 myLastCreatedElems.Append(newElem);
9106 if ( aShapeId && newElem )
9107 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9113 // create needed triangles using n1,n2,n3 and inserted nodes
9114 int nbn = 2 + aNodesToInsert.size();
9115 //const SMDS_MeshNode* aNodes[nbn];
9116 vector<const SMDS_MeshNode*> aNodes(nbn);
9117 aNodes[0] = nodes[n1];
9118 aNodes[nbn-1] = nodes[n2];
9119 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9120 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9121 aNodes[iNode++] = *nIt;
9123 for(i=1; i<nbn; i++) {
9124 SMDS_MeshElement* newElem =
9125 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9126 myLastCreatedElems.Append(newElem);
9127 if ( aShapeId && newElem )
9128 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9132 aMesh->RemoveElement(theFace);
9135 //=======================================================================
9136 //function : UpdateVolumes
9138 //=======================================================================
9139 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9140 const SMDS_MeshNode* theBetweenNode2,
9141 list<const SMDS_MeshNode*>& theNodesToInsert)
9143 myLastCreatedElems.Clear();
9144 myLastCreatedNodes.Clear();
9146 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9147 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9148 const SMDS_MeshElement* elem = invElemIt->next();
9150 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9151 SMDS_VolumeTool aVolume (elem);
9152 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9155 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9156 int iface, nbFaces = aVolume.NbFaces();
9157 vector<const SMDS_MeshNode *> poly_nodes;
9158 vector<int> quantities (nbFaces);
9160 for (iface = 0; iface < nbFaces; iface++) {
9161 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9162 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9163 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9165 for (int inode = 0; inode < nbFaceNodes; inode++) {
9166 poly_nodes.push_back(faceNodes[inode]);
9168 if (nbInserted == 0) {
9169 if (faceNodes[inode] == theBetweenNode1) {
9170 if (faceNodes[inode + 1] == theBetweenNode2) {
9171 nbInserted = theNodesToInsert.size();
9173 // add nodes to insert
9174 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9175 for (; nIt != theNodesToInsert.end(); nIt++) {
9176 poly_nodes.push_back(*nIt);
9180 else if (faceNodes[inode] == theBetweenNode2) {
9181 if (faceNodes[inode + 1] == theBetweenNode1) {
9182 nbInserted = theNodesToInsert.size();
9184 // add nodes to insert in reversed order
9185 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9187 for (; nIt != theNodesToInsert.begin(); nIt--) {
9188 poly_nodes.push_back(*nIt);
9190 poly_nodes.push_back(*nIt);
9197 quantities[iface] = nbFaceNodes + nbInserted;
9200 // Replace or update the volume
9201 SMESHDS_Mesh *aMesh = GetMeshDS();
9203 if (elem->IsPoly()) {
9204 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9208 int aShapeId = FindShape( elem );
9210 SMDS_MeshElement* newElem =
9211 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9212 myLastCreatedElems.Append(newElem);
9213 if (aShapeId && newElem)
9214 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9216 aMesh->RemoveElement(elem);
9221 //=======================================================================
9223 * \brief Convert elements contained in a submesh to quadratic
9224 * \return int - nb of checked elements
9226 //=======================================================================
9228 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9229 SMESH_MesherHelper& theHelper,
9230 const bool theForce3d)
9233 if( !theSm ) return nbElem;
9235 vector<int> nbNodeInFaces;
9236 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9237 while(ElemItr->more())
9240 const SMDS_MeshElement* elem = ElemItr->next();
9241 if( !elem || elem->IsQuadratic() ) continue;
9243 int id = elem->GetID();
9244 int nbNodes = elem->NbNodes();
9245 SMDSAbs_ElementType aType = elem->GetType();
9247 vector<const SMDS_MeshNode *> nodes (elem->begin_nodes(), elem->end_nodes());
9248 if ( elem->GetEntityType() == SMDSEntity_Polyhedra )
9249 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9251 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9253 const SMDS_MeshElement* NewElem = 0;
9259 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9267 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9270 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9273 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9278 case SMDSAbs_Volume :
9283 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9286 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9289 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9292 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9293 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9296 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9303 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9305 theSm->AddElement( NewElem );
9307 // if (!GetMeshDS()->isCompacted())
9308 // GetMeshDS()->compactMesh();
9312 //=======================================================================
9313 //function : ConvertToQuadratic
9315 //=======================================================================
9316 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9318 SMESHDS_Mesh* meshDS = GetMeshDS();
9320 SMESH_MesherHelper aHelper(*myMesh);
9321 aHelper.SetIsQuadratic( true );
9323 int nbCheckedElems = 0;
9324 if ( myMesh->HasShapeToMesh() )
9326 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9328 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9329 while ( smIt->more() ) {
9330 SMESH_subMesh* sm = smIt->next();
9331 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9332 aHelper.SetSubShape( sm->GetSubShape() );
9333 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9338 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9339 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9341 SMESHDS_SubMesh *smDS = 0;
9342 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9343 while(aEdgeItr->more())
9345 const SMDS_MeshEdge* edge = aEdgeItr->next();
9346 if(edge && !edge->IsQuadratic())
9348 int id = edge->GetID();
9349 //MESSAGE("edge->GetID() " << id);
9350 const SMDS_MeshNode* n1 = edge->GetNode(0);
9351 const SMDS_MeshNode* n2 = edge->GetNode(1);
9353 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9355 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9356 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9359 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9360 while(aFaceItr->more())
9362 const SMDS_MeshFace* face = aFaceItr->next();
9363 if(!face || face->IsQuadratic() ) continue;
9365 int id = face->GetID();
9366 int nbNodes = face->NbNodes();
9367 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9369 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9371 SMDS_MeshFace * NewFace = 0;
9375 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9378 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9381 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9383 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9385 vector<int> nbNodeInFaces;
9386 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9387 while(aVolumeItr->more())
9389 const SMDS_MeshVolume* volume = aVolumeItr->next();
9390 if(!volume || volume->IsQuadratic() ) continue;
9392 int id = volume->GetID();
9393 int nbNodes = volume->NbNodes();
9394 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9395 if ( volume->GetEntityType() == SMDSEntity_Polyhedra )
9396 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9398 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9400 SMDS_MeshVolume * NewVolume = 0;
9404 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9405 nodes[3], id, theForce3d );
9408 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9409 nodes[3], nodes[4], id, theForce3d);
9412 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9413 nodes[3], nodes[4], nodes[5], id, theForce3d);
9416 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9417 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9420 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9422 ReplaceElemInGroups(volume, NewVolume, meshDS);
9427 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9428 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9429 aHelper.FixQuadraticElements();
9433 //================================================================================
9435 * \brief Makes given elements quadratic
9436 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9437 * \param theElements - elements to make quadratic
9439 //================================================================================
9441 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9442 TIDSortedElemSet& theElements)
9444 if ( theElements.empty() ) return;
9446 // we believe that all theElements are of the same type
9447 SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9449 // get all nodes shared by theElements
9450 TIDSortedNodeSet allNodes;
9451 TIDSortedElemSet::iterator eIt = theElements.begin();
9452 for ( ; eIt != theElements.end(); ++eIt )
9453 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9455 // complete theElements with elements of lower dim whose all nodes are in allNodes
9457 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9458 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9459 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9460 for ( ; nIt != allNodes.end(); ++nIt )
9462 const SMDS_MeshNode* n = *nIt;
9463 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9464 while ( invIt->more() )
9466 const SMDS_MeshElement* e = invIt->next();
9467 if ( e->IsQuadratic() )
9469 quadAdjacentElems[ e->GetType() ].insert( e );
9472 if ( e->GetType() >= elemType )
9474 continue; // same type of more complex linear element
9477 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9478 continue; // e is already checked
9482 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9483 while ( nodeIt->more() && allIn )
9484 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9486 theElements.insert(e );
9490 SMESH_MesherHelper helper(*myMesh);
9491 helper.SetIsQuadratic( true );
9493 // add links of quadratic adjacent elements to the helper
9495 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9496 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9497 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9499 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9501 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9502 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9503 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9505 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9507 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9508 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9509 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9511 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9514 // make quadratic elements instead of linear ones
9516 SMESHDS_Mesh* meshDS = GetMeshDS();
9517 SMESHDS_SubMesh* smDS = 0;
9518 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9520 const SMDS_MeshElement* elem = *eIt;
9521 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9524 int id = elem->GetID();
9525 SMDSAbs_ElementType type = elem->GetType();
9526 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9528 if ( !smDS || !smDS->Contains( elem ))
9529 smDS = meshDS->MeshElements( elem->getshapeId() );
9530 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9532 SMDS_MeshElement * newElem = 0;
9533 switch( nodes.size() )
9535 case 4: // cases for most multiple element types go first (for optimization)
9536 if ( type == SMDSAbs_Volume )
9537 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9539 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9542 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9543 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9546 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9549 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9552 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9553 nodes[4], id, theForce3d);
9556 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9557 nodes[4], nodes[5], id, theForce3d);
9561 ReplaceElemInGroups( elem, newElem, meshDS);
9562 if( newElem && smDS )
9563 smDS->AddElement( newElem );
9566 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9567 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9568 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9569 helper.FixQuadraticElements();
9573 //=======================================================================
9575 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9576 * \return int - nb of checked elements
9578 //=======================================================================
9580 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9581 SMDS_ElemIteratorPtr theItr,
9582 const int theShapeID)
9585 SMESHDS_Mesh* meshDS = GetMeshDS();
9587 while( theItr->more() )
9589 const SMDS_MeshElement* elem = theItr->next();
9591 if( elem && elem->IsQuadratic())
9593 int id = elem->GetID();
9594 int nbCornerNodes = elem->NbCornerNodes();
9595 SMDSAbs_ElementType aType = elem->GetType();
9597 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9599 //remove a quadratic element
9600 if ( !theSm || !theSm->Contains( elem ))
9601 theSm = meshDS->MeshElements( elem->getshapeId() );
9602 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9604 // remove medium nodes
9605 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9606 if ( nodes[i]->NbInverseElements() == 0 )
9607 meshDS->RemoveFreeNode( nodes[i], theSm );
9609 // add a linear element
9610 nodes.resize( nbCornerNodes );
9611 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9612 ReplaceElemInGroups(elem, newElem, meshDS);
9613 if( theSm && newElem )
9614 theSm->AddElement( newElem );
9620 //=======================================================================
9621 //function : ConvertFromQuadratic
9623 //=======================================================================
9625 bool SMESH_MeshEditor::ConvertFromQuadratic()
9627 int nbCheckedElems = 0;
9628 if ( myMesh->HasShapeToMesh() )
9630 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9632 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9633 while ( smIt->more() ) {
9634 SMESH_subMesh* sm = smIt->next();
9635 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9636 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9642 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9643 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9645 SMESHDS_SubMesh *aSM = 0;
9646 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9654 //================================================================================
9656 * \brief Return true if all medium nodes of the element are in the node set
9658 //================================================================================
9660 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9662 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9663 if ( !nodeSet.count( elem->GetNode(i) ))
9669 //================================================================================
9671 * \brief Makes given elements linear
9673 //================================================================================
9675 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9677 if ( theElements.empty() ) return;
9679 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9680 set<int> mediumNodeIDs;
9681 TIDSortedElemSet::iterator eIt = theElements.begin();
9682 for ( ; eIt != theElements.end(); ++eIt )
9684 const SMDS_MeshElement* e = *eIt;
9685 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9686 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9689 // replace given elements by linear ones
9690 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9691 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9692 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9694 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9695 // except those elements sharing medium nodes of quadratic element whose medium nodes
9696 // are not all in mediumNodeIDs
9698 // get remaining medium nodes
9699 TIDSortedNodeSet mediumNodes;
9700 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9701 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9702 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9703 mediumNodes.insert( mediumNodes.end(), n );
9705 // find more quadratic elements to convert
9706 TIDSortedElemSet moreElemsToConvert;
9707 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9708 for ( ; nIt != mediumNodes.end(); ++nIt )
9710 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9711 while ( invIt->more() )
9713 const SMDS_MeshElement* e = invIt->next();
9714 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9716 // find a more complex element including e and
9717 // whose medium nodes are not in mediumNodes
9718 bool complexFound = false;
9719 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9721 SMDS_ElemIteratorPtr invIt2 =
9722 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9723 while ( invIt2->more() )
9725 const SMDS_MeshElement* eComplex = invIt2->next();
9726 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9728 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9729 if ( nbCommonNodes == e->NbNodes())
9731 complexFound = true;
9732 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9738 if ( !complexFound )
9739 moreElemsToConvert.insert( e );
9743 elemIt = SMDS_ElemIteratorPtr
9744 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9745 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9748 //=======================================================================
9749 //function : SewSideElements
9751 //=======================================================================
9753 SMESH_MeshEditor::Sew_Error
9754 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9755 TIDSortedElemSet& theSide2,
9756 const SMDS_MeshNode* theFirstNode1,
9757 const SMDS_MeshNode* theFirstNode2,
9758 const SMDS_MeshNode* theSecondNode1,
9759 const SMDS_MeshNode* theSecondNode2)
9761 myLastCreatedElems.Clear();
9762 myLastCreatedNodes.Clear();
9764 MESSAGE ("::::SewSideElements()");
9765 if ( theSide1.size() != theSide2.size() )
9766 return SEW_DIFF_NB_OF_ELEMENTS;
9768 Sew_Error aResult = SEW_OK;
9770 // 1. Build set of faces representing each side
9771 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9772 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9774 // =======================================================================
9775 // 1. Build set of faces representing each side:
9776 // =======================================================================
9777 // a. build set of nodes belonging to faces
9778 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9779 // c. create temporary faces representing side of volumes if correspondent
9780 // face does not exist
9782 SMESHDS_Mesh* aMesh = GetMeshDS();
9783 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9784 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9785 set<const SMDS_MeshElement*> faceSet1, faceSet2;
9786 set<const SMDS_MeshElement*> volSet1, volSet2;
9787 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9788 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9789 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9790 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9791 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9792 int iSide, iFace, iNode;
9794 list<const SMDS_MeshElement* > tempFaceList;
9795 for ( iSide = 0; iSide < 2; iSide++ ) {
9796 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9797 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9798 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9799 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9800 set<const SMDS_MeshElement*>::iterator vIt;
9801 TIDSortedElemSet::iterator eIt;
9802 set<const SMDS_MeshNode*>::iterator nIt;
9804 // check that given nodes belong to given elements
9805 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9806 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9807 int firstIndex = -1, secondIndex = -1;
9808 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9809 const SMDS_MeshElement* elem = *eIt;
9810 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9811 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9812 if ( firstIndex > -1 && secondIndex > -1 ) break;
9814 if ( firstIndex < 0 || secondIndex < 0 ) {
9815 // we can simply return until temporary faces created
9816 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9819 // -----------------------------------------------------------
9820 // 1a. Collect nodes of existing faces
9821 // and build set of face nodes in order to detect missing
9822 // faces corresponding to sides of volumes
9823 // -----------------------------------------------------------
9825 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9827 // loop on the given element of a side
9828 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9829 //const SMDS_MeshElement* elem = *eIt;
9830 const SMDS_MeshElement* elem = *eIt;
9831 if ( elem->GetType() == SMDSAbs_Face ) {
9832 faceSet->insert( elem );
9833 set <const SMDS_MeshNode*> faceNodeSet;
9834 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9835 while ( nodeIt->more() ) {
9836 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9837 nodeSet->insert( n );
9838 faceNodeSet.insert( n );
9840 setOfFaceNodeSet.insert( faceNodeSet );
9842 else if ( elem->GetType() == SMDSAbs_Volume )
9843 volSet->insert( elem );
9845 // ------------------------------------------------------------------------------
9846 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9847 // ------------------------------------------------------------------------------
9849 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9850 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9851 while ( fIt->more() ) { // loop on faces sharing a node
9852 const SMDS_MeshElement* f = fIt->next();
9853 if ( faceSet->find( f ) == faceSet->end() ) {
9854 // check if all nodes are in nodeSet and
9855 // complete setOfFaceNodeSet if they are
9856 set <const SMDS_MeshNode*> faceNodeSet;
9857 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9858 bool allInSet = true;
9859 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9860 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9861 if ( nodeSet->find( n ) == nodeSet->end() )
9864 faceNodeSet.insert( n );
9867 faceSet->insert( f );
9868 setOfFaceNodeSet.insert( faceNodeSet );
9874 // -------------------------------------------------------------------------
9875 // 1c. Create temporary faces representing sides of volumes if correspondent
9876 // face does not exist
9877 // -------------------------------------------------------------------------
9879 if ( !volSet->empty() ) {
9880 //int nodeSetSize = nodeSet->size();
9882 // loop on given volumes
9883 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9884 SMDS_VolumeTool vol (*vIt);
9885 // loop on volume faces: find free faces
9886 // --------------------------------------
9887 list<const SMDS_MeshElement* > freeFaceList;
9888 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9889 if ( !vol.IsFreeFace( iFace ))
9891 // check if there is already a face with same nodes in a face set
9892 const SMDS_MeshElement* aFreeFace = 0;
9893 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9894 int nbNodes = vol.NbFaceNodes( iFace );
9895 set <const SMDS_MeshNode*> faceNodeSet;
9896 vol.GetFaceNodes( iFace, faceNodeSet );
9897 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9899 // no such a face is given but it still can exist, check it
9900 if ( nbNodes == 3 ) {
9901 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9903 else if ( nbNodes == 4 ) {
9904 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9907 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9908 aFreeFace = aMesh->FindFace(poly_nodes);
9912 // create a temporary face
9913 if ( nbNodes == 3 ) {
9914 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9915 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9917 else if ( nbNodes == 4 ) {
9918 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9919 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9922 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9923 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9924 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9928 freeFaceList.push_back( aFreeFace );
9929 tempFaceList.push_back( aFreeFace );
9932 } // loop on faces of a volume
9934 // choose one of several free faces
9935 // --------------------------------------
9936 if ( freeFaceList.size() > 1 ) {
9937 // choose a face having max nb of nodes shared by other elems of a side
9938 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9939 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9940 while ( fIt != freeFaceList.end() ) { // loop on free faces
9941 int nbSharedNodes = 0;
9942 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9943 while ( nodeIt->more() ) { // loop on free face nodes
9944 const SMDS_MeshNode* n =
9945 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9946 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9947 while ( invElemIt->more() ) {
9948 const SMDS_MeshElement* e = invElemIt->next();
9949 if ( faceSet->find( e ) != faceSet->end() )
9951 if ( elemSet->find( e ) != elemSet->end() )
9955 if ( nbSharedNodes >= maxNbNodes ) {
9956 maxNbNodes = nbSharedNodes;
9960 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9962 if ( freeFaceList.size() > 1 )
9964 // could not choose one face, use another way
9965 // choose a face most close to the bary center of the opposite side
9966 gp_XYZ aBC( 0., 0., 0. );
9967 set <const SMDS_MeshNode*> addedNodes;
9968 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9969 eIt = elemSet2->begin();
9970 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9971 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9972 while ( nodeIt->more() ) { // loop on free face nodes
9973 const SMDS_MeshNode* n =
9974 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9975 if ( addedNodes.insert( n ).second )
9976 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9979 aBC /= addedNodes.size();
9980 double minDist = DBL_MAX;
9981 fIt = freeFaceList.begin();
9982 while ( fIt != freeFaceList.end() ) { // loop on free faces
9984 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9985 while ( nodeIt->more() ) { // loop on free face nodes
9986 const SMDS_MeshNode* n =
9987 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9988 gp_XYZ p( n->X(),n->Y(),n->Z() );
9989 dist += ( aBC - p ).SquareModulus();
9991 if ( dist < minDist ) {
9993 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9996 fIt = freeFaceList.erase( fIt++ );
9999 } // choose one of several free faces of a volume
10001 if ( freeFaceList.size() == 1 ) {
10002 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10003 faceSet->insert( aFreeFace );
10004 // complete a node set with nodes of a found free face
10005 // for ( iNode = 0; iNode < ; iNode++ )
10006 // nodeSet->insert( fNodes[ iNode ] );
10009 } // loop on volumes of a side
10011 // // complete a set of faces if new nodes in a nodeSet appeared
10012 // // ----------------------------------------------------------
10013 // if ( nodeSetSize != nodeSet->size() ) {
10014 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10015 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10016 // while ( fIt->more() ) { // loop on faces sharing a node
10017 // const SMDS_MeshElement* f = fIt->next();
10018 // if ( faceSet->find( f ) == faceSet->end() ) {
10019 // // check if all nodes are in nodeSet and
10020 // // complete setOfFaceNodeSet if they are
10021 // set <const SMDS_MeshNode*> faceNodeSet;
10022 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10023 // bool allInSet = true;
10024 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10025 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10026 // if ( nodeSet->find( n ) == nodeSet->end() )
10027 // allInSet = false;
10029 // faceNodeSet.insert( n );
10031 // if ( allInSet ) {
10032 // faceSet->insert( f );
10033 // setOfFaceNodeSet.insert( faceNodeSet );
10039 } // Create temporary faces, if there are volumes given
10042 if ( faceSet1.size() != faceSet2.size() ) {
10043 // delete temporary faces: they are in reverseElements of actual nodes
10044 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10045 // while ( tmpFaceIt->more() )
10046 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10047 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10048 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10049 // aMesh->RemoveElement(*tmpFaceIt);
10050 MESSAGE("Diff nb of faces");
10051 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10054 // ============================================================
10055 // 2. Find nodes to merge:
10056 // bind a node to remove to a node to put instead
10057 // ============================================================
10059 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10060 if ( theFirstNode1 != theFirstNode2 )
10061 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
10062 if ( theSecondNode1 != theSecondNode2 )
10063 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
10065 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10066 set< long > linkIdSet; // links to process
10067 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10069 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10070 list< NLink > linkList[2];
10071 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10072 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10073 // loop on links in linkList; find faces by links and append links
10074 // of the found faces to linkList
10075 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10076 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10077 NLink link[] = { *linkIt[0], *linkIt[1] };
10078 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10079 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
10082 // by links, find faces in the face sets,
10083 // and find indices of link nodes in the found faces;
10084 // in a face set, there is only one or no face sharing a link
10085 // ---------------------------------------------------------------
10087 const SMDS_MeshElement* face[] = { 0, 0 };
10088 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
10089 vector<const SMDS_MeshNode*> fnodes1(9);
10090 vector<const SMDS_MeshNode*> fnodes2(9);
10091 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
10092 vector<const SMDS_MeshNode*> notLinkNodes1(6);
10093 vector<const SMDS_MeshNode*> notLinkNodes2(6);
10094 int iLinkNode[2][2];
10095 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10096 const SMDS_MeshNode* n1 = link[iSide].first;
10097 const SMDS_MeshNode* n2 = link[iSide].second;
10098 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10099 set< const SMDS_MeshElement* > fMap;
10100 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
10101 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
10102 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10103 while ( fIt->more() ) { // loop on faces sharing a node
10104 const SMDS_MeshElement* f = fIt->next();
10105 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10106 ! fMap.insert( f ).second ) // f encounters twice
10108 if ( face[ iSide ] ) {
10109 MESSAGE( "2 faces per link " );
10110 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
10114 faceSet->erase( f );
10115 // get face nodes and find ones of a link
10120 fnodes1.resize(f->NbNodes()+1);
10121 notLinkNodes1.resize(f->NbNodes()-2);
10124 fnodes2.resize(f->NbNodes()+1);
10125 notLinkNodes2.resize(f->NbNodes()-2);
10128 if(!f->IsQuadratic()) {
10129 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
10130 while ( nIt->more() ) {
10131 const SMDS_MeshNode* n =
10132 static_cast<const SMDS_MeshNode*>( nIt->next() );
10134 iLinkNode[ iSide ][ 0 ] = iNode;
10136 else if ( n == n2 ) {
10137 iLinkNode[ iSide ][ 1 ] = iNode;
10139 //else if ( notLinkNodes[ iSide ][ 0 ] )
10140 // notLinkNodes[ iSide ][ 1 ] = n;
10142 // notLinkNodes[ iSide ][ 0 ] = n;
10146 notLinkNodes1[nbl] = n;
10147 //notLinkNodes1.push_back(n);
10149 notLinkNodes2[nbl] = n;
10150 //notLinkNodes2.push_back(n);
10152 //faceNodes[ iSide ][ iNode++ ] = n;
10154 fnodes1[iNode++] = n;
10157 fnodes2[iNode++] = n;
10161 else { // f->IsQuadratic()
10162 const SMDS_VtkFace* F =
10163 dynamic_cast<const SMDS_VtkFace*>(f);
10164 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10165 // use special nodes iterator
10166 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
10167 while ( anIter->more() ) {
10168 const SMDS_MeshNode* n =
10169 static_cast<const SMDS_MeshNode*>( anIter->next() );
10171 iLinkNode[ iSide ][ 0 ] = iNode;
10173 else if ( n == n2 ) {
10174 iLinkNode[ iSide ][ 1 ] = iNode;
10179 notLinkNodes1[nbl] = n;
10182 notLinkNodes2[nbl] = n;
10186 fnodes1[iNode++] = n;
10189 fnodes2[iNode++] = n;
10193 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
10195 fnodes1[iNode] = fnodes1[0];
10198 fnodes2[iNode] = fnodes1[0];
10205 // check similarity of elements of the sides
10206 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10207 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10208 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10209 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10212 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10214 break; // do not return because it s necessary to remove tmp faces
10217 // set nodes to merge
10218 // -------------------
10220 if ( face[0] && face[1] ) {
10221 int nbNodes = face[0]->NbNodes();
10222 if ( nbNodes != face[1]->NbNodes() ) {
10223 MESSAGE("Diff nb of face nodes");
10224 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10225 break; // do not return because it s necessary to remove tmp faces
10227 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
10228 if ( nbNodes == 3 ) {
10229 //nReplaceMap.insert( TNodeNodeMap::value_type
10230 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10231 nReplaceMap.insert( TNodeNodeMap::value_type
10232 ( notLinkNodes1[0], notLinkNodes2[0] ));
10235 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10236 // analyse link orientation in faces
10237 int i1 = iLinkNode[ iSide ][ 0 ];
10238 int i2 = iLinkNode[ iSide ][ 1 ];
10239 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10240 // if notLinkNodes are the first and the last ones, then
10241 // their order does not correspond to the link orientation
10242 if (( i1 == 1 && i2 == 2 ) ||
10243 ( i1 == 2 && i2 == 1 ))
10244 reverse[ iSide ] = !reverse[ iSide ];
10246 if ( reverse[0] == reverse[1] ) {
10247 //nReplaceMap.insert( TNodeNodeMap::value_type
10248 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10249 //nReplaceMap.insert( TNodeNodeMap::value_type
10250 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
10251 for(int nn=0; nn<nbNodes-2; nn++) {
10252 nReplaceMap.insert( TNodeNodeMap::value_type
10253 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
10257 //nReplaceMap.insert( TNodeNodeMap::value_type
10258 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
10259 //nReplaceMap.insert( TNodeNodeMap::value_type
10260 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
10261 for(int nn=0; nn<nbNodes-2; nn++) {
10262 nReplaceMap.insert( TNodeNodeMap::value_type
10263 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
10268 // add other links of the faces to linkList
10269 // -----------------------------------------
10271 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
10272 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10273 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
10274 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
10275 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10276 if ( !iter_isnew.second ) { // already in a set: no need to process
10277 linkIdSet.erase( iter_isnew.first );
10279 else // new in set == encountered for the first time: add
10281 //const SMDS_MeshNode* n1 = nodes[ iNode ];
10282 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
10283 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
10284 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
10285 linkList[0].push_back ( NLink( n1, n2 ));
10286 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10290 } // loop on link lists
10292 if ( aResult == SEW_OK &&
10293 ( linkIt[0] != linkList[0].end() ||
10294 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10295 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10296 " " << (faceSetPtr[1]->empty()));
10297 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10300 // ====================================================================
10301 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10302 // ====================================================================
10304 // delete temporary faces: they are in reverseElements of actual nodes
10305 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10306 // while ( tmpFaceIt->more() )
10307 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10308 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10309 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10310 // aMesh->RemoveElement(*tmpFaceIt);
10312 if ( aResult != SEW_OK)
10315 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10316 // loop on nodes replacement map
10317 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10318 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10319 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10320 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10321 nodeIDsToRemove.push_back( nToRemove->GetID() );
10322 // loop on elements sharing nToRemove
10323 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10324 while ( invElemIt->more() ) {
10325 const SMDS_MeshElement* e = invElemIt->next();
10326 // get a new suite of nodes: make replacement
10327 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10328 vector< const SMDS_MeshNode*> nodes( nbNodes );
10329 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10330 while ( nIt->more() ) {
10331 const SMDS_MeshNode* n =
10332 static_cast<const SMDS_MeshNode*>( nIt->next() );
10333 nnIt = nReplaceMap.find( n );
10334 if ( nnIt != nReplaceMap.end() ) {
10336 n = (*nnIt).second;
10340 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10341 // elemIDsToRemove.push_back( e->GetID() );
10345 SMDSAbs_ElementType etyp = e->GetType();
10346 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10349 myLastCreatedElems.Append(newElem);
10350 AddToSameGroups(newElem, e, aMesh);
10351 int aShapeId = e->getshapeId();
10354 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10357 aMesh->RemoveElement(e);
10362 Remove( nodeIDsToRemove, true );
10367 //================================================================================
10369 * \brief Find corresponding nodes in two sets of faces
10370 * \param theSide1 - first face set
10371 * \param theSide2 - second first face
10372 * \param theFirstNode1 - a boundary node of set 1
10373 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10374 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10375 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10376 * \param nReplaceMap - output map of corresponding nodes
10377 * \return bool - is a success or not
10379 //================================================================================
10382 //#define DEBUG_MATCHING_NODES
10385 SMESH_MeshEditor::Sew_Error
10386 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10387 set<const SMDS_MeshElement*>& theSide2,
10388 const SMDS_MeshNode* theFirstNode1,
10389 const SMDS_MeshNode* theFirstNode2,
10390 const SMDS_MeshNode* theSecondNode1,
10391 const SMDS_MeshNode* theSecondNode2,
10392 TNodeNodeMap & nReplaceMap)
10394 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10396 nReplaceMap.clear();
10397 if ( theFirstNode1 != theFirstNode2 )
10398 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10399 if ( theSecondNode1 != theSecondNode2 )
10400 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10402 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10403 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10405 list< NLink > linkList[2];
10406 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10407 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10409 // loop on links in linkList; find faces by links and append links
10410 // of the found faces to linkList
10411 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10412 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10413 NLink link[] = { *linkIt[0], *linkIt[1] };
10414 if ( linkSet.find( link[0] ) == linkSet.end() )
10417 // by links, find faces in the face sets,
10418 // and find indices of link nodes in the found faces;
10419 // in a face set, there is only one or no face sharing a link
10420 // ---------------------------------------------------------------
10422 const SMDS_MeshElement* face[] = { 0, 0 };
10423 list<const SMDS_MeshNode*> notLinkNodes[2];
10424 //bool reverse[] = { false, false }; // order of notLinkNodes
10426 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10428 const SMDS_MeshNode* n1 = link[iSide].first;
10429 const SMDS_MeshNode* n2 = link[iSide].second;
10430 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10431 set< const SMDS_MeshElement* > facesOfNode1;
10432 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10434 // during a loop of the first node, we find all faces around n1,
10435 // during a loop of the second node, we find one face sharing both n1 and n2
10436 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10437 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10438 while ( fIt->more() ) { // loop on faces sharing a node
10439 const SMDS_MeshElement* f = fIt->next();
10440 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10441 ! facesOfNode1.insert( f ).second ) // f encounters twice
10443 if ( face[ iSide ] ) {
10444 MESSAGE( "2 faces per link " );
10445 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10448 faceSet->erase( f );
10450 // get not link nodes
10451 int nbN = f->NbNodes();
10452 if ( f->IsQuadratic() )
10454 nbNodes[ iSide ] = nbN;
10455 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10456 int i1 = f->GetNodeIndex( n1 );
10457 int i2 = f->GetNodeIndex( n2 );
10458 int iEnd = nbN, iBeg = -1, iDelta = 1;
10459 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10461 std::swap( iEnd, iBeg ); iDelta = -1;
10466 if ( i == iEnd ) i = iBeg + iDelta;
10467 if ( i == i1 ) break;
10468 nodes.push_back ( f->GetNode( i ) );
10474 // check similarity of elements of the sides
10475 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10476 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10477 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10478 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10481 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10485 // set nodes to merge
10486 // -------------------
10488 if ( face[0] && face[1] ) {
10489 if ( nbNodes[0] != nbNodes[1] ) {
10490 MESSAGE("Diff nb of face nodes");
10491 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10493 #ifdef DEBUG_MATCHING_NODES
10494 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10495 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10496 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10498 int nbN = nbNodes[0];
10500 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10501 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10502 for ( int i = 0 ; i < nbN - 2; ++i ) {
10503 #ifdef DEBUG_MATCHING_NODES
10504 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10506 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10510 // add other links of the face 1 to linkList
10511 // -----------------------------------------
10513 const SMDS_MeshElement* f0 = face[0];
10514 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10515 for ( int i = 0; i < nbN; i++ )
10517 const SMDS_MeshNode* n2 = f0->GetNode( i );
10518 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10519 linkSet.insert( SMESH_TLink( n1, n2 ));
10520 if ( !iter_isnew.second ) { // already in a set: no need to process
10521 linkSet.erase( iter_isnew.first );
10523 else // new in set == encountered for the first time: add
10525 #ifdef DEBUG_MATCHING_NODES
10526 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10527 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10529 linkList[0].push_back ( NLink( n1, n2 ));
10530 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10535 } // loop on link lists
10540 //================================================================================
10542 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10543 \param theElems - the list of elements (edges or faces) to be replicated
10544 The nodes for duplication could be found from these elements
10545 \param theNodesNot - list of nodes to NOT replicate
10546 \param theAffectedElems - the list of elements (cells and edges) to which the
10547 replicated nodes should be associated to.
10548 \return TRUE if operation has been completed successfully, FALSE otherwise
10550 //================================================================================
10552 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10553 const TIDSortedElemSet& theNodesNot,
10554 const TIDSortedElemSet& theAffectedElems )
10556 myLastCreatedElems.Clear();
10557 myLastCreatedNodes.Clear();
10559 if ( theElems.size() == 0 )
10562 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10567 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10568 // duplicate elements and nodes
10569 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10570 // replce nodes by duplications
10571 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10575 //================================================================================
10577 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10578 \param theMeshDS - mesh instance
10579 \param theElems - the elements replicated or modified (nodes should be changed)
10580 \param theNodesNot - nodes to NOT replicate
10581 \param theNodeNodeMap - relation of old node to new created node
10582 \param theIsDoubleElem - flag os to replicate element or modify
10583 \return TRUE if operation has been completed successfully, FALSE otherwise
10585 //================================================================================
10587 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10588 const TIDSortedElemSet& theElems,
10589 const TIDSortedElemSet& theNodesNot,
10590 std::map< const SMDS_MeshNode*,
10591 const SMDS_MeshNode* >& theNodeNodeMap,
10592 const bool theIsDoubleElem )
10594 MESSAGE("doubleNodes");
10595 // iterate on through element and duplicate them (by nodes duplication)
10597 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10598 for ( ; elemItr != theElems.end(); ++elemItr )
10600 const SMDS_MeshElement* anElem = *elemItr;
10604 bool isDuplicate = false;
10605 // duplicate nodes to duplicate element
10606 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10607 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10609 while ( anIter->more() )
10612 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10613 SMDS_MeshNode* aNewNode = aCurrNode;
10614 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10615 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10616 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10619 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10620 theNodeNodeMap[ aCurrNode ] = aNewNode;
10621 myLastCreatedNodes.Append( aNewNode );
10623 isDuplicate |= (aCurrNode != aNewNode);
10624 newNodes[ ind++ ] = aNewNode;
10626 if ( !isDuplicate )
10629 if ( theIsDoubleElem )
10630 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10633 MESSAGE("ChangeElementNodes");
10634 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10641 //================================================================================
10643 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10644 \param theNodes - identifiers of nodes to be doubled
10645 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10646 nodes. If list of element identifiers is empty then nodes are doubled but
10647 they not assigned to elements
10648 \return TRUE if operation has been completed successfully, FALSE otherwise
10650 //================================================================================
10652 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10653 const std::list< int >& theListOfModifiedElems )
10655 MESSAGE("DoubleNodes");
10656 myLastCreatedElems.Clear();
10657 myLastCreatedNodes.Clear();
10659 if ( theListOfNodes.size() == 0 )
10662 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10666 // iterate through nodes and duplicate them
10668 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10670 std::list< int >::const_iterator aNodeIter;
10671 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10673 int aCurr = *aNodeIter;
10674 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10680 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10683 anOldNodeToNewNode[ aNode ] = aNewNode;
10684 myLastCreatedNodes.Append( aNewNode );
10688 // Create map of new nodes for modified elements
10690 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10692 std::list< int >::const_iterator anElemIter;
10693 for ( anElemIter = theListOfModifiedElems.begin();
10694 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10696 int aCurr = *anElemIter;
10697 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10701 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10703 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10705 while ( anIter->more() )
10707 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10708 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10710 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10711 aNodeArr[ ind++ ] = aNewNode;
10714 aNodeArr[ ind++ ] = aCurrNode;
10716 anElemToNodes[ anElem ] = aNodeArr;
10719 // Change nodes of elements
10721 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10722 anElemToNodesIter = anElemToNodes.begin();
10723 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10725 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10726 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10729 MESSAGE("ChangeElementNodes");
10730 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10739 //================================================================================
10741 \brief Check if element located inside shape
10742 \return TRUE if IN or ON shape, FALSE otherwise
10744 //================================================================================
10746 template<class Classifier>
10747 bool isInside(const SMDS_MeshElement* theElem,
10748 Classifier& theClassifier,
10749 const double theTol)
10751 gp_XYZ centerXYZ (0, 0, 0);
10752 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10753 while (aNodeItr->more())
10754 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10756 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10757 theClassifier.Perform(aPnt, theTol);
10758 TopAbs_State aState = theClassifier.State();
10759 return (aState == TopAbs_IN || aState == TopAbs_ON );
10762 //================================================================================
10764 * \brief Classifier of the 3D point on the TopoDS_Face
10765 * with interaface suitable for isInside()
10767 //================================================================================
10769 struct _FaceClassifier
10771 Extrema_ExtPS _extremum;
10772 BRepAdaptor_Surface _surface;
10773 TopAbs_State _state;
10775 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10777 _extremum.Initialize( _surface,
10778 _surface.FirstUParameter(), _surface.LastUParameter(),
10779 _surface.FirstVParameter(), _surface.LastVParameter(),
10780 _surface.Tolerance(), _surface.Tolerance() );
10782 void Perform(const gp_Pnt& aPnt, double theTol)
10784 _state = TopAbs_OUT;
10785 _extremum.Perform(aPnt);
10786 if ( _extremum.IsDone() )
10787 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10788 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10790 TopAbs_State State() const
10797 //================================================================================
10799 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10800 \param theElems - group of of elements (edges or faces) to be replicated
10801 \param theNodesNot - group of nodes not to replicate
10802 \param theShape - shape to detect affected elements (element which geometric center
10803 located on or inside shape).
10804 The replicated nodes should be associated to affected elements.
10805 \return TRUE if operation has been completed successfully, FALSE otherwise
10807 //================================================================================
10809 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10810 const TIDSortedElemSet& theNodesNot,
10811 const TopoDS_Shape& theShape )
10813 if ( theShape.IsNull() )
10816 const double aTol = Precision::Confusion();
10817 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10818 auto_ptr<_FaceClassifier> aFaceClassifier;
10819 if ( theShape.ShapeType() == TopAbs_SOLID )
10821 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10822 bsc3d->PerformInfinitePoint(aTol);
10824 else if (theShape.ShapeType() == TopAbs_FACE )
10826 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10829 // iterates on indicated elements and get elements by back references from their nodes
10830 TIDSortedElemSet anAffected;
10831 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10832 for ( ; elemItr != theElems.end(); ++elemItr )
10834 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10838 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10839 while ( nodeItr->more() )
10841 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10842 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10844 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10845 while ( backElemItr->more() )
10847 const SMDS_MeshElement* curElem = backElemItr->next();
10848 if ( curElem && theElems.find(curElem) == theElems.end() &&
10850 isInside( curElem, *bsc3d, aTol ) :
10851 isInside( curElem, *aFaceClassifier, aTol )))
10852 anAffected.insert( curElem );
10856 return DoubleNodes( theElems, theNodesNot, anAffected );
10860 * \brief compute an oriented angle between two planes defined by four points.
10861 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10862 * @param p0 base of the rotation axe
10863 * @param p1 extremity of the rotation axe
10864 * @param g1 belongs to the first plane
10865 * @param g2 belongs to the second plane
10867 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10869 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10870 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10871 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10872 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10873 gp_Vec vref(p0, p1);
10876 gp_Vec n1 = vref.Crossed(v1);
10877 gp_Vec n2 = vref.Crossed(v2);
10878 return n2.AngleWithRef(n1, vref);
10882 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10883 * The list of groups must describe a partition of the mesh volumes.
10884 * The nodes of the internal faces at the boundaries of the groups are doubled.
10885 * In option, the internal faces are replaced by flat elements.
10886 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10887 * The flat elements are stored in groups of volumes.
10888 * @param theElems - list of groups of volumes, where a group of volume is a set of
10889 * SMDS_MeshElements sorted by Id.
10890 * @param createJointElems - if TRUE, create the elements
10891 * @return TRUE if operation has been completed successfully, FALSE otherwise
10893 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10894 bool createJointElems)
10896 MESSAGE("----------------------------------------------");
10897 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10898 MESSAGE("----------------------------------------------");
10900 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10901 meshDS->BuildDownWardConnectivity(true);
10903 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10905 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10906 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10907 // build the list of nodes shared by 2 or more domains, with their domain indexes
10909 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10910 std::map<int,int>celldom; // cell vtkId --> domain
10911 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10912 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10913 faceDomains.clear();
10915 cellDomains.clear();
10916 nodeDomains.clear();
10917 std::map<int,int> emptyMap;
10918 std::set<int> emptySet;
10921 for (int idom = 0; idom < theElems.size(); idom++)
10924 // --- build a map (face to duplicate --> volume to modify)
10925 // with all the faces shared by 2 domains (group of elements)
10926 // and corresponding volume of this domain, for each shared face.
10927 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10929 const TIDSortedElemSet& domain = theElems[idom];
10930 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10931 for (; elemItr != domain.end(); ++elemItr)
10933 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10936 int vtkId = anElem->getVtkId();
10937 int neighborsVtkIds[NBMAXNEIGHBORS];
10938 int downIds[NBMAXNEIGHBORS];
10939 unsigned char downTypes[NBMAXNEIGHBORS];
10940 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10941 for (int n = 0; n < nbNeighbors; n++)
10943 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10944 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10945 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10947 DownIdType face(downIds[n], downTypes[n]);
10948 if (!faceDomains.count(face))
10949 faceDomains[face] = emptyMap; // create an empty entry for face
10950 if (!faceDomains[face].count(idom))
10952 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10953 celldom[vtkId] = idom;
10960 //MESSAGE("Number of shared faces " << faceDomains.size());
10961 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10963 // --- explore the shared faces domain by domain,
10964 // explore the nodes of the face and see if they belong to a cell in the domain,
10965 // which has only a node or an edge on the border (not a shared face)
10967 for (int idomain = 0; idomain < theElems.size(); idomain++)
10969 const TIDSortedElemSet& domain = theElems[idomain];
10970 itface = faceDomains.begin();
10971 for (; itface != faceDomains.end(); ++itface)
10973 std::map<int, int> domvol = itface->second;
10974 if (!domvol.count(idomain))
10976 DownIdType face = itface->first;
10977 //MESSAGE(" --- face " << face.cellId);
10978 std::set<int> oldNodes;
10980 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10981 std::set<int>::iterator itn = oldNodes.begin();
10982 for (; itn != oldNodes.end(); ++itn)
10985 //MESSAGE(" node " << oldId);
10986 std::set<int> cells;
10988 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10989 for (int i=0; i<l.ncells; i++)
10991 int vtkId = l.cells[i];
10992 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10993 if (!domain.count(anElem))
10995 int vtkType = grid->GetCellType(vtkId);
10996 int downId = grid->CellIdToDownId(vtkId);
10999 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11000 continue; // not OK at this stage of the algorithm:
11001 //no cells created after BuildDownWardConnectivity
11003 DownIdType aCell(downId, vtkType);
11004 if (celldom.count(vtkId))
11006 cellDomains[aCell][idomain] = vtkId;
11007 celldom[vtkId] = idomain;
11013 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11014 // for each shared face, get the nodes
11015 // for each node, for each domain of the face, create a clone of the node
11017 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11018 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11019 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11021 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11022 std::map<int, std::vector<int> > mutipleNodes; // nodes muti domains with domain order
11024 for (int idomain = 0; idomain < theElems.size(); idomain++)
11026 itface = faceDomains.begin();
11027 for (; itface != faceDomains.end(); ++itface)
11029 std::map<int, int> domvol = itface->second;
11030 if (!domvol.count(idomain))
11032 DownIdType face = itface->first;
11033 //MESSAGE(" --- face " << face.cellId);
11034 std::set<int> oldNodes;
11036 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11037 bool isMultipleDetected = false;
11038 std::set<int>::iterator itn = oldNodes.begin();
11039 for (; itn != oldNodes.end(); ++itn)
11042 //MESSAGE(" node " << oldId);
11043 if (!nodeDomains.count(oldId))
11044 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11045 if (nodeDomains[oldId].empty())
11046 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11047 std::map<int, int>::iterator itdom = domvol.begin();
11048 for (; itdom != domvol.end(); ++itdom)
11050 int idom = itdom->first;
11051 //MESSAGE(" domain " << idom);
11052 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11054 if (nodeDomains[oldId].size() >= 2) // a multiple node
11056 vector<int> orderedDoms;
11057 //MESSAGE("multiple node " << oldId);
11058 isMultipleDetected =true;
11059 if (mutipleNodes.count(oldId))
11060 orderedDoms = mutipleNodes[oldId];
11063 map<int,int>::iterator it = nodeDomains[oldId].begin();
11064 for (; it != nodeDomains[oldId].end(); ++it)
11065 orderedDoms.push_back(it->first);
11067 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11068 //stringstream txt;
11069 //for (int i=0; i<orderedDoms.size(); i++)
11070 // txt << orderedDoms[i] << " ";
11071 //MESSAGE("orderedDoms " << txt.str());
11072 mutipleNodes[oldId] = orderedDoms;
11074 double *coords = grid->GetPoint(oldId);
11075 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11076 int newId = newNode->getVtkId();
11077 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11078 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
11080 if (nodeDomains[oldId].size() >= 3)
11082 //MESSAGE("confirm multiple node " << oldId);
11083 isMultipleDetected =true;
11087 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
11089 //MESSAGE("multiple Nodes detected on a shared face");
11090 int downId = itface->first.cellId;
11091 unsigned char cellType = itface->first.cellType;
11092 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11093 const int *downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11094 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11095 for (int ie =0; ie < nbEdges; ie++)
11098 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11099 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11101 vector<int> vn0 = mutipleNodes[nodes[0]];
11102 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11103 sort( vn0.begin(), vn0.end() );
11104 sort( vn1.begin(), vn1.end() );
11107 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11108 double *coords = grid->GetPoint(nodes[0]);
11109 gp_Pnt p0(coords[0], coords[1], coords[2]);
11110 coords = grid->GetPoint(nodes[nbNodes - 1]);
11111 gp_Pnt p1(coords[0], coords[1], coords[2]);
11113 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11114 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11115 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11116 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11117 for (int id=0; id < vn0.size(); id++)
11119 int idom = vn0[id];
11120 for (int ivol=0; ivol<nbvol; ivol++)
11122 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11123 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11124 if (theElems[idom].count(elem))
11126 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11127 domvol[idom] = svol;
11128 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11130 vtkIdType npts = 0;
11131 vtkIdType* pts = 0;
11132 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11133 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11136 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11137 angleDom[idom] = 0;
11141 gp_Pnt g(values[0], values[1], values[2]);
11142 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11143 //MESSAGE(" angle=" << angleDom[idom]);
11149 map<double, int> sortedDom; // sort domains by angle
11150 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11151 sortedDom[ia->second] = ia->first;
11152 vector<int> vnodes;
11154 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11156 vdom.push_back(ib->second);
11157 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11159 for (int ino = 0; ino < nbNodes; ino++)
11160 vnodes.push_back(nodes[ino]);
11161 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11169 // --- iterate on shared faces (volumes to modify, face to extrude)
11170 // get node id's of the face (id SMDS = id VTK)
11171 // create flat element with old and new nodes if requested
11173 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11174 // (domain1 X domain2) = domain1 + MAXINT*domain2
11176 std::map<int, std::map<long,int> > nodeQuadDomains;
11177 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11179 if (createJointElems)
11181 itface = faceDomains.begin();
11182 for (; itface != faceDomains.end(); ++itface)
11184 DownIdType face = itface->first;
11185 std::set<int> oldNodes;
11186 std::set<int>::iterator itn;
11188 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11190 std::map<int, int> domvol = itface->second;
11191 std::map<int, int>::iterator itdom = domvol.begin();
11192 int dom1 = itdom->first;
11193 int vtkVolId = itdom->second;
11195 int dom2 = itdom->first;
11196 SMDS_MeshVolume *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11198 stringstream grpname;
11201 grpname << dom1 << "_" << dom2;
11203 grpname << dom2 << "_" << dom1;
11205 string namegrp = grpname.str();
11206 if (!mapOfJunctionGroups.count(namegrp))
11207 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11208 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11210 sgrp->Add(vol->GetID());
11214 // --- create volumes on multiple domain intersection if requested
11215 // iterate on edgesMultiDomains
11217 if (createJointElems)
11219 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11220 for (; ite != edgesMultiDomains.end(); ++ite)
11222 vector<int> nodes = ite->first;
11223 vector<int> orderDom = ite->second;
11224 vector<vtkIdType> orderedNodes;
11225 if (nodes.size() == 2)
11227 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11228 for (int ino=0; ino < nodes.size(); ino++)
11229 if (orderDom.size() == 3)
11230 for (int idom = 0; idom <orderDom.size(); idom++)
11231 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11233 for (int idom = orderDom.size()-1; idom >=0; idom--)
11234 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11235 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11237 stringstream grpname;
11239 grpname << 0 << "_" << 0;
11241 string namegrp = grpname.str();
11242 if (!mapOfJunctionGroups.count(namegrp))
11243 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11244 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11246 sgrp->Add(vol->GetID());
11250 //MESSAGE("Quadratic multiple joints not implemented");
11251 // TODO quadratic nodes
11256 // --- list the explicit faces and edges of the mesh that need to be modified,
11257 // i.e. faces and edges built with one or more duplicated nodes.
11258 // associate these faces or edges to their corresponding domain.
11259 // only the first domain found is kept when a face or edge is shared
11261 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11262 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11263 faceOrEdgeDom.clear();
11266 for (int idomain = 0; idomain < theElems.size(); idomain++)
11268 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11269 for (; itnod != nodeDomains.end(); ++itnod)
11271 int oldId = itnod->first;
11272 //MESSAGE(" node " << oldId);
11273 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11274 for (int i = 0; i < l.ncells; i++)
11276 int vtkId = l.cells[i];
11277 int vtkType = grid->GetCellType(vtkId);
11278 int downId = grid->CellIdToDownId(vtkId);
11280 continue; // new cells: not to be modified
11281 DownIdType aCell(downId, vtkType);
11282 int volParents[1000];
11283 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11284 for (int j = 0; j < nbvol; j++)
11285 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11286 if (!feDom.count(vtkId))
11288 feDom[vtkId] = idomain;
11289 faceOrEdgeDom[aCell] = emptyMap;
11290 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11291 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11292 // << " type " << vtkType << " downId " << downId);
11298 // --- iterate on shared faces (volumes to modify, face to extrude)
11299 // get node id's of the face
11300 // replace old nodes by new nodes in volumes, and update inverse connectivity
11302 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11303 for (int m=0; m<3; m++)
11305 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11306 itface = (*amap).begin();
11307 for (; itface != (*amap).end(); ++itface)
11309 DownIdType face = itface->first;
11310 std::set<int> oldNodes;
11311 std::set<int>::iterator itn;
11313 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11314 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11315 std::map<int, int> localClonedNodeIds;
11317 std::map<int, int> domvol = itface->second;
11318 std::map<int, int>::iterator itdom = domvol.begin();
11319 for (; itdom != domvol.end(); ++itdom)
11321 int idom = itdom->first;
11322 int vtkVolId = itdom->second;
11323 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11324 localClonedNodeIds.clear();
11325 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11328 if (nodeDomains[oldId].count(idom))
11330 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11331 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11334 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11339 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11340 grid->BuildLinks();
11348 * \brief Double nodes on some external faces and create flat elements.
11349 * Flat elements are mainly used by some types of mechanic calculations.
11351 * Each group of the list must be constituted of faces.
11352 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11353 * @param theElems - list of groups of faces, where a group of faces is a set of
11354 * SMDS_MeshElements sorted by Id.
11355 * @return TRUE if operation has been completed successfully, FALSE otherwise
11357 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11359 MESSAGE("-------------------------------------------------");
11360 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11361 MESSAGE("-------------------------------------------------");
11363 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11365 // --- For each group of faces
11366 // duplicate the nodes, create a flat element based on the face
11367 // replace the nodes of the faces by their clones
11369 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11370 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11371 clonedNodes.clear();
11372 intermediateNodes.clear();
11373 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11374 mapOfJunctionGroups.clear();
11376 for (int idom = 0; idom < theElems.size(); idom++)
11378 const TIDSortedElemSet& domain = theElems[idom];
11379 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11380 for (; elemItr != domain.end(); ++elemItr)
11382 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11383 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11386 // MESSAGE("aFace=" << aFace->GetID());
11387 bool isQuad = aFace->IsQuadratic();
11388 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11390 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11392 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11393 while (nodeIt->more())
11395 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11396 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11398 ln2.push_back(node);
11400 ln0.push_back(node);
11402 const SMDS_MeshNode* clone = 0;
11403 if (!clonedNodes.count(node))
11405 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11406 clonedNodes[node] = clone;
11409 clone = clonedNodes[node];
11412 ln3.push_back(clone);
11414 ln1.push_back(clone);
11416 const SMDS_MeshNode* inter = 0;
11417 if (isQuad && (!isMedium))
11419 if (!intermediateNodes.count(node))
11421 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11422 intermediateNodes[node] = inter;
11425 inter = intermediateNodes[node];
11426 ln4.push_back(inter);
11430 // --- extrude the face
11432 vector<const SMDS_MeshNode*> ln;
11433 SMDS_MeshVolume* vol = 0;
11434 vtkIdType aType = aFace->GetVtkType();
11438 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11439 // MESSAGE("vol prism " << vol->GetID());
11440 ln.push_back(ln1[0]);
11441 ln.push_back(ln1[1]);
11442 ln.push_back(ln1[2]);
11445 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11446 // MESSAGE("vol hexa " << vol->GetID());
11447 ln.push_back(ln1[0]);
11448 ln.push_back(ln1[1]);
11449 ln.push_back(ln1[2]);
11450 ln.push_back(ln1[3]);
11452 case VTK_QUADRATIC_TRIANGLE:
11453 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11454 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11455 // MESSAGE("vol quad prism " << vol->GetID());
11456 ln.push_back(ln1[0]);
11457 ln.push_back(ln1[1]);
11458 ln.push_back(ln1[2]);
11459 ln.push_back(ln3[0]);
11460 ln.push_back(ln3[1]);
11461 ln.push_back(ln3[2]);
11463 case VTK_QUADRATIC_QUAD:
11464 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11465 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11466 // ln4[0], ln4[1], ln4[2], ln4[3]);
11467 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11468 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11469 ln4[0], ln4[1], ln4[2], ln4[3]);
11470 // MESSAGE("vol quad hexa " << vol->GetID());
11471 ln.push_back(ln1[0]);
11472 ln.push_back(ln1[1]);
11473 ln.push_back(ln1[2]);
11474 ln.push_back(ln1[3]);
11475 ln.push_back(ln3[0]);
11476 ln.push_back(ln3[1]);
11477 ln.push_back(ln3[2]);
11478 ln.push_back(ln3[3]);
11488 stringstream grpname;
11492 string namegrp = grpname.str();
11493 if (!mapOfJunctionGroups.count(namegrp))
11494 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11495 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11497 sgrp->Add(vol->GetID());
11500 // --- modify the face
11502 aFace->ChangeNodes(&ln[0], ln.size());
11508 //================================================================================
11510 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11511 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11512 * \return TRUE if operation has been completed successfully, FALSE otherwise
11514 //================================================================================
11516 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11518 // iterates on volume elements and detect all free faces on them
11519 SMESHDS_Mesh* aMesh = GetMeshDS();
11522 //bool res = false;
11523 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11524 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11527 const SMDS_MeshVolume* volume = vIt->next();
11528 SMDS_VolumeTool vTool( volume );
11529 vTool.SetExternalNormal();
11530 const bool isPoly = volume->IsPoly();
11531 const bool isQuad = volume->IsQuadratic();
11532 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11534 if (!vTool.IsFreeFace(iface))
11537 vector<const SMDS_MeshNode *> nodes;
11538 int nbFaceNodes = vTool.NbFaceNodes(iface);
11539 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11541 for ( ; inode < nbFaceNodes; inode += isQuad ? 2 : 1)
11542 nodes.push_back(faceNodes[inode]);
11544 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11545 nodes.push_back(faceNodes[inode]);
11547 // add new face based on volume nodes
11548 if (aMesh->FindFace( nodes ) ) {
11550 continue; // face already exsist
11552 AddElement(nodes, SMDSAbs_Face, isPoly && iface == 1);
11556 return ( nbFree==(nbExisted+nbCreated) );
11561 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11563 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11565 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11568 //================================================================================
11570 * \brief Creates missing boundary elements
11571 * \param elements - elements whose boundary is to be checked
11572 * \param dimension - defines type of boundary elements to create
11573 * \param group - a group to store created boundary elements in
11574 * \param targetMesh - a mesh to store created boundary elements in
11575 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11576 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11577 * boundary elements will be copied into the targetMesh
11578 * \param toAddExistingBondary - if true, not only new but also pre-existing
11579 * boundary elements will be added into the new group
11580 * \param aroundElements - if true, elements will be created on boundary of given
11581 * elements else, on boundary of the whole mesh.
11582 * \return nb of added boundary elements
11584 //================================================================================
11586 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11587 Bnd_Dimension dimension,
11588 SMESH_Group* group/*=0*/,
11589 SMESH_Mesh* targetMesh/*=0*/,
11590 bool toCopyElements/*=false*/,
11591 bool toCopyExistingBoundary/*=false*/,
11592 bool toAddExistingBondary/*= false*/,
11593 bool aroundElements/*= false*/)
11595 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11596 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11597 // hope that all elements are of the same type, do not check them all
11598 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11599 throw SALOME_Exception(LOCALIZED("wrong element type"));
11602 toCopyElements = toCopyExistingBoundary = false;
11604 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11605 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11606 int nbAddedBnd = 0;
11608 // editor adding present bnd elements and optionally holding elements to add to the group
11609 SMESH_MeshEditor* presentEditor;
11610 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11611 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11613 SMDS_VolumeTool vTool;
11614 TIDSortedElemSet avoidSet;
11615 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11618 typedef vector<const SMDS_MeshNode*> TConnectivity;
11620 SMDS_ElemIteratorPtr eIt;
11621 if (elements.empty())
11622 eIt = aMesh->elementsIterator(elemType);
11624 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11626 while (eIt->more())
11628 const SMDS_MeshElement* elem = eIt->next();
11629 const int iQuad = elem->IsQuadratic();
11631 // ------------------------------------------------------------------------------------
11632 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11633 // ------------------------------------------------------------------------------------
11634 vector<const SMDS_MeshElement*> presentBndElems;
11635 vector<TConnectivity> missingBndElems;
11636 TConnectivity nodes;
11637 if ( vTool.Set(elem) ) // elem is a volume ------------------------------------------
11639 vTool.SetExternalNormal();
11640 const SMDS_MeshElement* otherVol = 0;
11641 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11643 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11644 ( !aroundElements || elements.count( otherVol )))
11646 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11647 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11648 if ( missType == SMDSAbs_Edge ) // boundary edges
11650 nodes.resize( 2+iQuad );
11651 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11653 for ( int j = 0; j < nodes.size(); ++j )
11655 if ( const SMDS_MeshElement* edge =
11656 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/0))
11657 presentBndElems.push_back( edge );
11659 missingBndElems.push_back( nodes );
11662 else // boundary face
11665 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11666 nodes.push_back( nn[inode] );
11668 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11669 nodes.push_back( nn[inode] );
11671 if (const SMDS_MeshFace * f = aMesh->FindFace( nodes ) )
11672 presentBndElems.push_back( f );
11674 missingBndElems.push_back( nodes );
11676 if ( targetMesh != myMesh )
11678 // add 1D elements on face boundary to be added to a new mesh
11679 const SMDS_MeshElement* edge;
11680 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11683 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11685 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11686 if ( edge && avoidSet.insert( edge ).second )
11687 presentBndElems.push_back( edge );
11693 else // elem is a face ------------------------------------------
11695 avoidSet.clear(), avoidSet.insert( elem );
11696 int nbNodes = elem->NbCornerNodes();
11697 nodes.resize( 2 /*+ iQuad*/);
11698 for ( int i = 0; i < nbNodes; i++ )
11700 nodes[0] = elem->GetNode(i);
11701 nodes[1] = elem->GetNode((i+1)%nbNodes);
11702 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11703 continue; // not free link
11706 //nodes[2] = elem->GetNode( i + nbNodes );
11707 if ( const SMDS_MeshElement* edge =
11708 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11709 presentBndElems.push_back( edge );
11711 missingBndElems.push_back( nodes );
11715 // ---------------------------------
11716 // 2. Add missing boundary elements
11717 // ---------------------------------
11718 if ( targetMesh != myMesh )
11719 // instead of making a map of nodes in this mesh and targetMesh,
11720 // we create nodes with same IDs.
11721 for ( int i = 0; i < missingBndElems.size(); ++i )
11723 TConnectivity& srcNodes = missingBndElems[i];
11724 TConnectivity nodes( srcNodes.size() );
11725 for ( inode = 0; inode < nodes.size(); ++inode )
11726 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11727 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11729 /*noMedium=*/true))
11731 tgtEditor.AddElement(nodes, missType, elem->IsPoly() && nodes.size()/(iQuad+1)>4);
11735 for ( int i = 0; i < missingBndElems.size(); ++i )
11737 TConnectivity& nodes = missingBndElems[i];
11738 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11740 /*noMedium=*/true))
11742 tgtEditor.AddElement(nodes, missType, elem->IsPoly() && nodes.size()/(iQuad+1)>4);
11746 // ----------------------------------
11747 // 3. Copy present boundary elements
11748 // ----------------------------------
11749 if ( toCopyExistingBoundary )
11750 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11752 const SMDS_MeshElement* e = presentBndElems[i];
11753 TConnectivity nodes( e->NbNodes() );
11754 for ( inode = 0; inode < nodes.size(); ++inode )
11755 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11756 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11758 else // store present elements to add them to a group
11759 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11761 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11764 } // loop on given elements
11766 // ---------------------------------------------
11767 // 4. Fill group with boundary elements
11768 // ---------------------------------------------
11771 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11772 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11773 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11775 tgtEditor.myLastCreatedElems.Clear();
11776 tgtEditor2.myLastCreatedElems.Clear();
11778 // -----------------------
11779 // 5. Copy given elements
11780 // -----------------------
11781 if ( toCopyElements && targetMesh != myMesh )
11783 if (elements.empty())
11784 eIt = aMesh->elementsIterator(elemType);
11786 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11787 while (eIt->more())
11789 const SMDS_MeshElement* elem = eIt->next();
11790 TConnectivity nodes( elem->NbNodes() );
11791 for ( inode = 0; inode < nodes.size(); ++inode )
11792 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11793 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11795 tgtEditor.myLastCreatedElems.Clear();