1 // Copyright (C) 2007-2010 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)
28 #include "SMESH_MeshEditor.hxx"
30 #include "SMDS_FaceOfNodes.hxx"
31 #include "SMDS_VolumeTool.hxx"
32 #include "SMDS_EdgePosition.hxx"
33 #include "SMDS_PolyhedralVolumeOfNodes.hxx"
34 #include "SMDS_FacePosition.hxx"
35 #include "SMDS_SpacePosition.hxx"
36 #include "SMDS_QuadraticFaceOfNodes.hxx"
37 #include "SMDS_MeshGroup.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
51 #include <BRepAdaptor_Surface.hxx>
52 #include <BRepClass3d_SolidClassifier.hxx>
53 #include <BRep_Tool.hxx>
55 #include <Extrema_GenExtPS.hxx>
56 #include <Extrema_POnCurv.hxx>
57 #include <Extrema_POnSurf.hxx>
58 #include <GC_MakeSegment.hxx>
59 #include <Geom2d_Curve.hxx>
60 #include <GeomAPI_ExtremaCurveCurve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Line.hxx>
64 #include <Geom_Surface.hxx>
65 #include <IntAna_IntConicQuad.hxx>
66 #include <IntAna_Quadric.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
82 #include <gp_Trsf.hxx>
94 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
97 using namespace SMESH::Controls;
99 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
100 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
102 //=======================================================================
103 //function : SMESH_MeshEditor
105 //=======================================================================
107 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
108 :myMesh( theMesh ) // theMesh may be NULL
112 //=======================================================================
116 //=======================================================================
119 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
120 const SMDSAbs_ElementType type,
124 SMDS_MeshElement* e = 0;
125 int nbnode = node.size();
126 SMESHDS_Mesh* mesh = GetMeshDS();
128 case SMDSAbs_0DElement:
130 if ( ID ) e = mesh->Add0DElementWithID(node[0], ID);
131 else e = mesh->Add0DElement (node[0] );
135 if ( ID ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
136 else e = mesh->AddEdge (node[0], node[1] );
137 else if ( nbnode == 3 )
138 if ( ID ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
139 else e = mesh->AddEdge (node[0], node[1], node[2] );
144 if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
145 else e = mesh->AddFace (node[0], node[1], node[2] );
146 else if (nbnode == 4)
147 if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
148 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
149 else if (nbnode == 6)
150 if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
151 node[4], node[5], ID);
152 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
154 else if (nbnode == 8)
155 if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
156 node[4], node[5], node[6], node[7], ID);
157 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
158 node[4], node[5], node[6], node[7] );
160 if ( ID ) e = mesh->AddPolygonalFaceWithID(node, ID);
161 else e = mesh->AddPolygonalFace (node );
167 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
168 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
169 else if (nbnode == 5)
170 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
172 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
174 else if (nbnode == 6)
175 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
176 node[4], node[5], ID);
177 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
179 else if (nbnode == 8)
180 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
181 node[4], node[5], node[6], node[7], ID);
182 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
183 node[4], node[5], node[6], node[7] );
184 else if (nbnode == 10)
185 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
186 node[4], node[5], node[6], node[7],
187 node[8], node[9], ID);
188 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
189 node[4], node[5], node[6], node[7],
191 else if (nbnode == 13)
192 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
193 node[4], node[5], node[6], node[7],
194 node[8], node[9], node[10],node[11],
196 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
197 node[4], node[5], node[6], node[7],
198 node[8], node[9], node[10],node[11],
200 else if (nbnode == 15)
201 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
202 node[4], node[5], node[6], node[7],
203 node[8], node[9], node[10],node[11],
204 node[12],node[13],node[14],ID);
205 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
206 node[4], node[5], node[6], node[7],
207 node[8], node[9], node[10],node[11],
208 node[12],node[13],node[14] );
209 else if (nbnode == 20)
210 if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6], node[7],
212 node[8], node[9], node[10],node[11],
213 node[12],node[13],node[14],node[15],
214 node[16],node[17],node[18],node[19],ID);
215 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7],
217 node[8], node[9], node[10],node[11],
218 node[12],node[13],node[14],node[15],
219 node[16],node[17],node[18],node[19] );
225 //=======================================================================
229 //=======================================================================
231 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
232 const SMDSAbs_ElementType type,
236 vector<const SMDS_MeshNode*> nodes;
237 nodes.reserve( nodeIDs.size() );
238 vector<int>::const_iterator id = nodeIDs.begin();
239 while ( id != nodeIDs.end() ) {
240 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
241 nodes.push_back( node );
245 return AddElement( nodes, type, isPoly, ID );
248 //=======================================================================
250 //purpose : Remove a node or an element.
251 // Modify a compute state of sub-meshes which become empty
252 //=======================================================================
254 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
257 myLastCreatedElems.Clear();
258 myLastCreatedNodes.Clear();
260 SMESHDS_Mesh* aMesh = GetMeshDS();
261 set< SMESH_subMesh *> smmap;
264 list<int>::const_iterator it = theIDs.begin();
265 for ( ; it != theIDs.end(); it++ ) {
266 const SMDS_MeshElement * elem;
268 elem = aMesh->FindNode( *it );
270 elem = aMesh->FindElement( *it );
274 // Notify VERTEX sub-meshes about modification
276 const SMDS_MeshNode* node = cast2Node( elem );
277 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
278 if ( int aShapeID = node->GetPosition()->GetShapeId() )
279 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
282 // Find sub-meshes to notify about modification
283 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
284 // while ( nodeIt->more() ) {
285 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
286 // const SMDS_PositionPtr& aPosition = node->GetPosition();
287 // if ( aPosition.get() ) {
288 // if ( int aShapeID = aPosition->GetShapeId() ) {
289 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
290 // smmap.insert( sm );
297 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
299 aMesh->RemoveElement( elem );
303 // Notify sub-meshes about modification
304 if ( !smmap.empty() ) {
305 set< SMESH_subMesh *>::iterator smIt;
306 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
307 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
310 // // Check if the whole mesh becomes empty
311 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
312 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
317 //=======================================================================
318 //function : FindShape
319 //purpose : Return an index of the shape theElem is on
320 // or zero if a shape not found
321 //=======================================================================
323 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
325 myLastCreatedElems.Clear();
326 myLastCreatedNodes.Clear();
328 SMESHDS_Mesh * aMesh = GetMeshDS();
329 if ( aMesh->ShapeToMesh().IsNull() )
332 if ( theElem->GetType() == SMDSAbs_Node ) {
333 const SMDS_PositionPtr& aPosition =
334 static_cast<const SMDS_MeshNode*>( theElem )->GetPosition();
335 if ( aPosition.get() )
336 return aPosition->GetShapeId();
341 TopoDS_Shape aShape; // the shape a node is on
342 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
343 while ( nodeIt->more() ) {
344 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
345 const SMDS_PositionPtr& aPosition = node->GetPosition();
346 if ( aPosition.get() ) {
347 int aShapeID = aPosition->GetShapeId();
348 SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID );
350 if ( sm->Contains( theElem ))
352 if ( aShape.IsNull() )
353 aShape = aMesh->IndexToShape( aShapeID );
356 //MESSAGE ( "::FindShape() No SubShape for aShapeID " << aShapeID );
361 // None of nodes is on a proper shape,
362 // find the shape among ancestors of aShape on which a node is
363 if ( aShape.IsNull() ) {
364 //MESSAGE ("::FindShape() - NONE node is on shape")
367 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
368 for ( ; ancIt.More(); ancIt.Next() ) {
369 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
370 if ( sm && sm->Contains( theElem ))
371 return aMesh->ShapeToIndex( ancIt.Value() );
374 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
378 //=======================================================================
379 //function : IsMedium
381 //=======================================================================
383 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
384 const SMDSAbs_ElementType typeToCheck)
386 bool isMedium = false;
387 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
388 while (it->more() && !isMedium ) {
389 const SMDS_MeshElement* elem = it->next();
390 isMedium = elem->IsMediumNode(node);
395 //=======================================================================
396 //function : ShiftNodesQuadTria
398 // Shift nodes in the array corresponded to quadratic triangle
399 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
400 //=======================================================================
401 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
403 const SMDS_MeshNode* nd1 = aNodes[0];
404 aNodes[0] = aNodes[1];
405 aNodes[1] = aNodes[2];
407 const SMDS_MeshNode* nd2 = aNodes[3];
408 aNodes[3] = aNodes[4];
409 aNodes[4] = aNodes[5];
413 //=======================================================================
414 //function : GetNodesFromTwoTria
416 // Shift nodes in the array corresponded to quadratic triangle
417 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
418 //=======================================================================
419 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
420 const SMDS_MeshElement * theTria2,
421 const SMDS_MeshNode* N1[],
422 const SMDS_MeshNode* N2[])
424 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
427 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
430 if(it->more()) return false;
431 it = theTria2->nodesIterator();
434 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
437 if(it->more()) return false;
439 int sames[3] = {-1,-1,-1};
451 if(nbsames!=2) return false;
453 ShiftNodesQuadTria(N1);
455 ShiftNodesQuadTria(N1);
458 i = sames[0] + sames[1] + sames[2];
460 ShiftNodesQuadTria(N2);
462 // now we receive following N1 and N2 (using numeration as above image)
463 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
464 // i.e. first nodes from both arrays determ new diagonal
468 //=======================================================================
469 //function : InverseDiag
470 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
471 // but having other common link.
472 // Return False if args are improper
473 //=======================================================================
475 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
476 const SMDS_MeshElement * theTria2 )
478 myLastCreatedElems.Clear();
479 myLastCreatedNodes.Clear();
481 if (!theTria1 || !theTria2)
484 const SMDS_FaceOfNodes* F1 = dynamic_cast<const SMDS_FaceOfNodes*>( theTria1 );
485 const SMDS_FaceOfNodes* F2 = dynamic_cast<const SMDS_FaceOfNodes*>( theTria2 );
488 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
489 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
493 // put nodes in array and find out indices of the same ones
494 const SMDS_MeshNode* aNodes [6];
495 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
497 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
498 while ( it->more() ) {
499 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
501 if ( i > 2 ) // theTria2
502 // find same node of theTria1
503 for ( int j = 0; j < 3; j++ )
504 if ( aNodes[ i ] == aNodes[ j ]) {
513 return false; // theTria1 is not a triangle
514 it = theTria2->nodesIterator();
516 if ( i == 6 && it->more() )
517 return false; // theTria2 is not a triangle
520 // find indices of 1,2 and of A,B in theTria1
521 int iA = 0, iB = 0, i1 = 0, i2 = 0;
522 for ( i = 0; i < 6; i++ ) {
523 if ( sameInd [ i ] == 0 )
530 // nodes 1 and 2 should not be the same
531 if ( aNodes[ i1 ] == aNodes[ i2 ] )
535 aNodes[ iA ] = aNodes[ i2 ];
537 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
539 //MESSAGE( theTria1 << theTria2 );
541 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
542 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
544 //MESSAGE( theTria1 << theTria2 );
548 } // end if(F1 && F2)
550 // check case of quadratic faces
551 const SMDS_QuadraticFaceOfNodes* QF1 =
552 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (theTria1);
553 if(!QF1) return false;
554 const SMDS_QuadraticFaceOfNodes* QF2 =
555 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (theTria2);
556 if(!QF2) return false;
559 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
560 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
568 const SMDS_MeshNode* N1 [6];
569 const SMDS_MeshNode* N2 [6];
570 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
572 // now we receive following N1 and N2 (using numeration as above image)
573 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
574 // i.e. first nodes from both arrays determ new diagonal
576 const SMDS_MeshNode* N1new [6];
577 const SMDS_MeshNode* N2new [6];
590 // replaces nodes in faces
591 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
592 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
597 //=======================================================================
598 //function : findTriangles
599 //purpose : find triangles sharing theNode1-theNode2 link
600 //=======================================================================
602 static bool findTriangles(const SMDS_MeshNode * theNode1,
603 const SMDS_MeshNode * theNode2,
604 const SMDS_MeshElement*& theTria1,
605 const SMDS_MeshElement*& theTria2)
607 if ( !theNode1 || !theNode2 ) return false;
609 theTria1 = theTria2 = 0;
611 set< const SMDS_MeshElement* > emap;
612 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
614 const SMDS_MeshElement* elem = it->next();
615 if ( elem->NbNodes() == 3 )
618 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
620 const SMDS_MeshElement* elem = it->next();
621 if ( emap.find( elem ) != emap.end() )
623 // theTria1 must be element with minimum ID
624 if( theTria1->GetID() < elem->GetID() ) {
637 return ( theTria1 && theTria2 );
640 //=======================================================================
641 //function : InverseDiag
642 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
643 // with ones built on the same 4 nodes but having other common link.
644 // Return false if proper faces not found
645 //=======================================================================
647 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
648 const SMDS_MeshNode * theNode2)
650 myLastCreatedElems.Clear();
651 myLastCreatedNodes.Clear();
653 MESSAGE( "::InverseDiag()" );
655 const SMDS_MeshElement *tr1, *tr2;
656 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
659 const SMDS_FaceOfNodes* F1 = dynamic_cast<const SMDS_FaceOfNodes*>( tr1 );
660 //if (!F1) return false;
661 const SMDS_FaceOfNodes* F2 = dynamic_cast<const SMDS_FaceOfNodes*>( tr2 );
662 //if (!F2) return false;
665 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
666 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
670 // put nodes in array
671 // and find indices of 1,2 and of A in tr1 and of B in tr2
672 int i, iA1 = 0, i1 = 0;
673 const SMDS_MeshNode* aNodes1 [3];
674 SMDS_ElemIteratorPtr it;
675 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
676 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
677 if ( aNodes1[ i ] == theNode1 )
678 iA1 = i; // node A in tr1
679 else if ( aNodes1[ i ] != theNode2 )
683 const SMDS_MeshNode* aNodes2 [3];
684 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
685 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
686 if ( aNodes2[ i ] == theNode2 )
687 iB2 = i; // node B in tr2
688 else if ( aNodes2[ i ] != theNode1 )
692 // nodes 1 and 2 should not be the same
693 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
697 aNodes1[ iA1 ] = aNodes2[ i2 ];
699 aNodes2[ iB2 ] = aNodes1[ i1 ];
701 //MESSAGE( tr1 << tr2 );
703 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
704 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
706 //MESSAGE( tr1 << tr2 );
711 // check case of quadratic faces
712 const SMDS_QuadraticFaceOfNodes* QF1 =
713 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (tr1);
714 if(!QF1) return false;
715 const SMDS_QuadraticFaceOfNodes* QF2 =
716 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (tr2);
717 if(!QF2) return false;
718 return InverseDiag(tr1,tr2);
721 //=======================================================================
722 //function : getQuadrangleNodes
723 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
724 // fusion of triangles tr1 and tr2 having shared link on
725 // theNode1 and theNode2
726 //=======================================================================
728 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
729 const SMDS_MeshNode * theNode1,
730 const SMDS_MeshNode * theNode2,
731 const SMDS_MeshElement * tr1,
732 const SMDS_MeshElement * tr2 )
734 if( tr1->NbNodes() != tr2->NbNodes() )
736 // find the 4-th node to insert into tr1
737 const SMDS_MeshNode* n4 = 0;
738 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
740 while ( !n4 && i<3 ) {
741 const SMDS_MeshNode * n = cast2Node( it->next() );
743 bool isDiag = ( n == theNode1 || n == theNode2 );
747 // Make an array of nodes to be in a quadrangle
748 int iNode = 0, iFirstDiag = -1;
749 it = tr1->nodesIterator();
752 const SMDS_MeshNode * n = cast2Node( it->next() );
754 bool isDiag = ( n == theNode1 || n == theNode2 );
756 if ( iFirstDiag < 0 )
758 else if ( iNode - iFirstDiag == 1 )
759 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
761 else if ( n == n4 ) {
762 return false; // tr1 and tr2 should not have all the same nodes
764 theQuadNodes[ iNode++ ] = n;
766 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
767 theQuadNodes[ iNode ] = n4;
772 //=======================================================================
773 //function : DeleteDiag
774 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
775 // with a quadrangle built on the same 4 nodes.
776 // Return false if proper faces not found
777 //=======================================================================
779 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
780 const SMDS_MeshNode * theNode2)
782 myLastCreatedElems.Clear();
783 myLastCreatedNodes.Clear();
785 MESSAGE( "::DeleteDiag()" );
787 const SMDS_MeshElement *tr1, *tr2;
788 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
791 const SMDS_FaceOfNodes* F1 = dynamic_cast<const SMDS_FaceOfNodes*>( tr1 );
792 //if (!F1) return false;
793 const SMDS_FaceOfNodes* F2 = dynamic_cast<const SMDS_FaceOfNodes*>( tr2 );
794 //if (!F2) return false;
797 const SMDS_MeshNode* aNodes [ 4 ];
798 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
801 //MESSAGE( endl << tr1 << tr2 );
803 GetMeshDS()->ChangeElementNodes( tr1, aNodes, 4 );
804 myLastCreatedElems.Append(tr1);
805 GetMeshDS()->RemoveElement( tr2 );
807 //MESSAGE( endl << tr1 );
812 // check case of quadratic faces
813 const SMDS_QuadraticFaceOfNodes* QF1 =
814 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (tr1);
815 if(!QF1) return false;
816 const SMDS_QuadraticFaceOfNodes* QF2 =
817 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (tr2);
818 if(!QF2) return false;
821 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
822 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
830 const SMDS_MeshNode* N1 [6];
831 const SMDS_MeshNode* N2 [6];
832 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
834 // now we receive following N1 and N2 (using numeration as above image)
835 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
836 // i.e. first nodes from both arrays determ new diagonal
838 const SMDS_MeshNode* aNodes[8];
848 GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 );
849 myLastCreatedElems.Append(tr1);
850 GetMeshDS()->RemoveElement( tr2 );
852 // remove middle node (9)
853 GetMeshDS()->RemoveNode( N1[4] );
858 //=======================================================================
859 //function : Reorient
860 //purpose : Reverse theElement orientation
861 //=======================================================================
863 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
865 myLastCreatedElems.Clear();
866 myLastCreatedNodes.Clear();
870 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
871 if ( !it || !it->more() )
874 switch ( theElem->GetType() ) {
878 if(!theElem->IsQuadratic()) {
879 int i = theElem->NbNodes();
880 vector<const SMDS_MeshNode*> aNodes( i );
882 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
883 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
886 // quadratic elements
887 if(theElem->GetType()==SMDSAbs_Edge) {
888 vector<const SMDS_MeshNode*> aNodes(3);
889 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
890 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
891 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
892 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
895 int nbn = theElem->NbNodes();
896 vector<const SMDS_MeshNode*> aNodes(nbn);
897 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
899 for(; i<nbn/2; i++) {
900 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
902 for(i=0; i<nbn/2; i++) {
903 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
905 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
909 case SMDSAbs_Volume: {
910 if (theElem->IsPoly()) {
911 const SMDS_PolyhedralVolumeOfNodes* aPolyedre =
912 static_cast<const SMDS_PolyhedralVolumeOfNodes*>( theElem );
914 MESSAGE("Warning: bad volumic element");
918 int nbFaces = aPolyedre->NbFaces();
919 vector<const SMDS_MeshNode *> poly_nodes;
920 vector<int> quantities (nbFaces);
922 // reverse each face of the polyedre
923 for (int iface = 1; iface <= nbFaces; iface++) {
924 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
925 quantities[iface - 1] = nbFaceNodes;
927 for (inode = nbFaceNodes; inode >= 1; inode--) {
928 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
929 poly_nodes.push_back(curNode);
933 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
937 SMDS_VolumeTool vTool;
938 if ( !vTool.Set( theElem ))
941 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
950 //=======================================================================
951 //function : getBadRate
953 //=======================================================================
955 static double getBadRate (const SMDS_MeshElement* theElem,
956 SMESH::Controls::NumericalFunctorPtr& theCrit)
958 SMESH::Controls::TSequenceOfXYZ P;
959 if ( !theElem || !theCrit->GetPoints( theElem, P ))
961 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
962 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
965 //=======================================================================
966 //function : QuadToTri
967 //purpose : Cut quadrangles into triangles.
968 // theCrit is used to select a diagonal to cut
969 //=======================================================================
971 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
972 SMESH::Controls::NumericalFunctorPtr theCrit)
974 myLastCreatedElems.Clear();
975 myLastCreatedNodes.Clear();
977 MESSAGE( "::QuadToTri()" );
979 if ( !theCrit.get() )
982 SMESHDS_Mesh * aMesh = GetMeshDS();
984 Handle(Geom_Surface) surface;
985 SMESH_MesherHelper helper( *GetMesh() );
987 TIDSortedElemSet::iterator itElem;
988 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
989 const SMDS_MeshElement* elem = *itElem;
990 if ( !elem || elem->GetType() != SMDSAbs_Face )
992 if ( elem->NbNodes() != ( elem->IsQuadratic() ? 8 : 4 ))
995 // retrieve element nodes
996 const SMDS_MeshNode* aNodes [8];
997 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
999 while ( itN->more() )
1000 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1002 // compare two sets of possible triangles
1003 double aBadRate1, aBadRate2; // to what extent a set is bad
1004 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1005 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1006 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1008 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1009 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1010 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1012 int aShapeId = FindShape( elem );
1013 const SMDS_MeshElement* newElem = 0;
1015 if( !elem->IsQuadratic() ) {
1017 // split liner quadrangle
1019 if ( aBadRate1 <= aBadRate2 ) {
1020 // tr1 + tr2 is better
1021 aMesh->ChangeElementNodes( elem, aNodes, 3 );
1022 newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1025 // tr3 + tr4 is better
1026 aMesh->ChangeElementNodes( elem, &aNodes[1], 3 );
1027 newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1032 // split quadratic quadrangle
1034 // get surface elem is on
1035 if ( aShapeId != helper.GetSubShapeID() ) {
1039 shape = aMesh->IndexToShape( aShapeId );
1040 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1041 TopoDS_Face face = TopoDS::Face( shape );
1042 surface = BRep_Tool::Surface( face );
1043 if ( !surface.IsNull() )
1044 helper.SetSubShape( shape );
1048 const SMDS_MeshNode* aNodes [8];
1049 const SMDS_MeshNode* inFaceNode = 0;
1050 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1052 while ( itN->more() ) {
1053 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1054 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
1055 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1057 inFaceNode = aNodes[ i-1 ];
1060 // find middle point for (0,1,2,3)
1061 // and create a node in this point;
1063 if ( surface.IsNull() ) {
1065 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1069 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1072 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1074 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1076 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1077 myLastCreatedNodes.Append(newN);
1079 // create a new element
1080 const SMDS_MeshNode* N[6];
1081 if ( aBadRate1 <= aBadRate2 ) {
1088 newElem = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1089 aNodes[6], aNodes[7], newN );
1098 newElem = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1099 aNodes[7], aNodes[4], newN );
1101 aMesh->ChangeElementNodes( elem, N, 6 );
1105 // care of a new element
1107 myLastCreatedElems.Append(newElem);
1108 AddToSameGroups( newElem, elem, aMesh );
1110 // put a new triangle on the same shape
1112 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1117 //=======================================================================
1118 //function : BestSplit
1119 //purpose : Find better diagonal for cutting.
1120 //=======================================================================
1122 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1123 SMESH::Controls::NumericalFunctorPtr theCrit)
1125 myLastCreatedElems.Clear();
1126 myLastCreatedNodes.Clear();
1131 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1134 if( theQuad->NbNodes()==4 ||
1135 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1137 // retrieve element nodes
1138 const SMDS_MeshNode* aNodes [4];
1139 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1141 //while (itN->more())
1143 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1145 // compare two sets of possible triangles
1146 double aBadRate1, aBadRate2; // to what extent a set is bad
1147 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1148 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1149 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1151 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1152 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1153 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1155 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1156 return 1; // diagonal 1-3
1158 return 2; // diagonal 2-4
1165 // Methods of splitting volumes into tetra
1167 const int theHexTo5_1[5*4+1] =
1169 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1171 const int theHexTo5_2[5*4+1] =
1173 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1175 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1177 const int theHexTo6_1[6*4+1] =
1179 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
1181 const int theHexTo6_2[6*4+1] =
1183 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
1185 const int theHexTo6_3[6*4+1] =
1187 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
1189 const int theHexTo6_4[6*4+1] =
1191 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
1193 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1195 const int thePyraTo2_1[2*4+1] =
1197 0, 1, 2, 4, 0, 2, 3, 4, -1
1199 const int thePyraTo2_2[2*4+1] =
1201 1, 2, 3, 4, 1, 3, 0, 4, -1
1203 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1205 const int thePentaTo3_1[3*4+1] =
1207 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1209 const int thePentaTo3_2[3*4+1] =
1211 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1213 const int thePentaTo3_3[3*4+1] =
1215 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1217 const int thePentaTo3_4[3*4+1] =
1219 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1221 const int thePentaTo3_5[3*4+1] =
1223 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1225 const int thePentaTo3_6[3*4+1] =
1227 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1229 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1230 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1232 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1235 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1236 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1237 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1242 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1243 bool _baryNode; //!< additional node is to be created at cell barycenter
1244 bool _ownConn; //!< to delete _connectivity in destructor
1246 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1247 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1248 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1249 bool hasFacet( const TTriangleFacet& facet ) const
1251 const int* tetConn = _connectivity;
1252 for ( ; tetConn[0] >= 0; tetConn += 4 )
1253 if (( facet.contains( tetConn[0] ) +
1254 facet.contains( tetConn[1] ) +
1255 facet.contains( tetConn[2] ) +
1256 facet.contains( tetConn[3] )) == 3 )
1262 //=======================================================================
1264 * \brief return TSplitMethod for the given element
1266 //=======================================================================
1268 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1270 int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1272 // Find out how adjacent volumes are split
1274 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1275 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1276 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1278 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1279 maxTetConnSize += 4 * ( nbNodes - 2 );
1280 if ( nbNodes < 4 ) continue;
1282 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1283 const int* nInd = vol.GetFaceNodesIndices( iF );
1286 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1287 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1288 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1289 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1293 int iCom = 0; // common node of triangle faces to split into
1294 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1296 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1297 nInd[ iQ * ( (iCom+1)%nbNodes )],
1298 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1299 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1300 nInd[ iQ * ( (iCom+2)%nbNodes )],
1301 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1302 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1304 triaSplits.push_back( t012 );
1305 triaSplits.push_back( t023 );
1310 if ( !triaSplits.empty() )
1311 hasAdjacentSplits = true;
1314 // Among variants of split method select one compliant with adjacent volumes
1316 TSplitMethod method;
1317 if ( !vol.Element()->IsPoly() )
1319 int nbVariants = 2, nbTet = 0;
1320 const int** connVariants = 0;
1321 switch ( vol.Element()->GetEntityType() )
1323 case SMDSEntity_Hexa:
1324 case SMDSEntity_Quad_Hexa:
1325 if ( theMethodFlags & SMESH_MeshEditor::HEXA_TO_5 )
1326 connVariants = theHexTo5, nbTet = 5;
1328 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1330 case SMDSEntity_Pyramid:
1331 case SMDSEntity_Quad_Pyramid:
1332 connVariants = thePyraTo2; nbTet = 2;
1334 case SMDSEntity_Penta:
1335 case SMDSEntity_Quad_Penta:
1336 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1341 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1343 // check method compliancy with adjacent tetras,
1344 // all found splits must be among facets of tetras described by this method
1345 method = TSplitMethod( nbTet, connVariants[variant] );
1346 if ( hasAdjacentSplits && method._nbTetra > 0 )
1348 bool facetCreated = true;
1349 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1351 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1352 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1353 facetCreated = method.hasFacet( *facet );
1355 if ( !facetCreated )
1356 method = TSplitMethod(0); // incompatible method
1360 if ( method._nbTetra < 1 )
1362 // No standard method is applicable, use a generic solution:
1363 // each facet of a volume is split into triangles and
1364 // each of triangles and a volume barycenter form a tetrahedron.
1366 int* connectivity = new int[ maxTetConnSize + 1 ];
1367 method._connectivity = connectivity;
1368 method._ownConn = true;
1369 method._baryNode = true;
1372 int baryCenInd = vol.NbNodes();
1373 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1375 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1376 const int* nInd = vol.GetFaceNodesIndices( iF );
1377 // find common node of triangle facets of tetra to create
1378 int iCommon = 0; // index in linear numeration
1379 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1380 if ( !triaSplits.empty() )
1383 const TTriangleFacet* facet = &triaSplits.front();
1384 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1385 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1386 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1389 else if ( nbNodes > 3 )
1391 // find the best method of splitting into triangles by aspect ratio
1392 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1393 map< double, int > badness2iCommon;
1394 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1395 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1396 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1397 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1399 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1400 nodes[ iQ*((iLast-1)%nbNodes)],
1401 nodes[ iQ*((iLast )%nbNodes)]);
1402 double badness = getBadRate( &tria, aspectRatio );
1403 badness2iCommon.insert( make_pair( badness, iCommon ));
1405 // use iCommon with lowest badness
1406 iCommon = badness2iCommon.begin()->second;
1408 if ( iCommon >= nbNodes )
1409 iCommon = 0; // something wrong
1410 // fill connectivity of tetra
1411 int nbTet = nbNodes - 2;
1412 for ( int i = 0; i < nbTet; ++i )
1414 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1415 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1416 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1417 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1418 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1419 connectivity[ connSize++ ] = baryCenInd;
1423 connectivity[ connSize++ ] = -1;
1427 //================================================================================
1429 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1431 //================================================================================
1433 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1435 // find the tetrahedron including the three nodes of facet
1436 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1437 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1438 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1439 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1440 while ( volIt1->more() )
1442 const SMDS_MeshElement* v = volIt1->next();
1443 if ( v->GetEntityType() != ( v->IsQuadratic() ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra ))
1445 SMDS_ElemIteratorPtr volIt2 = n2->GetInverseElementIterator(SMDSAbs_Volume);
1446 while ( volIt2->more() )
1447 if ( v != volIt2->next() )
1449 SMDS_ElemIteratorPtr volIt3 = n3->GetInverseElementIterator(SMDSAbs_Volume);
1450 while ( volIt3->more() )
1451 if ( v == volIt3->next() )
1458 //=======================================================================
1459 //function : SplitVolumesIntoTetra
1460 //purpose : Split volumic elements into tetrahedra.
1461 //=======================================================================
1463 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1464 const int theMethodFlags)
1466 // std-like iterator on coordinates of nodes of mesh element
1467 typedef SMDS_StdIterator< TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1468 NXyzIterator xyzEnd;
1470 SMDS_VolumeTool volTool;
1471 SMESH_MesherHelper helper( *GetMesh());
1473 SMESHDS_SubMesh* subMesh = GetMeshDS()->MeshElements(1);
1474 SMESHDS_SubMesh* fSubMesh = subMesh;
1476 SMESH_SequenceOfElemPtr newNodes, newElems;
1478 TIDSortedElemSet::const_iterator elem = theElems.begin();
1479 for ( ; elem != theElems.end(); ++elem )
1481 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1482 if ( geomType <= SMDSEntity_Quad_Tetra )
1483 continue; // tetra or face or ...
1485 if ( !volTool.Set( *elem )) continue; // not volume? strange...
1487 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1488 if ( splitMethod._nbTetra < 1 ) continue;
1490 // find submesh to add new tetras in
1491 if ( !subMesh || !subMesh->Contains( *elem ))
1493 int shapeID = FindShape( *elem );
1494 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1495 subMesh = GetMeshDS()->MeshElements( shapeID );
1498 if ( (*elem)->IsQuadratic() )
1501 // add quadratic links to the helper
1502 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1504 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1505 for ( int iN = 0; iN < volTool.NbFaceNodes( iF ); iN += iQ )
1506 helper.AddTLinkNode( fNodes[iF], fNodes[iF+2], fNodes[iF+1] );
1508 helper.SetIsQuadratic( true );
1513 helper.SetIsQuadratic( false );
1515 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1516 if ( splitMethod._baryNode )
1518 // make a node at barycenter
1520 gc = accumulate( NXyzIterator((*elem)->nodesIterator()), xyzEnd, gc ) / nodes.size();
1521 SMDS_MeshNode* gcNode = helper.AddNode( gc.X(), gc.Y(), gc.Z() );
1522 nodes.push_back( gcNode );
1523 newNodes.Append( gcNode );
1527 helper.SetElementsOnShape( true );
1528 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1529 const int* tetConn = splitMethod._connectivity;
1530 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1531 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1532 nodes[ tetConn[1] ],
1533 nodes[ tetConn[2] ],
1534 nodes[ tetConn[3] ]));
1536 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1538 // Split faces on sides of the split volume
1540 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1541 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1543 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1544 if ( nbNodes < 4 ) continue;
1546 // find an existing face
1547 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1548 volTool.GetFaceNodes( iF ) + nbNodes*iQ );
1549 while ( const SMDS_MeshElement* face = GetMeshDS()->FindFace( fNodes ))
1551 // among possible triangles create ones discribed by split method
1552 const int* nInd = volTool.GetFaceNodesIndices( iF );
1553 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1554 int iCom = 0; // common node of triangle faces to split into
1555 list< TTriangleFacet > facets;
1556 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1558 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1559 nInd[ iQ * ( (iCom+1)%nbNodes )],
1560 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1561 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1562 nInd[ iQ * ( (iCom+2)%nbNodes )],
1563 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1564 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1566 facets.push_back( t012 );
1567 facets.push_back( t023 );
1568 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1569 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1570 nInd[ iQ * ((iLast-1)%nbNodes )],
1571 nInd[ iQ * ((iLast )%nbNodes )]));
1575 // find submesh to add new faces in
1576 if ( !fSubMesh || !fSubMesh->Contains( face ))
1578 int shapeID = FindShape( face );
1579 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1582 helper.SetElementsOnShape( false );
1583 vector< const SMDS_MeshElement* > triangles;
1584 list< TTriangleFacet >::iterator facet = facets.begin();
1585 for ( ; facet != facets.end(); ++facet )
1587 if ( !volTool.IsFaceExternal( iF ))
1588 swap( facet->_n2, facet->_n3 );
1589 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1590 volNodes[ facet->_n2 ],
1591 volNodes[ facet->_n3 ]));
1592 if ( triangles.back() && fSubMesh )
1593 fSubMesh->AddElement( triangles.back());
1594 newElems.Append( triangles.back() );
1596 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1597 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1600 } // loop on volume faces to split them into triangles
1602 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1604 } // loop on volumes to split
1606 myLastCreatedNodes = newNodes;
1607 myLastCreatedElems = newElems;
1610 //=======================================================================
1611 //function : AddToSameGroups
1612 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1613 //=======================================================================
1615 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1616 const SMDS_MeshElement* elemInGroups,
1617 SMESHDS_Mesh * aMesh)
1619 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1620 if (!groups.empty()) {
1621 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1622 for ( ; grIt != groups.end(); grIt++ ) {
1623 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1624 if ( group && group->Contains( elemInGroups ))
1625 group->SMDSGroup().Add( elemToAdd );
1631 //=======================================================================
1632 //function : RemoveElemFromGroups
1633 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1634 //=======================================================================
1635 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1636 SMESHDS_Mesh * aMesh)
1638 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1639 if (!groups.empty())
1641 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1642 for (; GrIt != groups.end(); GrIt++)
1644 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1645 if (!grp || grp->IsEmpty()) continue;
1646 grp->SMDSGroup().Remove(removeelem);
1651 //================================================================================
1653 * \brief Replace elemToRm by elemToAdd in the all groups
1655 //================================================================================
1657 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1658 const SMDS_MeshElement* elemToAdd,
1659 SMESHDS_Mesh * aMesh)
1661 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1662 if (!groups.empty()) {
1663 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1664 for ( ; grIt != groups.end(); grIt++ ) {
1665 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1666 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1667 group->SMDSGroup().Add( elemToAdd );
1672 //================================================================================
1674 * \brief Replace elemToRm by elemToAdd in the all groups
1676 //================================================================================
1678 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1679 const vector<const SMDS_MeshElement*>& elemToAdd,
1680 SMESHDS_Mesh * aMesh)
1682 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1683 if (!groups.empty())
1685 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1686 for ( ; grIt != groups.end(); grIt++ ) {
1687 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1688 if ( group && group->SMDSGroup().Remove( elemToRm ) )
1689 for ( int i = 0; i < elemToAdd.size(); ++i )
1690 group->SMDSGroup().Add( elemToAdd[ i ] );
1695 //=======================================================================
1696 //function : QuadToTri
1697 //purpose : Cut quadrangles into triangles.
1698 // theCrit is used to select a diagonal to cut
1699 //=======================================================================
1701 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1702 const bool the13Diag)
1704 myLastCreatedElems.Clear();
1705 myLastCreatedNodes.Clear();
1707 MESSAGE( "::QuadToTri()" );
1709 SMESHDS_Mesh * aMesh = GetMeshDS();
1711 Handle(Geom_Surface) surface;
1712 SMESH_MesherHelper helper( *GetMesh() );
1714 TIDSortedElemSet::iterator itElem;
1715 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1716 const SMDS_MeshElement* elem = *itElem;
1717 if ( !elem || elem->GetType() != SMDSAbs_Face )
1719 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1720 if(!isquad) continue;
1722 if(elem->NbNodes()==4) {
1723 // retrieve element nodes
1724 const SMDS_MeshNode* aNodes [4];
1725 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1727 while ( itN->more() )
1728 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1730 int aShapeId = FindShape( elem );
1731 const SMDS_MeshElement* newElem = 0;
1733 aMesh->ChangeElementNodes( elem, aNodes, 3 );
1734 newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1737 aMesh->ChangeElementNodes( elem, &aNodes[1], 3 );
1738 newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1740 myLastCreatedElems.Append(newElem);
1741 // put a new triangle on the same shape and add to the same groups
1743 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1744 AddToSameGroups( newElem, elem, aMesh );
1747 // Quadratic quadrangle
1749 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1751 // get surface elem is on
1752 int aShapeId = FindShape( elem );
1753 if ( aShapeId != helper.GetSubShapeID() ) {
1757 shape = aMesh->IndexToShape( aShapeId );
1758 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1759 TopoDS_Face face = TopoDS::Face( shape );
1760 surface = BRep_Tool::Surface( face );
1761 if ( !surface.IsNull() )
1762 helper.SetSubShape( shape );
1766 const SMDS_MeshNode* aNodes [8];
1767 const SMDS_MeshNode* inFaceNode = 0;
1768 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1770 while ( itN->more() ) {
1771 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1772 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
1773 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1775 inFaceNode = aNodes[ i-1 ];
1779 // find middle point for (0,1,2,3)
1780 // and create a node in this point;
1782 if ( surface.IsNull() ) {
1784 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1788 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
1791 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
1793 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1795 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1796 myLastCreatedNodes.Append(newN);
1798 // create a new element
1799 const SMDS_MeshElement* newElem = 0;
1800 const SMDS_MeshNode* N[6];
1808 newElem = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1809 aNodes[6], aNodes[7], newN );
1818 newElem = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1819 aNodes[7], aNodes[4], newN );
1821 myLastCreatedElems.Append(newElem);
1822 aMesh->ChangeElementNodes( elem, N, 6 );
1823 // put a new triangle on the same shape and add to the same groups
1825 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1826 AddToSameGroups( newElem, elem, aMesh );
1833 //=======================================================================
1834 //function : getAngle
1836 //=======================================================================
1838 double getAngle(const SMDS_MeshElement * tr1,
1839 const SMDS_MeshElement * tr2,
1840 const SMDS_MeshNode * n1,
1841 const SMDS_MeshNode * n2)
1843 double angle = 2*PI; // bad angle
1846 SMESH::Controls::TSequenceOfXYZ P1, P2;
1847 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
1848 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
1851 if(!tr1->IsQuadratic())
1852 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
1854 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
1855 if ( N1.SquareMagnitude() <= gp::Resolution() )
1857 if(!tr2->IsQuadratic())
1858 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
1860 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
1861 if ( N2.SquareMagnitude() <= gp::Resolution() )
1864 // find the first diagonal node n1 in the triangles:
1865 // take in account a diagonal link orientation
1866 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
1867 for ( int t = 0; t < 2; t++ ) {
1868 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
1869 int i = 0, iDiag = -1;
1870 while ( it->more()) {
1871 const SMDS_MeshElement *n = it->next();
1872 if ( n == n1 || n == n2 )
1876 if ( i - iDiag == 1 )
1877 nFirst[ t ] = ( n == n1 ? n2 : n1 );
1885 if ( nFirst[ 0 ] == nFirst[ 1 ] )
1888 angle = N1.Angle( N2 );
1893 // =================================================
1894 // class generating a unique ID for a pair of nodes
1895 // and able to return nodes by that ID
1896 // =================================================
1900 LinkID_Gen( const SMESHDS_Mesh* theMesh )
1901 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
1904 long GetLinkID (const SMDS_MeshNode * n1,
1905 const SMDS_MeshNode * n2) const
1907 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
1910 bool GetNodes (const long theLinkID,
1911 const SMDS_MeshNode* & theNode1,
1912 const SMDS_MeshNode* & theNode2) const
1914 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
1915 if ( !theNode1 ) return false;
1916 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
1917 if ( !theNode2 ) return false;
1923 const SMESHDS_Mesh* myMesh;
1928 //=======================================================================
1929 //function : TriToQuad
1930 //purpose : Fuse neighbour triangles into quadrangles.
1931 // theCrit is used to select a neighbour to fuse with.
1932 // theMaxAngle is a max angle between element normals at which
1933 // fusion is still performed.
1934 //=======================================================================
1936 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
1937 SMESH::Controls::NumericalFunctorPtr theCrit,
1938 const double theMaxAngle)
1940 myLastCreatedElems.Clear();
1941 myLastCreatedNodes.Clear();
1943 MESSAGE( "::TriToQuad()" );
1945 if ( !theCrit.get() )
1948 SMESHDS_Mesh * aMesh = GetMeshDS();
1950 // Prepare data for algo: build
1951 // 1. map of elements with their linkIDs
1952 // 2. map of linkIDs with their elements
1954 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
1955 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
1956 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
1957 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
1959 TIDSortedElemSet::iterator itElem;
1960 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1961 const SMDS_MeshElement* elem = *itElem;
1962 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
1963 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
1964 if(!IsTria) continue;
1966 // retrieve element nodes
1967 const SMDS_MeshNode* aNodes [4];
1968 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1971 aNodes[ i++ ] = cast2Node( itN->next() );
1972 aNodes[ 3 ] = aNodes[ 0 ];
1975 for ( i = 0; i < 3; i++ ) {
1976 SMESH_TLink link( aNodes[i], aNodes[i+1] );
1977 // check if elements sharing a link can be fused
1978 itLE = mapLi_listEl.find( link );
1979 if ( itLE != mapLi_listEl.end() ) {
1980 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
1982 const SMDS_MeshElement* elem2 = (*itLE).second.front();
1983 //if ( FindShape( elem ) != FindShape( elem2 ))
1984 // continue; // do not fuse triangles laying on different shapes
1985 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
1986 continue; // avoid making badly shaped quads
1987 (*itLE).second.push_back( elem );
1990 mapLi_listEl[ link ].push_back( elem );
1992 mapEl_setLi [ elem ].insert( link );
1995 // Clean the maps from the links shared by a sole element, ie
1996 // links to which only one element is bound in mapLi_listEl
1998 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
1999 int nbElems = (*itLE).second.size();
2000 if ( nbElems < 2 ) {
2001 const SMDS_MeshElement* elem = (*itLE).second.front();
2002 SMESH_TLink link = (*itLE).first;
2003 mapEl_setLi[ elem ].erase( link );
2004 if ( mapEl_setLi[ elem ].empty() )
2005 mapEl_setLi.erase( elem );
2009 // Algo: fuse triangles into quadrangles
2011 while ( ! mapEl_setLi.empty() ) {
2012 // Look for the start element:
2013 // the element having the least nb of shared links
2014 const SMDS_MeshElement* startElem = 0;
2016 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2017 int nbLinks = (*itEL).second.size();
2018 if ( nbLinks < minNbLinks ) {
2019 startElem = (*itEL).first;
2020 minNbLinks = nbLinks;
2021 if ( minNbLinks == 1 )
2026 // search elements to fuse starting from startElem or links of elements
2027 // fused earlyer - startLinks
2028 list< SMESH_TLink > startLinks;
2029 while ( startElem || !startLinks.empty() ) {
2030 while ( !startElem && !startLinks.empty() ) {
2031 // Get an element to start, by a link
2032 SMESH_TLink linkId = startLinks.front();
2033 startLinks.pop_front();
2034 itLE = mapLi_listEl.find( linkId );
2035 if ( itLE != mapLi_listEl.end() ) {
2036 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2037 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2038 for ( ; itE != listElem.end() ; itE++ )
2039 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2041 mapLi_listEl.erase( itLE );
2046 // Get candidates to be fused
2047 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2048 const SMESH_TLink *link12, *link13;
2050 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2051 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2052 ASSERT( !setLi.empty() );
2053 set< SMESH_TLink >::iterator itLi;
2054 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2056 const SMESH_TLink & link = (*itLi);
2057 itLE = mapLi_listEl.find( link );
2058 if ( itLE == mapLi_listEl.end() )
2061 const SMDS_MeshElement* elem = (*itLE).second.front();
2063 elem = (*itLE).second.back();
2064 mapLi_listEl.erase( itLE );
2065 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2076 // add other links of elem to list of links to re-start from
2077 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2078 set< SMESH_TLink >::iterator it;
2079 for ( it = links.begin(); it != links.end(); it++ ) {
2080 const SMESH_TLink& link2 = (*it);
2081 if ( link2 != link )
2082 startLinks.push_back( link2 );
2086 // Get nodes of possible quadrangles
2087 const SMDS_MeshNode *n12 [4], *n13 [4];
2088 bool Ok12 = false, Ok13 = false;
2089 const SMDS_MeshNode *linkNode1, *linkNode2;
2091 linkNode1 = link12->first;
2092 linkNode2 = link12->second;
2093 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2097 linkNode1 = link13->first;
2098 linkNode2 = link13->second;
2099 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2103 // Choose a pair to fuse
2104 if ( Ok12 && Ok13 ) {
2105 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2106 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2107 double aBadRate12 = getBadRate( &quad12, theCrit );
2108 double aBadRate13 = getBadRate( &quad13, theCrit );
2109 if ( aBadRate13 < aBadRate12 )
2116 // and remove fused elems and removed links from the maps
2117 mapEl_setLi.erase( tr1 );
2119 mapEl_setLi.erase( tr2 );
2120 mapLi_listEl.erase( *link12 );
2121 if(tr1->NbNodes()==3) {
2122 if( tr1->GetID() < tr2->GetID() ) {
2123 aMesh->ChangeElementNodes( tr1, n12, 4 );
2124 myLastCreatedElems.Append(tr1);
2125 aMesh->RemoveElement( tr2 );
2128 aMesh->ChangeElementNodes( tr2, n12, 4 );
2129 myLastCreatedElems.Append(tr2);
2130 aMesh->RemoveElement( tr1);
2134 const SMDS_MeshNode* N1 [6];
2135 const SMDS_MeshNode* N2 [6];
2136 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2137 // now we receive following N1 and N2 (using numeration as above image)
2138 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2139 // i.e. first nodes from both arrays determ new diagonal
2140 const SMDS_MeshNode* aNodes[8];
2149 if( tr1->GetID() < tr2->GetID() ) {
2150 GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 );
2151 myLastCreatedElems.Append(tr1);
2152 GetMeshDS()->RemoveElement( tr2 );
2155 GetMeshDS()->ChangeElementNodes( tr2, aNodes, 8 );
2156 myLastCreatedElems.Append(tr2);
2157 GetMeshDS()->RemoveElement( tr1 );
2159 // remove middle node (9)
2160 GetMeshDS()->RemoveNode( N1[4] );
2164 mapEl_setLi.erase( tr3 );
2165 mapLi_listEl.erase( *link13 );
2166 if(tr1->NbNodes()==3) {
2167 if( tr1->GetID() < tr2->GetID() ) {
2168 aMesh->ChangeElementNodes( tr1, n13, 4 );
2169 myLastCreatedElems.Append(tr1);
2170 aMesh->RemoveElement( tr3 );
2173 aMesh->ChangeElementNodes( tr3, n13, 4 );
2174 myLastCreatedElems.Append(tr3);
2175 aMesh->RemoveElement( tr1 );
2179 const SMDS_MeshNode* N1 [6];
2180 const SMDS_MeshNode* N2 [6];
2181 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2182 // now we receive following N1 and N2 (using numeration as above image)
2183 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2184 // i.e. first nodes from both arrays determ new diagonal
2185 const SMDS_MeshNode* aNodes[8];
2194 if( tr1->GetID() < tr2->GetID() ) {
2195 GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 );
2196 myLastCreatedElems.Append(tr1);
2197 GetMeshDS()->RemoveElement( tr3 );
2200 GetMeshDS()->ChangeElementNodes( tr3, aNodes, 8 );
2201 myLastCreatedElems.Append(tr3);
2202 GetMeshDS()->RemoveElement( tr1 );
2204 // remove middle node (9)
2205 GetMeshDS()->RemoveNode( N1[4] );
2209 // Next element to fuse: the rejected one
2211 startElem = Ok12 ? tr3 : tr2;
2213 } // if ( startElem )
2214 } // while ( startElem || !startLinks.empty() )
2215 } // while ( ! mapEl_setLi.empty() )
2221 /*#define DUMPSO(txt) \
2222 // cout << txt << endl;
2223 //=============================================================================
2227 //=============================================================================
2228 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2232 int tmp = idNodes[ i1 ];
2233 idNodes[ i1 ] = idNodes[ i2 ];
2234 idNodes[ i2 ] = tmp;
2235 gp_Pnt Ptmp = P[ i1 ];
2238 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2241 //=======================================================================
2242 //function : SortQuadNodes
2243 //purpose : Set 4 nodes of a quadrangle face in a good order.
2244 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2246 //=======================================================================
2248 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2253 for ( i = 0; i < 4; i++ ) {
2254 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2256 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2259 gp_Vec V1(P[0], P[1]);
2260 gp_Vec V2(P[0], P[2]);
2261 gp_Vec V3(P[0], P[3]);
2263 gp_Vec Cross1 = V1 ^ V2;
2264 gp_Vec Cross2 = V2 ^ V3;
2267 if (Cross1.Dot(Cross2) < 0)
2272 if (Cross1.Dot(Cross2) < 0)
2276 swap ( i, i + 1, idNodes, P );
2278 // for ( int ii = 0; ii < 4; ii++ ) {
2279 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2280 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2286 //=======================================================================
2287 //function : SortHexaNodes
2288 //purpose : Set 8 nodes of a hexahedron in a good order.
2289 // Return success status
2290 //=======================================================================
2292 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2297 DUMPSO( "INPUT: ========================================");
2298 for ( i = 0; i < 8; i++ ) {
2299 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2300 if ( !n ) return false;
2301 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2302 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2304 DUMPSO( "========================================");
2307 set<int> faceNodes; // ids of bottom face nodes, to be found
2308 set<int> checkedId1; // ids of tried 2-nd nodes
2309 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2310 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2311 int iMin, iLoop1 = 0;
2313 // Loop to try the 2-nd nodes
2315 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2317 // Find not checked 2-nd node
2318 for ( i = 1; i < 8; i++ )
2319 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2320 int id1 = idNodes[i];
2321 swap ( 1, i, idNodes, P );
2322 checkedId1.insert ( id1 );
2326 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2327 // ie that all but meybe one (id3 which is on the same face) nodes
2328 // lay on the same side from the triangle plane.
2330 bool manyInPlane = false; // more than 4 nodes lay in plane
2332 while ( ++iLoop2 < 6 ) {
2334 // get 1-2-3 plane coeffs
2335 Standard_Real A, B, C, D;
2336 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2337 if ( N.SquareMagnitude() > gp::Resolution() )
2339 gp_Pln pln ( P[0], N );
2340 pln.Coefficients( A, B, C, D );
2342 // find the node (iMin) closest to pln
2343 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2345 for ( i = 3; i < 8; i++ ) {
2346 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2347 if ( fabs( dist[i] ) < minDist ) {
2348 minDist = fabs( dist[i] );
2351 if ( fabs( dist[i] ) <= tol )
2352 idInPln.insert( idNodes[i] );
2355 // there should not be more than 4 nodes in bottom plane
2356 if ( idInPln.size() > 1 )
2358 DUMPSO( "### idInPln.size() = " << idInPln.size());
2359 // idInPlane does not contain the first 3 nodes
2360 if ( manyInPlane || idInPln.size() == 5)
2361 return false; // all nodes in one plane
2364 // set the 1-st node to be not in plane
2365 for ( i = 3; i < 8; i++ ) {
2366 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2367 DUMPSO( "### Reset 0-th node");
2368 swap( 0, i, idNodes, P );
2373 // reset to re-check second nodes
2374 leastDist = DBL_MAX;
2378 break; // from iLoop2;
2381 // check that the other 4 nodes are on the same side
2382 bool sameSide = true;
2383 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2384 for ( i = 3; sameSide && i < 8; i++ ) {
2386 sameSide = ( isNeg == dist[i] <= 0.);
2389 // keep best solution
2390 if ( sameSide && minDist < leastDist ) {
2391 leastDist = minDist;
2393 faceNodes.insert( idNodes[ 1 ] );
2394 faceNodes.insert( idNodes[ 2 ] );
2395 faceNodes.insert( idNodes[ iMin ] );
2396 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2397 << " leastDist = " << leastDist);
2398 if ( leastDist <= DBL_MIN )
2403 // set next 3-d node to check
2404 int iNext = 2 + iLoop2;
2406 DUMPSO( "Try 2-nd");
2407 swap ( 2, iNext, idNodes, P );
2409 } // while ( iLoop2 < 6 )
2412 if ( faceNodes.empty() ) return false;
2414 // Put the faceNodes in proper places
2415 for ( i = 4; i < 8; i++ ) {
2416 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2417 // find a place to put
2419 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2421 DUMPSO( "Set faceNodes");
2422 swap ( iTo, i, idNodes, P );
2427 // Set nodes of the found bottom face in good order
2428 DUMPSO( " Found bottom face: ");
2429 i = SortQuadNodes( theMesh, idNodes );
2431 gp_Pnt Ptmp = P[ i ];
2436 // for ( int ii = 0; ii < 4; ii++ ) {
2437 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2438 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2441 // Gravity center of the top and bottom faces
2442 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2443 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2445 // Get direction from the bottom to the top face
2446 gp_Vec upDir ( aGCb, aGCt );
2447 Standard_Real upDirSize = upDir.Magnitude();
2448 if ( upDirSize <= gp::Resolution() ) return false;
2451 // Assure that the bottom face normal points up
2452 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2453 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2454 if ( Nb.Dot( upDir ) < 0 ) {
2455 DUMPSO( "Reverse bottom face");
2456 swap( 1, 3, idNodes, P );
2459 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2460 Standard_Real minDist = DBL_MAX;
2461 for ( i = 4; i < 8; i++ ) {
2462 // projection of P[i] to the plane defined by P[0] and upDir
2463 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2464 Standard_Real sqDist = P[0].SquareDistance( Pp );
2465 if ( sqDist < minDist ) {
2470 DUMPSO( "Set 4-th");
2471 swap ( 4, iMin, idNodes, P );
2473 // Set nodes of the top face in good order
2474 DUMPSO( "Sort top face");
2475 i = SortQuadNodes( theMesh, &idNodes[4] );
2478 gp_Pnt Ptmp = P[ i ];
2483 // Assure that direction of the top face normal is from the bottom face
2484 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2485 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2486 if ( Nt.Dot( upDir ) < 0 ) {
2487 DUMPSO( "Reverse top face");
2488 swap( 5, 7, idNodes, P );
2491 // DUMPSO( "OUTPUT: ========================================");
2492 // for ( i = 0; i < 8; i++ ) {
2493 // float *p = ugrid->GetPoint(idNodes[i]);
2494 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2500 //================================================================================
2502 * \brief Return nodes linked to the given one
2503 * \param theNode - the node
2504 * \param linkedNodes - the found nodes
2505 * \param type - the type of elements to check
2507 * Medium nodes are ignored
2509 //================================================================================
2511 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2512 TIDSortedElemSet & linkedNodes,
2513 SMDSAbs_ElementType type )
2515 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2516 while ( elemIt->more() )
2518 const SMDS_MeshElement* elem = elemIt->next();
2519 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2520 if ( elem->GetType() == SMDSAbs_Volume )
2522 SMDS_VolumeTool vol( elem );
2523 while ( nodeIt->more() ) {
2524 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2525 if ( theNode != n && vol.IsLinked( theNode, n ))
2526 linkedNodes.insert( n );
2531 for ( int i = 0; nodeIt->more(); ++i ) {
2532 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2533 if ( n == theNode ) {
2534 int iBefore = i - 1;
2536 if ( elem->IsQuadratic() ) {
2537 int nb = elem->NbNodes() / 2;
2538 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2539 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2541 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2542 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2549 //=======================================================================
2550 //function : laplacianSmooth
2551 //purpose : pulls theNode toward the center of surrounding nodes directly
2552 // connected to that node along an element edge
2553 //=======================================================================
2555 void laplacianSmooth(const SMDS_MeshNode* theNode,
2556 const Handle(Geom_Surface)& theSurface,
2557 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2559 // find surrounding nodes
2561 TIDSortedElemSet nodeSet;
2562 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2564 // compute new coodrs
2566 double coord[] = { 0., 0., 0. };
2567 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2568 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2569 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2570 if ( theSurface.IsNull() ) { // smooth in 3D
2571 coord[0] += node->X();
2572 coord[1] += node->Y();
2573 coord[2] += node->Z();
2575 else { // smooth in 2D
2576 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2577 gp_XY* uv = theUVMap[ node ];
2578 coord[0] += uv->X();
2579 coord[1] += uv->Y();
2582 int nbNodes = nodeSet.size();
2585 coord[0] /= nbNodes;
2586 coord[1] /= nbNodes;
2588 if ( !theSurface.IsNull() ) {
2589 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2590 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2591 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2597 coord[2] /= nbNodes;
2601 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2604 //=======================================================================
2605 //function : centroidalSmooth
2606 //purpose : pulls theNode toward the element-area-weighted centroid of the
2607 // surrounding elements
2608 //=======================================================================
2610 void centroidalSmooth(const SMDS_MeshNode* theNode,
2611 const Handle(Geom_Surface)& theSurface,
2612 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2614 gp_XYZ aNewXYZ(0.,0.,0.);
2615 SMESH::Controls::Area anAreaFunc;
2616 double totalArea = 0.;
2621 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2622 while ( elemIt->more() )
2624 const SMDS_MeshElement* elem = elemIt->next();
2627 gp_XYZ elemCenter(0.,0.,0.);
2628 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2629 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2630 int nn = elem->NbNodes();
2631 if(elem->IsQuadratic()) nn = nn/2;
2633 //while ( itN->more() ) {
2635 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2637 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2638 aNodePoints.push_back( aP );
2639 if ( !theSurface.IsNull() ) { // smooth in 2D
2640 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2641 gp_XY* uv = theUVMap[ aNode ];
2642 aP.SetCoord( uv->X(), uv->Y(), 0. );
2646 double elemArea = anAreaFunc.GetValue( aNodePoints );
2647 totalArea += elemArea;
2649 aNewXYZ += elemCenter * elemArea;
2651 aNewXYZ /= totalArea;
2652 if ( !theSurface.IsNull() ) {
2653 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2654 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2659 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2662 //=======================================================================
2663 //function : getClosestUV
2664 //purpose : return UV of closest projection
2665 //=======================================================================
2667 static bool getClosestUV (Extrema_GenExtPS& projector,
2668 const gp_Pnt& point,
2671 projector.Perform( point );
2672 if ( projector.IsDone() ) {
2673 double u, v, minVal = DBL_MAX;
2674 for ( int i = projector.NbExt(); i > 0; i-- )
2675 if ( projector.Value( i ) < minVal ) {
2676 minVal = projector.Value( i );
2677 projector.Point( i ).Parameter( u, v );
2679 result.SetCoord( u, v );
2685 //=======================================================================
2687 //purpose : Smooth theElements during theNbIterations or until a worst
2688 // element has aspect ratio <= theTgtAspectRatio.
2689 // Aspect Ratio varies in range [1.0, inf].
2690 // If theElements is empty, the whole mesh is smoothed.
2691 // theFixedNodes contains additionally fixed nodes. Nodes built
2692 // on edges and boundary nodes are always fixed.
2693 //=======================================================================
2695 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
2696 set<const SMDS_MeshNode*> & theFixedNodes,
2697 const SmoothMethod theSmoothMethod,
2698 const int theNbIterations,
2699 double theTgtAspectRatio,
2702 myLastCreatedElems.Clear();
2703 myLastCreatedNodes.Clear();
2705 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2707 if ( theTgtAspectRatio < 1.0 )
2708 theTgtAspectRatio = 1.0;
2710 const double disttol = 1.e-16;
2712 SMESH::Controls::AspectRatio aQualityFunc;
2714 SMESHDS_Mesh* aMesh = GetMeshDS();
2716 if ( theElems.empty() ) {
2717 // add all faces to theElems
2718 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2719 while ( fIt->more() ) {
2720 const SMDS_MeshElement* face = fIt->next();
2721 theElems.insert( face );
2724 // get all face ids theElems are on
2725 set< int > faceIdSet;
2726 TIDSortedElemSet::iterator itElem;
2728 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2729 int fId = FindShape( *itElem );
2730 // check that corresponding submesh exists and a shape is face
2732 faceIdSet.find( fId ) == faceIdSet.end() &&
2733 aMesh->MeshElements( fId )) {
2734 TopoDS_Shape F = aMesh->IndexToShape( fId );
2735 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2736 faceIdSet.insert( fId );
2739 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2741 // ===============================================
2742 // smooth elements on each TopoDS_Face separately
2743 // ===============================================
2745 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2746 for ( ; fId != faceIdSet.rend(); ++fId ) {
2747 // get face surface and submesh
2748 Handle(Geom_Surface) surface;
2749 SMESHDS_SubMesh* faceSubMesh = 0;
2751 double fToler2 = 0, vPeriod = 0., uPeriod = 0., f,l;
2752 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2753 bool isUPeriodic = false, isVPeriodic = false;
2755 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
2756 surface = BRep_Tool::Surface( face );
2757 faceSubMesh = aMesh->MeshElements( *fId );
2758 fToler2 = BRep_Tool::Tolerance( face );
2759 fToler2 *= fToler2 * 10.;
2760 isUPeriodic = surface->IsUPeriodic();
2762 vPeriod = surface->UPeriod();
2763 isVPeriodic = surface->IsVPeriodic();
2765 uPeriod = surface->VPeriod();
2766 surface->Bounds( u1, u2, v1, v2 );
2768 // ---------------------------------------------------------
2769 // for elements on a face, find movable and fixed nodes and
2770 // compute UV for them
2771 // ---------------------------------------------------------
2772 bool checkBoundaryNodes = false;
2773 bool isQuadratic = false;
2774 set<const SMDS_MeshNode*> setMovableNodes;
2775 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
2776 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
2777 list< const SMDS_MeshElement* > elemsOnFace;
2779 Extrema_GenExtPS projector;
2780 GeomAdaptor_Surface surfAdaptor;
2781 if ( !surface.IsNull() ) {
2782 surfAdaptor.Load( surface );
2783 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
2785 int nbElemOnFace = 0;
2786 itElem = theElems.begin();
2787 // loop on not yet smoothed elements: look for elems on a face
2788 while ( itElem != theElems.end() ) {
2789 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
2790 break; // all elements found
2792 const SMDS_MeshElement* elem = *itElem;
2793 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
2794 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
2798 elemsOnFace.push_back( elem );
2799 theElems.erase( itElem++ );
2803 isQuadratic = elem->IsQuadratic();
2805 // get movable nodes of elem
2806 const SMDS_MeshNode* node;
2807 SMDS_TypeOfPosition posType;
2808 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2809 int nn = 0, nbn = elem->NbNodes();
2810 if(elem->IsQuadratic())
2812 while ( nn++ < nbn ) {
2813 node = static_cast<const SMDS_MeshNode*>( itN->next() );
2814 const SMDS_PositionPtr& pos = node->GetPosition();
2815 posType = pos.get() ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
2816 if (posType != SMDS_TOP_EDGE &&
2817 posType != SMDS_TOP_VERTEX &&
2818 theFixedNodes.find( node ) == theFixedNodes.end())
2820 // check if all faces around the node are on faceSubMesh
2821 // because a node on edge may be bound to face
2822 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
2824 if ( faceSubMesh ) {
2825 while ( eIt->more() && all ) {
2826 const SMDS_MeshElement* e = eIt->next();
2827 all = faceSubMesh->Contains( e );
2831 setMovableNodes.insert( node );
2833 checkBoundaryNodes = true;
2835 if ( posType == SMDS_TOP_3DSPACE )
2836 checkBoundaryNodes = true;
2839 if ( surface.IsNull() )
2842 // get nodes to check UV
2843 list< const SMDS_MeshNode* > uvCheckNodes;
2844 itN = elem->nodesIterator();
2845 nn = 0; nbn = elem->NbNodes();
2846 if(elem->IsQuadratic())
2848 while ( nn++ < nbn ) {
2849 node = static_cast<const SMDS_MeshNode*>( itN->next() );
2850 if ( uvMap.find( node ) == uvMap.end() )
2851 uvCheckNodes.push_back( node );
2852 // add nodes of elems sharing node
2853 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
2854 // while ( eIt->more() ) {
2855 // const SMDS_MeshElement* e = eIt->next();
2856 // if ( e != elem ) {
2857 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
2858 // while ( nIt->more() ) {
2859 // const SMDS_MeshNode* n =
2860 // static_cast<const SMDS_MeshNode*>( nIt->next() );
2861 // if ( uvMap.find( n ) == uvMap.end() )
2862 // uvCheckNodes.push_back( n );
2868 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
2869 for ( ; n != uvCheckNodes.end(); ++n ) {
2872 const SMDS_PositionPtr& pos = node->GetPosition();
2873 posType = pos.get() ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
2875 switch ( posType ) {
2876 case SMDS_TOP_FACE: {
2877 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos.get();
2878 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
2881 case SMDS_TOP_EDGE: {
2882 TopoDS_Shape S = aMesh->IndexToShape( pos->GetShapeId() );
2883 Handle(Geom2d_Curve) pcurve;
2884 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
2885 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
2886 if ( !pcurve.IsNull() ) {
2887 double u = (( SMDS_EdgePosition* ) pos.get() )->GetUParameter();
2888 uv = pcurve->Value( u ).XY();
2892 case SMDS_TOP_VERTEX: {
2893 TopoDS_Shape S = aMesh->IndexToShape( pos->GetShapeId() );
2894 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
2895 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
2900 // check existing UV
2901 bool project = true;
2902 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
2903 double dist1 = DBL_MAX, dist2 = 0;
2904 if ( posType != SMDS_TOP_3DSPACE ) {
2905 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
2906 project = dist1 > fToler2;
2908 if ( project ) { // compute new UV
2910 if ( !getClosestUV( projector, pNode, newUV )) {
2911 MESSAGE("Node Projection Failed " << node);
2915 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
2917 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
2919 if ( posType != SMDS_TOP_3DSPACE )
2920 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
2921 if ( dist2 < dist1 )
2925 // store UV in the map
2926 listUV.push_back( uv );
2927 uvMap.insert( make_pair( node, &listUV.back() ));
2929 } // loop on not yet smoothed elements
2931 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
2932 checkBoundaryNodes = true;
2934 // fix nodes on mesh boundary
2936 if ( checkBoundaryNodes ) {
2937 map< NLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
2938 map< NLink, int >::iterator link_nb;
2939 // put all elements links to linkNbMap
2940 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
2941 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
2942 const SMDS_MeshElement* elem = (*elemIt);
2943 int nbn = elem->NbNodes();
2944 if(elem->IsQuadratic())
2946 // loop on elem links: insert them in linkNbMap
2947 const SMDS_MeshNode* curNode, *prevNode = elem->GetNodeWrap( nbn );
2948 for ( int iN = 0; iN < nbn; ++iN ) {
2949 curNode = elem->GetNode( iN );
2951 if ( curNode < prevNode ) link = make_pair( curNode , prevNode );
2952 else link = make_pair( prevNode , curNode );
2954 link_nb = linkNbMap.find( link );
2955 if ( link_nb == linkNbMap.end() )
2956 linkNbMap.insert( make_pair ( link, 1 ));
2961 // remove nodes that are in links encountered only once from setMovableNodes
2962 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
2963 if ( link_nb->second == 1 ) {
2964 setMovableNodes.erase( link_nb->first.first );
2965 setMovableNodes.erase( link_nb->first.second );
2970 // -----------------------------------------------------
2971 // for nodes on seam edge, compute one more UV ( uvMap2 );
2972 // find movable nodes linked to nodes on seam and which
2973 // are to be smoothed using the second UV ( uvMap2 )
2974 // -----------------------------------------------------
2976 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
2977 if ( !surface.IsNull() ) {
2978 TopExp_Explorer eExp( face, TopAbs_EDGE );
2979 for ( ; eExp.More(); eExp.Next() ) {
2980 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
2981 if ( !BRep_Tool::IsClosed( edge, face ))
2983 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
2984 if ( !sm ) continue;
2985 // find out which parameter varies for a node on seam
2988 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
2989 if ( pcurve.IsNull() ) continue;
2990 uv1 = pcurve->Value( f );
2992 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
2993 if ( pcurve.IsNull() ) continue;
2994 uv2 = pcurve->Value( f );
2995 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
2997 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
2998 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3000 // get nodes on seam and its vertices
3001 list< const SMDS_MeshNode* > seamNodes;
3002 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3003 while ( nSeamIt->more() ) {
3004 const SMDS_MeshNode* node = nSeamIt->next();
3005 if ( !isQuadratic || !IsMedium( node ))
3006 seamNodes.push_back( node );
3008 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3009 for ( ; vExp.More(); vExp.Next() ) {
3010 sm = aMesh->MeshElements( vExp.Current() );
3012 nSeamIt = sm->GetNodes();
3013 while ( nSeamIt->more() )
3014 seamNodes.push_back( nSeamIt->next() );
3017 // loop on nodes on seam
3018 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3019 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3020 const SMDS_MeshNode* nSeam = *noSeIt;
3021 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3022 if ( n_uv == uvMap.end() )
3025 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3026 // set the second UV
3027 listUV.push_back( *n_uv->second );
3028 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3029 if ( uvMap2.empty() )
3030 uvMap2 = uvMap; // copy the uvMap contents
3031 uvMap2[ nSeam ] = &listUV.back();
3033 // collect movable nodes linked to ones on seam in nodesNearSeam
3034 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3035 while ( eIt->more() ) {
3036 const SMDS_MeshElement* e = eIt->next();
3037 int nbUseMap1 = 0, nbUseMap2 = 0;
3038 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3039 int nn = 0, nbn = e->NbNodes();
3040 if(e->IsQuadratic()) nbn = nbn/2;
3041 while ( nn++ < nbn )
3043 const SMDS_MeshNode* n =
3044 static_cast<const SMDS_MeshNode*>( nIt->next() );
3046 setMovableNodes.find( n ) == setMovableNodes.end() )
3048 // add only nodes being closer to uv2 than to uv1
3049 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3050 0.5 * ( n->Y() + nSeam->Y() ),
3051 0.5 * ( n->Z() + nSeam->Z() ));
3053 getClosestUV( projector, pMid, uv );
3054 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3055 nodesNearSeam.insert( n );
3061 // for centroidalSmooth all element nodes must
3062 // be on one side of a seam
3063 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3064 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3066 while ( nn++ < nbn ) {
3067 const SMDS_MeshNode* n =
3068 static_cast<const SMDS_MeshNode*>( nIt->next() );
3069 setMovableNodes.erase( n );
3073 } // loop on nodes on seam
3074 } // loop on edge of a face
3075 } // if ( !face.IsNull() )
3077 if ( setMovableNodes.empty() ) {
3078 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3079 continue; // goto next face
3087 double maxRatio = -1., maxDisplacement = -1.;
3088 set<const SMDS_MeshNode*>::iterator nodeToMove;
3089 for ( it = 0; it < theNbIterations; it++ ) {
3090 maxDisplacement = 0.;
3091 nodeToMove = setMovableNodes.begin();
3092 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3093 const SMDS_MeshNode* node = (*nodeToMove);
3094 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3097 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3098 if ( theSmoothMethod == LAPLACIAN )
3099 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3101 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3103 // node displacement
3104 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3105 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3106 if ( aDispl > maxDisplacement )
3107 maxDisplacement = aDispl;
3109 // no node movement => exit
3110 //if ( maxDisplacement < 1.e-16 ) {
3111 if ( maxDisplacement < disttol ) {
3112 MESSAGE("-- no node movement --");
3116 // check elements quality
3118 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3119 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3120 const SMDS_MeshElement* elem = (*elemIt);
3121 if ( !elem || elem->GetType() != SMDSAbs_Face )
3123 SMESH::Controls::TSequenceOfXYZ aPoints;
3124 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3125 double aValue = aQualityFunc.GetValue( aPoints );
3126 if ( aValue > maxRatio )
3130 if ( maxRatio <= theTgtAspectRatio ) {
3131 MESSAGE("-- quality achived --");
3134 if (it+1 == theNbIterations) {
3135 MESSAGE("-- Iteration limit exceeded --");
3137 } // smoothing iterations
3139 MESSAGE(" Face id: " << *fId <<
3140 " Nb iterstions: " << it <<
3141 " Displacement: " << maxDisplacement <<
3142 " Aspect Ratio " << maxRatio);
3144 // ---------------------------------------
3145 // new nodes positions are computed,
3146 // record movement in DS and set new UV
3147 // ---------------------------------------
3148 nodeToMove = setMovableNodes.begin();
3149 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3150 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3151 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3152 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3153 if ( node_uv != uvMap.end() ) {
3154 gp_XY* uv = node_uv->second;
3156 ( SMDS_PositionPtr( new SMDS_FacePosition( *fId, uv->X(), uv->Y() )));
3160 // move medium nodes of quadratic elements
3163 SMESH_MesherHelper helper( *GetMesh() );
3164 if ( !face.IsNull() )
3165 helper.SetSubShape( face );
3166 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3167 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3168 const SMDS_QuadraticFaceOfNodes* QF =
3169 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (*elemIt);
3171 vector<const SMDS_MeshNode*> Ns;
3172 Ns.reserve(QF->NbNodes()+1);
3173 SMDS_NodeIteratorPtr anIter = QF->interlacedNodesIterator();
3174 while ( anIter->more() )
3175 Ns.push_back( anIter->next() );
3176 Ns.push_back( Ns[0] );
3178 for(int i=0; i<QF->NbNodes(); i=i+2) {
3179 if ( !surface.IsNull() ) {
3180 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3181 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3182 gp_XY uv = ( uv1 + uv2 ) / 2.;
3183 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3184 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3187 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3188 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3189 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3191 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3192 fabs( Ns[i+1]->Y() - y ) > disttol ||
3193 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3194 // we have to move i+1 node
3195 aMesh->MoveNode( Ns[i+1], x, y, z );
3202 } // loop on face ids
3206 //=======================================================================
3207 //function : isReverse
3208 //purpose : Return true if normal of prevNodes is not co-directied with
3209 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3210 // iNotSame is where prevNodes and nextNodes are different
3211 //=======================================================================
3213 static bool isReverse(vector<const SMDS_MeshNode*> prevNodes,
3214 vector<const SMDS_MeshNode*> nextNodes,
3218 int iBeforeNotSame = ( iNotSame == 0 ? nbNodes - 1 : iNotSame - 1 );
3219 int iAfterNotSame = ( iNotSame + 1 == nbNodes ? 0 : iNotSame + 1 );
3221 const SMDS_MeshNode* nB = prevNodes[ iBeforeNotSame ];
3222 const SMDS_MeshNode* nA = prevNodes[ iAfterNotSame ];
3223 const SMDS_MeshNode* nP = prevNodes[ iNotSame ];
3224 const SMDS_MeshNode* nN = nextNodes[ iNotSame ];
3226 gp_Pnt pB ( nB->X(), nB->Y(), nB->Z() );
3227 gp_Pnt pA ( nA->X(), nA->Y(), nA->Z() );
3228 gp_Pnt pP ( nP->X(), nP->Y(), nP->Z() );
3229 gp_Pnt pN ( nN->X(), nN->Y(), nN->Z() );
3231 gp_Vec vB ( pP, pB ), vA ( pP, pA ), vN ( pP, pN );
3233 return (vA ^ vB) * vN < 0.0;
3236 //=======================================================================
3238 * \brief Create elements by sweeping an element
3239 * \param elem - element to sweep
3240 * \param newNodesItVec - nodes generated from each node of the element
3241 * \param newElems - generated elements
3242 * \param nbSteps - number of sweeping steps
3243 * \param srcElements - to append elem for each generated element
3245 //=======================================================================
3247 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3248 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3249 list<const SMDS_MeshElement*>& newElems,
3251 SMESH_SequenceOfElemPtr& srcElements)
3253 SMESHDS_Mesh* aMesh = GetMeshDS();
3255 // Loop on elem nodes:
3256 // find new nodes and detect same nodes indices
3257 int nbNodes = elem->NbNodes();
3258 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3259 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3260 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3261 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3263 int iNode, nbSame = 0, iNotSameNode = 0, iSameNode = 0;
3264 vector<int> sames(nbNodes);
3265 vector<bool> issimple(nbNodes);
3267 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3268 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3269 const SMDS_MeshNode* node = nnIt->first;
3270 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3271 if ( listNewNodes.empty() ) {
3275 issimple[iNode] = (listNewNodes.size()==nbSteps); // is node medium
3277 itNN[ iNode ] = listNewNodes.begin();
3278 prevNod[ iNode ] = node;
3279 nextNod[ iNode ] = listNewNodes.front();
3280 if( !elem->IsQuadratic() || !issimple[iNode] ) {
3281 if ( prevNod[ iNode ] != nextNod [ iNode ])
3282 iNotSameNode = iNode;
3286 sames[nbSame++] = iNode;
3291 //cout<<" nbSame = "<<nbSame<<endl;
3292 if ( nbSame == nbNodes || nbSame > 2) {
3293 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3294 //INFOS( " Too many same nodes of element " << elem->GetID() );
3298 // if( elem->IsQuadratic() && nbSame>0 ) {
3299 // MESSAGE( "Can not rotate quadratic element " << elem->GetID() );
3303 int iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3304 int nbBaseNodes = ( elem->IsQuadratic() ? nbNodes/2 : nbNodes );
3306 iBeforeSame = ( iSameNode == 0 ? nbBaseNodes - 1 : iSameNode - 1 );
3307 iAfterSame = ( iSameNode + 1 == nbBaseNodes ? 0 : iSameNode + 1 );
3308 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3312 //cout<<" prevNod[0]="<< prevNod[0]<<" prevNod[1]="<< prevNod[1]
3313 // <<" prevNod[2]="<< prevNod[2]<<" prevNod[3]="<< prevNod[4]
3314 // <<" prevNod[4]="<< prevNod[4]<<" prevNod[5]="<< prevNod[5]
3315 // <<" prevNod[6]="<< prevNod[6]<<" prevNod[7]="<< prevNod[7]<<endl;
3317 // check element orientation
3319 if ( nbNodes > 2 && !isReverse( prevNod, nextNod, nbNodes, iNotSameNode )) {
3320 //MESSAGE("Reversed elem " << elem );
3324 std::swap( iBeforeSame, iAfterSame );
3327 // make new elements
3328 for (int iStep = 0; iStep < nbSteps; iStep++ ) {
3330 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3331 if(issimple[iNode]) {
3332 nextNod[ iNode ] = *itNN[ iNode ];
3336 if( elem->GetType()==SMDSAbs_Node ) {
3337 // we have to use two nodes
3338 midlNod[ iNode ] = *itNN[ iNode ];
3340 nextNod[ iNode ] = *itNN[ iNode ];
3343 else if(!elem->IsQuadratic() || elem->IsMediumNode(prevNod[iNode]) ) {
3344 // we have to use each second node
3346 nextNod[ iNode ] = *itNN[ iNode ];
3350 // we have to use two nodes
3351 midlNod[ iNode ] = *itNN[ iNode ];
3353 nextNod[ iNode ] = *itNN[ iNode ];
3358 SMDS_MeshElement* aNewElem = 0;
3359 if(!elem->IsPoly()) {
3360 switch ( nbNodes ) {
3364 if ( nbSame == 0 ) {
3366 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3368 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3374 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3375 nextNod[ 1 ], nextNod[ 0 ] );
3377 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3378 nextNod[ iNotSameNode ] );
3382 case 3: { // TRIANGLE or quadratic edge
3383 if(elem->GetType() == SMDSAbs_Face) { // TRIANGLE
3385 if ( nbSame == 0 ) // --- pentahedron
3386 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3387 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ] );
3389 else if ( nbSame == 1 ) // --- pyramid
3390 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3391 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3392 nextNod[ iSameNode ]);
3394 else // 2 same nodes: --- tetrahedron
3395 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3396 nextNod[ iNotSameNode ]);
3398 else { // quadratic edge
3399 if(nbSame==0) { // quadratic quadrangle
3400 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], nextNod[1], prevNod[1],
3401 midlNod[0], nextNod[2], midlNod[1], prevNod[2]);
3403 else if(nbSame==1) { // quadratic triangle
3405 return; // medium node on axis
3407 else if(sames[0]==0) {
3408 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3409 nextNod[2], midlNod[1], prevNod[2]);
3411 else { // sames[0]==1
3412 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3413 midlNod[0], nextNod[2], prevNod[2]);
3422 case 4: { // QUADRANGLE
3424 if ( nbSame == 0 ) // --- hexahedron
3425 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], prevNod[ 3 ],
3426 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ], nextNod[ 3 ]);
3428 else if ( nbSame == 1 ) { // --- pyramid + pentahedron
3429 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3430 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3431 nextNod[ iSameNode ]);
3432 newElems.push_back( aNewElem );
3433 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3434 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3435 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3437 else if ( nbSame == 2 ) { // pentahedron
3438 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3439 // iBeforeSame is same too
3440 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3441 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3442 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3444 // iAfterSame is same too
3445 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3446 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3447 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3451 case 6: { // quadratic triangle
3452 // create pentahedron with 15 nodes
3454 if(i0>0) { // reversed case
3455 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[2], prevNod[1],
3456 nextNod[0], nextNod[2], nextNod[1],
3457 prevNod[5], prevNod[4], prevNod[3],
3458 nextNod[5], nextNod[4], nextNod[3],
3459 midlNod[0], midlNod[2], midlNod[1]);
3461 else { // not reversed case
3462 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3463 nextNod[0], nextNod[1], nextNod[2],
3464 prevNod[3], prevNod[4], prevNod[5],
3465 nextNod[3], nextNod[4], nextNod[5],
3466 midlNod[0], midlNod[1], midlNod[2]);
3469 else if(nbSame==1) {
3470 // 2d order pyramid of 13 nodes
3471 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5,
3472 // int n12,int n23,int n34,int n41,
3473 // int n15,int n25,int n35,int n45, int ID);
3475 int n1,n4,n41,n15,n45;
3476 if(i0>0) { // reversed case
3477 n1 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3478 n4 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3484 n1 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3485 n4 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3490 aNewElem = aMesh->AddVolume(prevNod[n1], nextNod[n1],
3491 nextNod[n4], prevNod[n4], prevNod[n5],
3492 midlNod[n1], nextNod[n41],
3493 midlNod[n4], prevNod[n41],
3494 prevNod[n15], nextNod[n15],
3495 nextNod[n45], prevNod[n45]);
3497 else if(nbSame==2) {
3498 // 2d order tetrahedron of 10 nodes
3499 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4,
3500 // int n12,int n23,int n31,
3501 // int n14,int n24,int n34, int ID);
3502 int n1 = iNotSameNode;
3503 int n2,n3,n12,n23,n31;
3504 if(i0>0) { // reversed case
3505 n2 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3506 n3 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3512 n2 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3513 n3 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3518 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3519 prevNod[n12], prevNod[n23], prevNod[n31],
3520 midlNod[n1], nextNod[n12], nextNod[n31]);
3524 case 8: { // quadratic quadrangle
3526 // create hexahedron with 20 nodes
3527 if(i0>0) { // reversed case
3528 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[3], prevNod[2], prevNod[1],
3529 nextNod[0], nextNod[3], nextNod[2], nextNod[1],
3530 prevNod[7], prevNod[6], prevNod[5], prevNod[4],
3531 nextNod[7], nextNod[6], nextNod[5], nextNod[4],
3532 midlNod[0], midlNod[3], midlNod[2], midlNod[1]);
3534 else { // not reversed case
3535 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3536 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3537 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3538 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3539 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3542 else if(nbSame==1) {
3543 // --- pyramid + pentahedron - can not be created since it is needed
3544 // additional middle node ot the center of face
3545 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3548 else if(nbSame==2) {
3549 // 2d order Pentahedron with 15 nodes
3550 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6,
3551 // int n12,int n23,int n31,int n45,int n56,int n64,
3552 // int n14,int n25,int n36, int ID);
3554 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3555 // iBeforeSame is same too
3562 // iAfterSame is same too
3568 int n12,n45,n14,n25;
3569 if(i0>0) { //reversed case
3581 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3582 prevNod[n4], prevNod[n5], nextNod[n5],
3583 prevNod[n12], midlNod[n2], nextNod[n12],
3584 prevNod[n45], midlNod[n5], nextNod[n45],
3585 prevNod[n14], prevNod[n25], nextNod[n25]);
3590 // realized for extrusion only
3591 //vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3592 //vector<int> quantities (nbNodes + 2);
3594 //quantities[0] = nbNodes; // bottom of prism
3595 //for (int inode = 0; inode < nbNodes; inode++) {
3596 // polyedre_nodes[inode] = prevNod[inode];
3599 //quantities[1] = nbNodes; // top of prism
3600 //for (int inode = 0; inode < nbNodes; inode++) {
3601 // polyedre_nodes[nbNodes + inode] = nextNod[inode];
3604 //for (int iface = 0; iface < nbNodes; iface++) {
3605 // quantities[iface + 2] = 4;
3606 // int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3607 // polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3608 // polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3609 // polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3610 // polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3612 //aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3619 // realized for extrusion only
3620 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3621 vector<int> quantities (nbNodes + 2);
3623 quantities[0] = nbNodes; // bottom of prism
3624 for (int inode = 0; inode < nbNodes; inode++) {
3625 polyedre_nodes[inode] = prevNod[inode];
3628 quantities[1] = nbNodes; // top of prism
3629 for (int inode = 0; inode < nbNodes; inode++) {
3630 polyedre_nodes[nbNodes + inode] = nextNod[inode];
3633 for (int iface = 0; iface < nbNodes; iface++) {
3634 quantities[iface + 2] = 4;
3635 int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3636 polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3637 polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3638 polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3639 polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3641 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3645 newElems.push_back( aNewElem );
3646 myLastCreatedElems.Append(aNewElem);
3647 srcElements.Append( elem );
3650 // set new prev nodes
3651 for ( iNode = 0; iNode < nbNodes; iNode++ )
3652 prevNod[ iNode ] = nextNod[ iNode ];
3657 //=======================================================================
3659 * \brief Create 1D and 2D elements around swept elements
3660 * \param mapNewNodes - source nodes and ones generated from them
3661 * \param newElemsMap - source elements and ones generated from them
3662 * \param elemNewNodesMap - nodes generated from each node of each element
3663 * \param elemSet - all swept elements
3664 * \param nbSteps - number of sweeping steps
3665 * \param srcElements - to append elem for each generated element
3667 //=======================================================================
3669 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3670 TElemOfElemListMap & newElemsMap,
3671 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3672 TIDSortedElemSet& elemSet,
3674 SMESH_SequenceOfElemPtr& srcElements)
3676 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3677 SMESHDS_Mesh* aMesh = GetMeshDS();
3679 // Find nodes belonging to only one initial element - sweep them to get edges.
3681 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3682 for ( ; nList != mapNewNodes.end(); nList++ ) {
3683 const SMDS_MeshNode* node =
3684 static_cast<const SMDS_MeshNode*>( nList->first );
3685 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3686 int nbInitElems = 0;
3687 const SMDS_MeshElement* el = 0;
3688 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3689 while ( eIt->more() && nbInitElems < 2 ) {
3691 SMDSAbs_ElementType type = el->GetType();
3692 if ( type == SMDSAbs_Volume || type < highType ) continue;
3693 if ( type > highType ) {
3697 if ( elemSet.find(el) != elemSet.end() )
3700 if ( nbInitElems < 2 ) {
3701 bool NotCreateEdge = el && el->IsQuadratic() && el->IsMediumNode(node);
3702 if(!NotCreateEdge) {
3703 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3704 list<const SMDS_MeshElement*> newEdges;
3705 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3710 // Make a ceiling for each element ie an equal element of last new nodes.
3711 // Find free links of faces - make edges and sweep them into faces.
3713 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3714 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3715 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) {
3716 const SMDS_MeshElement* elem = itElem->first;
3717 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3719 if ( elem->GetType() == SMDSAbs_Edge ) {
3720 // create a ceiling edge
3721 if (!elem->IsQuadratic()) {
3722 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3723 vecNewNodes[ 1 ]->second.back())) {
3724 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3725 vecNewNodes[ 1 ]->second.back()));
3726 srcElements.Append( myLastCreatedElems.Last() );
3730 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3731 vecNewNodes[ 1 ]->second.back(),
3732 vecNewNodes[ 2 ]->second.back())) {
3733 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3734 vecNewNodes[ 1 ]->second.back(),
3735 vecNewNodes[ 2 ]->second.back()));
3736 srcElements.Append( myLastCreatedElems.Last() );
3740 if ( elem->GetType() != SMDSAbs_Face )
3743 if(itElem->second.size()==0) continue;
3745 bool hasFreeLinks = false;
3747 TIDSortedElemSet avoidSet;
3748 avoidSet.insert( elem );
3750 set<const SMDS_MeshNode*> aFaceLastNodes;
3751 int iNode, nbNodes = vecNewNodes.size();
3752 if(!elem->IsQuadratic()) {
3753 // loop on the face nodes
3754 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3755 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3756 // look for free links of the face
3757 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
3758 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3759 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3760 // check if a link is free
3761 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3762 hasFreeLinks = true;
3763 // make an edge and a ceiling for a new edge
3764 if ( !aMesh->FindEdge( n1, n2 )) {
3765 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
3766 srcElements.Append( myLastCreatedElems.Last() );
3768 n1 = vecNewNodes[ iNode ]->second.back();
3769 n2 = vecNewNodes[ iNext ]->second.back();
3770 if ( !aMesh->FindEdge( n1, n2 )) {
3771 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
3772 srcElements.Append( myLastCreatedElems.Last() );
3777 else { // elem is quadratic face
3778 int nbn = nbNodes/2;
3779 for ( iNode = 0; iNode < nbn; iNode++ ) {
3780 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3781 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
3782 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3783 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3784 // check if a link is free
3785 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3786 hasFreeLinks = true;
3787 // make an edge and a ceiling for a new edge
3789 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
3790 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3791 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
3792 srcElements.Append( myLastCreatedElems.Last() );
3794 n1 = vecNewNodes[ iNode ]->second.back();
3795 n2 = vecNewNodes[ iNext ]->second.back();
3796 n3 = vecNewNodes[ iNode+nbn ]->second.back();
3797 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3798 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
3799 srcElements.Append( myLastCreatedElems.Last() );
3803 for ( iNode = nbn; iNode < 2*nbn; iNode++ ) {
3804 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3808 // sweep free links into faces
3810 if ( hasFreeLinks ) {
3811 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
3812 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
3814 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
3815 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3816 initNodeSet.insert( vecNewNodes[ iNode ]->first );
3817 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
3819 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
3820 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
3822 while ( iVol++ < volNb ) v++;
3823 // find indices of free faces of a volume and their source edges
3824 list< int > freeInd;
3825 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
3826 SMDS_VolumeTool vTool( *v );
3827 int iF, nbF = vTool.NbFaces();
3828 for ( iF = 0; iF < nbF; iF ++ ) {
3829 if (vTool.IsFreeFace( iF ) &&
3830 vTool.GetFaceNodes( iF, faceNodeSet ) &&
3831 initNodeSet != faceNodeSet) // except an initial face
3833 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
3835 freeInd.push_back( iF );
3836 // find source edge of a free face iF
3837 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
3838 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
3839 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
3840 initNodeSet.begin(), initNodeSet.end(),
3841 commonNodes.begin());
3842 if ( (*v)->IsQuadratic() )
3843 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
3845 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
3847 if ( !srcEdges.back() )
3849 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
3850 << iF << " of volume #" << vTool.ID() << endl;
3855 if ( freeInd.empty() )
3858 // create faces for all steps;
3859 // if such a face has been already created by sweep of edge,
3860 // assure that its orientation is OK
3861 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
3863 vTool.SetExternalNormal();
3864 list< int >::iterator ind = freeInd.begin();
3865 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
3866 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
3868 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
3869 int nbn = vTool.NbFaceNodes( *ind );
3871 case 3: { ///// triangle
3872 const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
3874 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
3875 else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 ))
3876 aMesh->ChangeElementNodes( f, nodes, nbn );
3879 case 4: { ///// quadrangle
3880 const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
3882 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ));
3883 else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 ))
3884 aMesh->ChangeElementNodes( f, nodes, nbn );
3888 if( (*v)->IsQuadratic() ) {
3889 if(nbn==6) { /////// quadratic triangle
3890 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4],
3891 nodes[1], nodes[3], nodes[5] );
3893 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
3894 nodes[1], nodes[3], nodes[5]));
3896 else if ( nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) {
3897 const SMDS_MeshNode** tmpnodes = new const SMDS_MeshNode*[6];
3898 tmpnodes[0] = nodes[0];
3899 tmpnodes[1] = nodes[2];
3900 tmpnodes[2] = nodes[4];
3901 tmpnodes[3] = nodes[1];
3902 tmpnodes[4] = nodes[3];
3903 tmpnodes[5] = nodes[5];
3904 aMesh->ChangeElementNodes( f, tmpnodes, nbn );
3907 else { /////// quadratic quadrangle
3908 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
3909 nodes[1], nodes[3], nodes[5], nodes[7] );
3911 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
3912 nodes[1], nodes[3], nodes[5], nodes[7]));
3914 else if ( nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) {
3915 const SMDS_MeshNode** tmpnodes = new const SMDS_MeshNode*[8];
3916 tmpnodes[0] = nodes[0];
3917 tmpnodes[1] = nodes[2];
3918 tmpnodes[2] = nodes[4];
3919 tmpnodes[3] = nodes[6];
3920 tmpnodes[4] = nodes[1];
3921 tmpnodes[5] = nodes[3];
3922 tmpnodes[6] = nodes[5];
3923 tmpnodes[7] = nodes[7];
3924 aMesh->ChangeElementNodes( f, tmpnodes, nbn );
3928 else { //////// polygon
3929 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
3930 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
3932 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
3933 else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 ))
3934 aMesh->ChangeElementNodes( f, nodes, nbn );
3937 while ( srcElements.Length() < myLastCreatedElems.Length() )
3938 srcElements.Append( *srcEdge );
3940 } // loop on free faces
3942 // go to the next volume
3944 while ( iVol++ < nbVolumesByStep ) v++;
3947 } // sweep free links into faces
3949 // Make a ceiling face with a normal external to a volume
3951 SMDS_VolumeTool lastVol( itElem->second.back() );
3953 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
3955 lastVol.SetExternalNormal();
3956 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
3957 int nbn = lastVol.NbFaceNodes( iF );
3960 if (!hasFreeLinks ||
3961 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
3962 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
3965 if (!hasFreeLinks ||
3966 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
3967 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ));
3970 if(itElem->second.back()->IsQuadratic()) {
3972 if (!hasFreeLinks ||
3973 !aMesh->FindFace(nodes[0], nodes[2], nodes[4],
3974 nodes[1], nodes[3], nodes[5]) ) {
3975 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
3976 nodes[1], nodes[3], nodes[5]));
3980 if (!hasFreeLinks ||
3981 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
3982 nodes[1], nodes[3], nodes[5], nodes[7]) )
3983 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
3984 nodes[1], nodes[3], nodes[5], nodes[7]));
3988 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
3989 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
3990 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
3994 while ( srcElements.Length() < myLastCreatedElems.Length() )
3995 srcElements.Append( myLastCreatedElems.Last() );
3997 } // loop on swept elements
4000 //=======================================================================
4001 //function : RotationSweep
4003 //=======================================================================
4005 SMESH_MeshEditor::PGroupIDs
4006 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4007 const gp_Ax1& theAxis,
4008 const double theAngle,
4009 const int theNbSteps,
4010 const double theTol,
4011 const bool theMakeGroups,
4012 const bool theMakeWalls)
4014 myLastCreatedElems.Clear();
4015 myLastCreatedNodes.Clear();
4017 // source elements for each generated one
4018 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4020 MESSAGE( "RotationSweep()");
4022 aTrsf.SetRotation( theAxis, theAngle );
4024 aTrsf2.SetRotation( theAxis, theAngle/2. );
4026 gp_Lin aLine( theAxis );
4027 double aSqTol = theTol * theTol;
4029 SMESHDS_Mesh* aMesh = GetMeshDS();
4031 TNodeOfNodeListMap mapNewNodes;
4032 TElemOfVecOfNnlmiMap mapElemNewNodes;
4033 TElemOfElemListMap newElemsMap;
4036 TIDSortedElemSet::iterator itElem;
4037 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4038 const SMDS_MeshElement* elem = *itElem;
4039 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4041 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4042 newNodesItVec.reserve( elem->NbNodes() );
4044 // loop on elem nodes
4045 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4046 while ( itN->more() ) {
4047 // check if a node has been already sweeped
4048 const SMDS_MeshNode* node = cast2Node( itN->next() );
4050 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4052 aXYZ.Coord( coord[0], coord[1], coord[2] );
4053 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4055 TNodeOfNodeListMapItr nIt = mapNewNodes.find( node );
4056 if ( nIt == mapNewNodes.end() ) {
4057 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4058 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4061 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4063 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4064 //bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4065 const SMDS_MeshNode * newNode = node;
4066 for ( int i = 0; i < theNbSteps; i++ ) {
4068 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4070 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4071 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4072 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4073 myLastCreatedNodes.Append(newNode);
4074 srcNodes.Append( node );
4075 listNewNodes.push_back( newNode );
4076 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4077 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4080 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4082 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4083 myLastCreatedNodes.Append(newNode);
4084 srcNodes.Append( node );
4085 listNewNodes.push_back( newNode );
4088 listNewNodes.push_back( newNode );
4089 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4090 listNewNodes.push_back( newNode );
4097 // if current elem is quadratic and current node is not medium
4098 // we have to check - may be it is needed to insert additional nodes
4099 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4100 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4101 if(listNewNodes.size()==theNbSteps) {
4102 listNewNodes.clear();
4104 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4106 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4107 const SMDS_MeshNode * newNode = node;
4109 for(int i = 0; i<theNbSteps; i++) {
4110 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4111 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4112 cout<<" 3 AddNode: "<<newNode;
4113 myLastCreatedNodes.Append(newNode);
4114 listNewNodes.push_back( newNode );
4115 srcNodes.Append( node );
4116 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4117 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4118 cout<<" 4 AddNode: "<<newNode;
4119 myLastCreatedNodes.Append(newNode);
4120 srcNodes.Append( node );
4121 listNewNodes.push_back( newNode );
4125 listNewNodes.push_back( newNode );
4131 newNodesItVec.push_back( nIt );
4133 // make new elements
4134 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4138 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4140 PGroupIDs newGroupIDs;
4141 if ( theMakeGroups )
4142 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4148 //=======================================================================
4149 //function : CreateNode
4151 //=======================================================================
4152 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4155 const double tolnode,
4156 SMESH_SequenceOfNode& aNodes)
4158 myLastCreatedElems.Clear();
4159 myLastCreatedNodes.Clear();
4162 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4164 // try to search in sequence of existing nodes
4165 // if aNodes.Length()>0 we 'nave to use given sequence
4166 // else - use all nodes of mesh
4167 if(aNodes.Length()>0) {
4169 for(i=1; i<=aNodes.Length(); i++) {
4170 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4171 if(P1.Distance(P2)<tolnode)
4172 return aNodes.Value(i);
4176 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4177 while(itn->more()) {
4178 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4179 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4180 if(P1.Distance(P2)<tolnode)
4185 // create new node and return it
4186 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4187 myLastCreatedNodes.Append(NewNode);
4192 //=======================================================================
4193 //function : ExtrusionSweep
4195 //=======================================================================
4197 SMESH_MeshEditor::PGroupIDs
4198 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4199 const gp_Vec& theStep,
4200 const int theNbSteps,
4201 TElemOfElemListMap& newElemsMap,
4202 const bool theMakeGroups,
4204 const double theTolerance)
4206 ExtrusParam aParams;
4207 aParams.myDir = gp_Dir(theStep);
4208 aParams.myNodes.Clear();
4209 aParams.mySteps = new TColStd_HSequenceOfReal;
4211 for(i=1; i<=theNbSteps; i++)
4212 aParams.mySteps->Append(theStep.Magnitude());
4215 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4219 //=======================================================================
4220 //function : ExtrusionSweep
4222 //=======================================================================
4224 SMESH_MeshEditor::PGroupIDs
4225 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4226 ExtrusParam& theParams,
4227 TElemOfElemListMap& newElemsMap,
4228 const bool theMakeGroups,
4230 const double theTolerance)
4232 myLastCreatedElems.Clear();
4233 myLastCreatedNodes.Clear();
4235 // source elements for each generated one
4236 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4238 SMESHDS_Mesh* aMesh = GetMeshDS();
4240 int nbsteps = theParams.mySteps->Length();
4242 TNodeOfNodeListMap mapNewNodes;
4243 //TNodeOfNodeVecMap mapNewNodes;
4244 TElemOfVecOfNnlmiMap mapElemNewNodes;
4245 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4248 TIDSortedElemSet::iterator itElem;
4249 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4250 // check element type
4251 const SMDS_MeshElement* elem = *itElem;
4252 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4255 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4256 //vector<TNodeOfNodeVecMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4257 newNodesItVec.reserve( elem->NbNodes() );
4259 // loop on elem nodes
4260 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4261 while ( itN->more() )
4263 // check if a node has been already sweeped
4264 const SMDS_MeshNode* node = cast2Node( itN->next() );
4265 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
4266 //TNodeOfNodeVecMap::iterator nIt = mapNewNodes.find( node );
4267 if ( nIt == mapNewNodes.end() ) {
4268 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4269 //nIt = mapNewNodes.insert( make_pair( node, vector<const SMDS_MeshNode*>() )).first;
4270 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4271 //vector<const SMDS_MeshNode*>& vecNewNodes = nIt->second;
4272 //vecNewNodes.reserve(nbsteps);
4275 double coord[] = { node->X(), node->Y(), node->Z() };
4276 //int nbsteps = theParams.mySteps->Length();
4277 for ( int i = 0; i < nbsteps; i++ ) {
4278 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4279 // create additional node
4280 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4281 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4282 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4283 if( theFlags & EXTRUSION_FLAG_SEW ) {
4284 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4285 theTolerance, theParams.myNodes);
4286 listNewNodes.push_back( newNode );
4289 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4290 myLastCreatedNodes.Append(newNode);
4291 srcNodes.Append( node );
4292 listNewNodes.push_back( newNode );
4295 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4296 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4297 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4298 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4299 if( theFlags & EXTRUSION_FLAG_SEW ) {
4300 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4301 theTolerance, theParams.myNodes);
4302 listNewNodes.push_back( newNode );
4303 //vecNewNodes[i]=newNode;
4306 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4307 myLastCreatedNodes.Append(newNode);
4308 srcNodes.Append( node );
4309 listNewNodes.push_back( newNode );
4310 //vecNewNodes[i]=newNode;
4315 // if current elem is quadratic and current node is not medium
4316 // we have to check - may be it is needed to insert additional nodes
4317 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4318 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4319 if(listNewNodes.size()==nbsteps) {
4320 listNewNodes.clear();
4321 double coord[] = { node->X(), node->Y(), node->Z() };
4322 for ( int i = 0; i < nbsteps; i++ ) {
4323 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4324 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4325 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4326 if( theFlags & EXTRUSION_FLAG_SEW ) {
4327 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4328 theTolerance, theParams.myNodes);
4329 listNewNodes.push_back( newNode );
4332 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4333 myLastCreatedNodes.Append(newNode);
4334 srcNodes.Append( node );
4335 listNewNodes.push_back( newNode );
4337 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4338 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4339 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4340 if( theFlags & EXTRUSION_FLAG_SEW ) {
4341 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4342 theTolerance, theParams.myNodes);
4343 listNewNodes.push_back( newNode );
4346 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4347 myLastCreatedNodes.Append(newNode);
4348 srcNodes.Append( node );
4349 listNewNodes.push_back( newNode );
4355 newNodesItVec.push_back( nIt );
4357 // make new elements
4358 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4361 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4362 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4364 PGroupIDs newGroupIDs;
4365 if ( theMakeGroups )
4366 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4372 //=======================================================================
4373 //class : SMESH_MeshEditor_PathPoint
4374 //purpose : auxiliary class
4375 //=======================================================================
4376 class SMESH_MeshEditor_PathPoint {
4378 SMESH_MeshEditor_PathPoint() {
4379 myPnt.SetCoord(99., 99., 99.);
4380 myTgt.SetCoord(1.,0.,0.);
4384 void SetPnt(const gp_Pnt& aP3D){
4387 void SetTangent(const gp_Dir& aTgt){
4390 void SetAngle(const double& aBeta){
4393 void SetParameter(const double& aPrm){
4396 const gp_Pnt& Pnt()const{
4399 const gp_Dir& Tangent()const{
4402 double Angle()const{
4405 double Parameter()const{
4417 //=======================================================================
4418 //function : ExtrusionAlongTrack
4420 //=======================================================================
4421 SMESH_MeshEditor::Extrusion_Error
4422 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4423 SMESH_subMesh* theTrack,
4424 const SMDS_MeshNode* theN1,
4425 const bool theHasAngles,
4426 list<double>& theAngles,
4427 const bool theLinearVariation,
4428 const bool theHasRefPoint,
4429 const gp_Pnt& theRefPoint,
4430 const bool theMakeGroups)
4432 myLastCreatedElems.Clear();
4433 myLastCreatedNodes.Clear();
4436 std::list<double> aPrms;
4437 TIDSortedElemSet::iterator itElem;
4440 TopoDS_Edge aTrackEdge;
4441 TopoDS_Vertex aV1, aV2;
4443 SMDS_ElemIteratorPtr aItE;
4444 SMDS_NodeIteratorPtr aItN;
4445 SMDSAbs_ElementType aTypeE;
4447 TNodeOfNodeListMap mapNewNodes;
4450 aNbE = theElements.size();
4453 return EXTR_NO_ELEMENTS;
4455 // 1.1 Track Pattern
4458 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4460 aItE = pSubMeshDS->GetElements();
4461 while ( aItE->more() ) {
4462 const SMDS_MeshElement* pE = aItE->next();
4463 aTypeE = pE->GetType();
4464 // Pattern must contain links only
4465 if ( aTypeE != SMDSAbs_Edge )
4466 return EXTR_PATH_NOT_EDGE;
4469 list<SMESH_MeshEditor_PathPoint> fullList;
4471 const TopoDS_Shape& aS = theTrack->GetSubShape();
4472 // Sub shape for the Pattern must be an Edge or Wire
4473 if( aS.ShapeType() == TopAbs_EDGE ) {
4474 aTrackEdge = TopoDS::Edge( aS );
4475 // the Edge must not be degenerated
4476 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4477 return EXTR_BAD_PATH_SHAPE;
4478 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4479 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4480 const SMDS_MeshNode* aN1 = aItN->next();
4481 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4482 const SMDS_MeshNode* aN2 = aItN->next();
4483 // starting node must be aN1 or aN2
4484 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4485 return EXTR_BAD_STARTING_NODE;
4486 aItN = pSubMeshDS->GetNodes();
4487 while ( aItN->more() ) {
4488 const SMDS_MeshNode* pNode = aItN->next();
4489 const SMDS_EdgePosition* pEPos =
4490 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4491 double aT = pEPos->GetUParameter();
4492 aPrms.push_back( aT );
4494 //Extrusion_Error err =
4495 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4497 else if( aS.ShapeType() == TopAbs_WIRE ) {
4498 list< SMESH_subMesh* > LSM;
4499 TopTools_SequenceOfShape Edges;
4500 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4501 while(itSM->more()) {
4502 SMESH_subMesh* SM = itSM->next();
4504 const TopoDS_Shape& aS = SM->GetSubShape();
4507 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4508 int startNid = theN1->GetID();
4509 TColStd_MapOfInteger UsedNums;
4510 int NbEdges = Edges.Length();
4512 for(; i<=NbEdges; i++) {
4514 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4515 for(; itLSM!=LSM.end(); itLSM++) {
4517 if(UsedNums.Contains(k)) continue;
4518 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4519 SMESH_subMesh* locTrack = *itLSM;
4520 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4521 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4522 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4523 const SMDS_MeshNode* aN1 = aItN->next();
4524 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4525 const SMDS_MeshNode* aN2 = aItN->next();
4526 // starting node must be aN1 or aN2
4527 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4528 // 2. Collect parameters on the track edge
4530 aItN = locMeshDS->GetNodes();
4531 while ( aItN->more() ) {
4532 const SMDS_MeshNode* pNode = aItN->next();
4533 const SMDS_EdgePosition* pEPos =
4534 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4535 double aT = pEPos->GetUParameter();
4536 aPrms.push_back( aT );
4538 list<SMESH_MeshEditor_PathPoint> LPP;
4539 //Extrusion_Error err =
4540 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4541 LLPPs.push_back(LPP);
4543 // update startN for search following egde
4544 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4545 else startNid = aN1->GetID();
4549 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4550 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4551 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4552 for(; itPP!=firstList.end(); itPP++) {
4553 fullList.push_back( *itPP );
4555 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4556 fullList.pop_back();
4558 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4559 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4560 itPP = currList.begin();
4561 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4562 gp_Dir D1 = PP1.Tangent();
4563 gp_Dir D2 = PP2.Tangent();
4564 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4565 (D1.Z()+D2.Z())/2 ) );
4566 PP1.SetTangent(Dnew);
4567 fullList.push_back(PP1);
4569 for(; itPP!=firstList.end(); itPP++) {
4570 fullList.push_back( *itPP );
4572 PP1 = fullList.back();
4573 fullList.pop_back();
4575 // if wire not closed
4576 fullList.push_back(PP1);
4580 return EXTR_BAD_PATH_SHAPE;
4583 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4584 theHasRefPoint, theRefPoint, theMakeGroups);
4588 //=======================================================================
4589 //function : ExtrusionAlongTrack
4591 //=======================================================================
4592 SMESH_MeshEditor::Extrusion_Error
4593 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4594 SMESH_Mesh* theTrack,
4595 const SMDS_MeshNode* theN1,
4596 const bool theHasAngles,
4597 list<double>& theAngles,
4598 const bool theLinearVariation,
4599 const bool theHasRefPoint,
4600 const gp_Pnt& theRefPoint,
4601 const bool theMakeGroups)
4603 myLastCreatedElems.Clear();
4604 myLastCreatedNodes.Clear();
4607 std::list<double> aPrms;
4608 TIDSortedElemSet::iterator itElem;
4611 TopoDS_Edge aTrackEdge;
4612 TopoDS_Vertex aV1, aV2;
4614 SMDS_ElemIteratorPtr aItE;
4615 SMDS_NodeIteratorPtr aItN;
4616 SMDSAbs_ElementType aTypeE;
4618 TNodeOfNodeListMap mapNewNodes;
4621 aNbE = theElements.size();
4624 return EXTR_NO_ELEMENTS;
4626 // 1.1 Track Pattern
4629 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4631 aItE = pMeshDS->elementsIterator();
4632 while ( aItE->more() ) {
4633 const SMDS_MeshElement* pE = aItE->next();
4634 aTypeE = pE->GetType();
4635 // Pattern must contain links only
4636 if ( aTypeE != SMDSAbs_Edge )
4637 return EXTR_PATH_NOT_EDGE;
4640 list<SMESH_MeshEditor_PathPoint> fullList;
4642 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4643 // Sub shape for the Pattern must be an Edge or Wire
4644 if( aS.ShapeType() == TopAbs_EDGE ) {
4645 aTrackEdge = TopoDS::Edge( aS );
4646 // the Edge must not be degenerated
4647 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4648 return EXTR_BAD_PATH_SHAPE;
4649 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4650 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4651 const SMDS_MeshNode* aN1 = aItN->next();
4652 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4653 const SMDS_MeshNode* aN2 = aItN->next();
4654 // starting node must be aN1 or aN2
4655 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4656 return EXTR_BAD_STARTING_NODE;
4657 aItN = pMeshDS->nodesIterator();
4658 while ( aItN->more() ) {
4659 const SMDS_MeshNode* pNode = aItN->next();
4660 if( pNode==aN1 || pNode==aN2 ) continue;
4661 const SMDS_EdgePosition* pEPos =
4662 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4663 double aT = pEPos->GetUParameter();
4664 aPrms.push_back( aT );
4666 //Extrusion_Error err =
4667 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4669 else if( aS.ShapeType() == TopAbs_WIRE ) {
4670 list< SMESH_subMesh* > LSM;
4671 TopTools_SequenceOfShape Edges;
4672 TopExp_Explorer eExp(aS, TopAbs_EDGE);
4673 for(; eExp.More(); eExp.Next()) {
4674 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
4675 if( BRep_Tool::Degenerated(E) ) continue;
4676 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
4682 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4683 int startNid = theN1->GetID();
4684 TColStd_MapOfInteger UsedNums;
4685 int NbEdges = Edges.Length();
4687 for(; i<=NbEdges; i++) {
4689 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4690 for(; itLSM!=LSM.end(); itLSM++) {
4692 if(UsedNums.Contains(k)) continue;
4693 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4694 SMESH_subMesh* locTrack = *itLSM;
4695 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4696 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4697 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4698 const SMDS_MeshNode* aN1 = aItN->next();
4699 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4700 const SMDS_MeshNode* aN2 = aItN->next();
4701 // starting node must be aN1 or aN2
4702 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4703 // 2. Collect parameters on the track edge
4705 aItN = locMeshDS->GetNodes();
4706 while ( aItN->more() ) {
4707 const SMDS_MeshNode* pNode = aItN->next();
4708 const SMDS_EdgePosition* pEPos =
4709 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4710 double aT = pEPos->GetUParameter();
4711 aPrms.push_back( aT );
4713 list<SMESH_MeshEditor_PathPoint> LPP;
4714 //Extrusion_Error err =
4715 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4716 LLPPs.push_back(LPP);
4718 // update startN for search following egde
4719 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4720 else startNid = aN1->GetID();
4724 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4725 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4726 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4727 for(; itPP!=firstList.end(); itPP++) {
4728 fullList.push_back( *itPP );
4730 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4731 fullList.pop_back();
4733 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4734 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4735 itPP = currList.begin();
4736 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4737 gp_Pnt P1 = PP1.Pnt();
4738 //cout<<" PP1: Pnt("<<P1.X()<<","<<P1.Y()<<","<<P1.Z()<<")"<<endl;
4739 gp_Pnt P2 = PP2.Pnt();
4740 gp_Dir D1 = PP1.Tangent();
4741 gp_Dir D2 = PP2.Tangent();
4742 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4743 (D1.Z()+D2.Z())/2 ) );
4744 PP1.SetTangent(Dnew);
4745 fullList.push_back(PP1);
4747 for(; itPP!=currList.end(); itPP++) {
4748 fullList.push_back( *itPP );
4750 PP1 = fullList.back();
4751 fullList.pop_back();
4753 // if wire not closed
4754 fullList.push_back(PP1);
4758 return EXTR_BAD_PATH_SHAPE;
4761 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4762 theHasRefPoint, theRefPoint, theMakeGroups);
4766 //=======================================================================
4767 //function : MakeEdgePathPoints
4768 //purpose : auxilary for ExtrusionAlongTrack
4769 //=======================================================================
4770 SMESH_MeshEditor::Extrusion_Error
4771 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
4772 const TopoDS_Edge& aTrackEdge,
4774 list<SMESH_MeshEditor_PathPoint>& LPP)
4776 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
4778 aTolVec2=aTolVec*aTolVec;
4780 TopoDS_Vertex aV1, aV2;
4781 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4782 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
4783 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
4784 // 2. Collect parameters on the track edge
4785 aPrms.push_front( aT1 );
4786 aPrms.push_back( aT2 );
4789 if( FirstIsStart ) {
4800 SMESH_MeshEditor_PathPoint aPP;
4801 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
4802 std::list<double>::iterator aItD = aPrms.begin();
4803 for(; aItD != aPrms.end(); ++aItD) {
4807 aC3D->D1( aT, aP3D, aVec );
4808 aL2 = aVec.SquareMagnitude();
4809 if ( aL2 < aTolVec2 )
4810 return EXTR_CANT_GET_TANGENT;
4811 gp_Dir aTgt( aVec );
4813 aPP.SetTangent( aTgt );
4814 aPP.SetParameter( aT );
4821 //=======================================================================
4822 //function : MakeExtrElements
4823 //purpose : auxilary for ExtrusionAlongTrack
4824 //=======================================================================
4825 SMESH_MeshEditor::Extrusion_Error
4826 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
4827 list<SMESH_MeshEditor_PathPoint>& fullList,
4828 const bool theHasAngles,
4829 list<double>& theAngles,
4830 const bool theLinearVariation,
4831 const bool theHasRefPoint,
4832 const gp_Pnt& theRefPoint,
4833 const bool theMakeGroups)
4835 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
4836 int aNbTP = fullList.size();
4837 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
4839 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
4840 LinearAngleVariation(aNbTP-1, theAngles);
4842 vector<double> aAngles( aNbTP );
4844 for(; j<aNbTP; ++j) {
4847 if ( theHasAngles ) {
4849 std::list<double>::iterator aItD = theAngles.begin();
4850 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
4852 aAngles[j] = anAngle;
4855 // fill vector of path points with angles
4856 //aPPs.resize(fullList.size());
4858 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
4859 for(; itPP!=fullList.end(); itPP++) {
4861 SMESH_MeshEditor_PathPoint PP = *itPP;
4862 PP.SetAngle(aAngles[j]);
4866 TNodeOfNodeListMap mapNewNodes;
4867 TElemOfVecOfNnlmiMap mapElemNewNodes;
4868 TElemOfElemListMap newElemsMap;
4869 TIDSortedElemSet::iterator itElem;
4872 SMDSAbs_ElementType aTypeE;
4873 // source elements for each generated one
4874 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4876 // 3. Center of rotation aV0
4877 gp_Pnt aV0 = theRefPoint;
4879 if ( !theHasRefPoint ) {
4881 aGC.SetCoord( 0.,0.,0. );
4883 itElem = theElements.begin();
4884 for ( ; itElem != theElements.end(); itElem++ ) {
4885 const SMDS_MeshElement* elem = *itElem;
4887 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4888 while ( itN->more() ) {
4889 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
4894 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
4895 list<const SMDS_MeshNode*> aLNx;
4896 mapNewNodes[node] = aLNx;
4898 gp_XYZ aXYZ( aX, aY, aZ );
4906 } // if (!theHasRefPoint) {
4907 mapNewNodes.clear();
4909 // 4. Processing the elements
4910 SMESHDS_Mesh* aMesh = GetMeshDS();
4912 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
4913 // check element type
4914 const SMDS_MeshElement* elem = *itElem;
4915 aTypeE = elem->GetType();
4916 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
4919 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4920 newNodesItVec.reserve( elem->NbNodes() );
4922 // loop on elem nodes
4924 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4925 while ( itN->more() )
4928 // check if a node has been already processed
4929 const SMDS_MeshNode* node =
4930 static_cast<const SMDS_MeshNode*>( itN->next() );
4931 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
4932 if ( nIt == mapNewNodes.end() ) {
4933 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4934 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4937 aX = node->X(); aY = node->Y(); aZ = node->Z();
4939 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
4940 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
4941 gp_Ax1 anAx1, anAxT1T0;
4942 gp_Dir aDT1x, aDT0x, aDT1T0;
4947 aPN0.SetCoord(aX, aY, aZ);
4949 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
4951 aDT0x= aPP0.Tangent();
4952 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
4954 for ( j = 1; j < aNbTP; ++j ) {
4955 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
4957 aDT1x = aPP1.Tangent();
4958 aAngle1x = aPP1.Angle();
4960 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
4962 gp_Vec aV01x( aP0x, aP1x );
4963 aTrsf.SetTranslation( aV01x );
4966 aV1x = aV0x.Transformed( aTrsf );
4967 aPN1 = aPN0.Transformed( aTrsf );
4969 // rotation 1 [ T1,T0 ]
4970 aAngleT1T0=-aDT1x.Angle( aDT0x );
4971 if (fabs(aAngleT1T0) > aTolAng) {
4973 anAxT1T0.SetLocation( aV1x );
4974 anAxT1T0.SetDirection( aDT1T0 );
4975 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
4977 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
4981 if ( theHasAngles ) {
4982 anAx1.SetLocation( aV1x );
4983 anAx1.SetDirection( aDT1x );
4984 aTrsfRot.SetRotation( anAx1, aAngle1x );
4986 aPN1 = aPN1.Transformed( aTrsfRot );
4990 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4991 // create additional node
4992 double x = ( aPN1.X() + aPN0.X() )/2.;
4993 double y = ( aPN1.Y() + aPN0.Y() )/2.;
4994 double z = ( aPN1.Z() + aPN0.Z() )/2.;
4995 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
4996 myLastCreatedNodes.Append(newNode);
4997 srcNodes.Append( node );
4998 listNewNodes.push_back( newNode );
5003 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5004 myLastCreatedNodes.Append(newNode);
5005 srcNodes.Append( node );
5006 listNewNodes.push_back( newNode );
5016 // if current elem is quadratic and current node is not medium
5017 // we have to check - may be it is needed to insert additional nodes
5018 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5019 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5020 if(listNewNodes.size()==aNbTP-1) {
5021 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5022 gp_XYZ P(node->X(), node->Y(), node->Z());
5023 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5025 for(i=0; i<aNbTP-1; i++) {
5026 const SMDS_MeshNode* N = *it;
5027 double x = ( N->X() + P.X() )/2.;
5028 double y = ( N->Y() + P.Y() )/2.;
5029 double z = ( N->Z() + P.Z() )/2.;
5030 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5031 srcNodes.Append( node );
5032 myLastCreatedNodes.Append(newN);
5035 P = gp_XYZ(N->X(),N->Y(),N->Z());
5037 listNewNodes.clear();
5038 for(i=0; i<2*(aNbTP-1); i++) {
5039 listNewNodes.push_back(aNodes[i]);
5045 newNodesItVec.push_back( nIt );
5047 // make new elements
5048 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5049 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5050 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5053 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5055 if ( theMakeGroups )
5056 generateGroups( srcNodes, srcElems, "extruded");
5062 //=======================================================================
5063 //function : LinearAngleVariation
5064 //purpose : auxilary for ExtrusionAlongTrack
5065 //=======================================================================
5066 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5067 list<double>& Angles)
5069 int nbAngles = Angles.size();
5070 if( nbSteps > nbAngles ) {
5071 vector<double> theAngles(nbAngles);
5072 list<double>::iterator it = Angles.begin();
5074 for(; it!=Angles.end(); it++) {
5076 theAngles[i] = (*it);
5079 double rAn2St = double( nbAngles ) / double( nbSteps );
5080 double angPrev = 0, angle;
5081 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5082 double angCur = rAn2St * ( iSt+1 );
5083 double angCurFloor = floor( angCur );
5084 double angPrevFloor = floor( angPrev );
5085 if ( angPrevFloor == angCurFloor )
5086 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5088 int iP = int( angPrevFloor );
5089 double angPrevCeil = ceil(angPrev);
5090 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5092 int iC = int( angCurFloor );
5093 if ( iC < nbAngles )
5094 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5096 iP = int( angPrevCeil );
5098 angle += theAngles[ iC ];
5100 res.push_back(angle);
5105 for(; it!=res.end(); it++)
5106 Angles.push_back( *it );
5111 //================================================================================
5113 * \brief Move or copy theElements applying theTrsf to their nodes
5114 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5115 * \param theTrsf - transformation to apply
5116 * \param theCopy - if true, create translated copies of theElems
5117 * \param theMakeGroups - if true and theCopy, create translated groups
5118 * \param theTargetMesh - mesh to copy translated elements into
5119 * \retval SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5121 //================================================================================
5123 SMESH_MeshEditor::PGroupIDs
5124 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5125 const gp_Trsf& theTrsf,
5127 const bool theMakeGroups,
5128 SMESH_Mesh* theTargetMesh)
5130 myLastCreatedElems.Clear();
5131 myLastCreatedNodes.Clear();
5133 bool needReverse = false;
5134 string groupPostfix;
5135 switch ( theTrsf.Form() ) {
5140 groupPostfix = "mirrored";
5143 groupPostfix = "rotated";
5145 case gp_Translation:
5146 groupPostfix = "translated";
5149 case gp_CompoundTrsf: // different scale by axis
5150 groupPostfix = "scaled";
5153 needReverse = false;
5154 groupPostfix = "transformed";
5157 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5158 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5159 SMESHDS_Mesh* aMesh = GetMeshDS();
5162 // map old node to new one
5163 TNodeNodeMap nodeMap;
5165 // elements sharing moved nodes; those of them which have all
5166 // nodes mirrored but are not in theElems are to be reversed
5167 TIDSortedElemSet inverseElemSet;
5169 // source elements for each generated one
5170 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5172 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5173 list<SMDS_MeshNode> orphanCopy; // copies of orphan nodes
5174 vector<const SMDS_MeshNode*> orphanNode; // original orphan nodes
5176 if ( theElems.empty() ) // transform the whole mesh
5179 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5180 while ( eIt->more() ) theElems.insert( eIt->next() );
5182 SMDS_MeshElementIDFactory idFactory;
5183 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5184 while ( nIt->more() )
5186 const SMDS_MeshNode* node = nIt->next();
5187 if ( node->NbInverseElements() == 0 && !theElems.insert( node ).second )
5189 // node was not inserted into theElems because an element with the same ID
5190 // is already there. As a work around we insert a copy of node with
5191 // an ID = -<index in orphanNode>
5192 orphanCopy.push_back( *node ); // copy node
5193 SMDS_MeshNode* nodeCopy = &orphanCopy.back();
5194 int uniqueID = -orphanNode.size();
5195 orphanNode.push_back( node );
5196 idFactory.BindID( uniqueID, nodeCopy );
5197 theElems.insert( nodeCopy );
5201 // loop on theElems to transorm nodes
5202 TIDSortedElemSet::iterator itElem;
5203 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5204 const SMDS_MeshElement* elem = *itElem;
5208 // loop on elem nodes
5209 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5210 while ( itN->more() ) {
5212 const SMDS_MeshNode* node = cast2Node( itN->next() );
5213 if ( node->GetID() < 0 )
5214 node = orphanNode[ -node->GetID() ];
5215 // check if a node has been already transformed
5216 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5217 nodeMap.insert( make_pair ( node, node ));
5218 if ( !n2n_isnew.second )
5222 coord[0] = node->X();
5223 coord[1] = node->Y();
5224 coord[2] = node->Z();
5225 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5226 if ( theTargetMesh ) {
5227 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5228 n2n_isnew.first->second = newNode;
5229 myLastCreatedNodes.Append(newNode);
5230 srcNodes.Append( node );
5232 else if ( theCopy ) {
5233 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5234 n2n_isnew.first->second = newNode;
5235 myLastCreatedNodes.Append(newNode);
5236 srcNodes.Append( node );
5239 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5240 // node position on shape becomes invalid
5241 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5242 ( SMDS_SpacePosition::originSpacePosition() );
5245 // keep inverse elements
5246 if ( !theCopy && !theTargetMesh && needReverse ) {
5247 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5248 while ( invElemIt->more() ) {
5249 const SMDS_MeshElement* iel = invElemIt->next();
5250 inverseElemSet.insert( iel );
5256 // either create new elements or reverse mirrored ones
5257 if ( !theCopy && !needReverse && !theTargetMesh )
5260 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5261 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5262 theElems.insert( *invElemIt );
5264 // replicate or reverse elements
5267 REV_TETRA = 0, // = nbNodes - 4
5268 REV_PYRAMID = 1, // = nbNodes - 4
5269 REV_PENTA = 2, // = nbNodes - 4
5271 REV_HEXA = 4, // = nbNodes - 4
5275 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA
5276 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID
5277 { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA
5278 { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE
5279 { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA
5280 { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD
5283 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5285 const SMDS_MeshElement* elem = *itElem;
5286 if ( !elem || elem->GetType() == SMDSAbs_Node )
5289 int nbNodes = elem->NbNodes();
5290 int elemType = elem->GetType();
5292 if (elem->IsPoly()) {
5293 // Polygon or Polyhedral Volume
5294 switch ( elemType ) {
5297 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5299 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5300 while (itN->more()) {
5301 const SMDS_MeshNode* node =
5302 static_cast<const SMDS_MeshNode*>(itN->next());
5303 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5304 if (nodeMapIt == nodeMap.end())
5305 break; // not all nodes transformed
5307 // reverse mirrored faces and volumes
5308 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5310 poly_nodes[iNode] = (*nodeMapIt).second;
5314 if ( iNode != nbNodes )
5315 continue; // not all nodes transformed
5317 if ( theTargetMesh ) {
5318 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5319 srcElems.Append( elem );
5321 else if ( theCopy ) {
5322 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5323 srcElems.Append( elem );
5326 aMesh->ChangePolygonNodes(elem, poly_nodes);
5330 case SMDSAbs_Volume:
5332 // ATTENTION: Reversing is not yet done!!!
5333 const SMDS_PolyhedralVolumeOfNodes* aPolyedre =
5334 dynamic_cast<const SMDS_PolyhedralVolumeOfNodes*>( elem );
5336 MESSAGE("Warning: bad volumic element");
5340 vector<const SMDS_MeshNode*> poly_nodes;
5341 vector<int> quantities;
5343 bool allTransformed = true;
5344 int nbFaces = aPolyedre->NbFaces();
5345 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5346 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5347 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5348 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5349 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5350 if (nodeMapIt == nodeMap.end()) {
5351 allTransformed = false; // not all nodes transformed
5353 poly_nodes.push_back((*nodeMapIt).second);
5356 quantities.push_back(nbFaceNodes);
5358 if ( !allTransformed )
5359 continue; // not all nodes transformed
5361 if ( theTargetMesh ) {
5362 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5363 srcElems.Append( elem );
5365 else if ( theCopy ) {
5366 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5367 srcElems.Append( elem );
5370 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5380 int* i = index[ FORWARD ];
5381 if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes
5382 if ( elemType == SMDSAbs_Face )
5383 i = index[ REV_FACE ];
5385 i = index[ nbNodes - 4 ];
5387 if(elem->IsQuadratic()) {
5388 static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
5391 if(nbNodes==3) { // quadratic edge
5392 static int anIds[] = {1,0,2};
5395 else if(nbNodes==6) { // quadratic triangle
5396 static int anIds[] = {0,2,1,5,4,3};
5399 else if(nbNodes==8) { // quadratic quadrangle
5400 static int anIds[] = {0,3,2,1,7,6,5,4};
5403 else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes
5404 static int anIds[] = {0,2,1,3,6,5,4,7,9,8};
5407 else if(nbNodes==13) { // quadratic pyramid of 13 nodes
5408 static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10};
5411 else if(nbNodes==15) { // quadratic pentahedron with 15 nodes
5412 static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13};
5415 else { // nbNodes==20 - quadratic hexahedron with 20 nodes
5416 static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17};
5422 // find transformed nodes
5423 vector<const SMDS_MeshNode*> nodes(nbNodes);
5425 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5426 while ( itN->more() ) {
5427 const SMDS_MeshNode* node =
5428 static_cast<const SMDS_MeshNode*>( itN->next() );
5429 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5430 if ( nodeMapIt == nodeMap.end() )
5431 break; // not all nodes transformed
5432 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5434 if ( iNode != nbNodes )
5435 continue; // not all nodes transformed
5437 if ( theTargetMesh ) {
5438 if ( SMDS_MeshElement* copy =
5439 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5440 myLastCreatedElems.Append( copy );
5441 srcElems.Append( elem );
5444 else if ( theCopy ) {
5445 if ( SMDS_MeshElement* copy = AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5446 myLastCreatedElems.Append( copy );
5447 srcElems.Append( elem );
5451 // reverse element as it was reversed by transformation
5453 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5457 PGroupIDs newGroupIDs;
5459 if ( theMakeGroups && theCopy ||
5460 theMakeGroups && theTargetMesh )
5461 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5466 //=======================================================================
5468 * \brief Create groups of elements made during transformation
5469 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5470 * \param elemGens - elements making corresponding myLastCreatedElems
5471 * \param postfix - to append to names of new groups
5473 //=======================================================================
5475 SMESH_MeshEditor::PGroupIDs
5476 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5477 const SMESH_SequenceOfElemPtr& elemGens,
5478 const std::string& postfix,
5479 SMESH_Mesh* targetMesh)
5481 PGroupIDs newGroupIDs( new list<int> );
5482 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5484 // Sort existing groups by types and collect their names
5486 // to store an old group and a generated new one
5487 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5488 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5490 set< string > groupNames;
5492 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5493 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5494 while ( groupIt->more() ) {
5495 SMESH_Group * group = groupIt->next();
5496 if ( !group ) continue;
5497 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5498 if ( !groupDS || groupDS->IsEmpty() ) continue;
5499 groupNames.insert( group->GetName() );
5500 groupDS->SetStoreName( group->GetName() );
5501 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5506 // loop on nodes and elements
5507 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5509 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5510 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5511 if ( gens.Length() != elems.Length() )
5512 throw SALOME_Exception(LOCALIZED("invalid args"));
5514 // loop on created elements
5515 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5517 const SMDS_MeshElement* sourceElem = gens( iElem );
5518 if ( !sourceElem ) {
5519 MESSAGE("generateGroups(): NULL source element");
5522 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5523 if ( groupsOldNew.empty() ) {
5524 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5525 ++iElem; // skip all elements made by sourceElem
5528 // collect all elements made by sourceElem
5529 list< const SMDS_MeshElement* > resultElems;
5530 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5531 if ( resElem != sourceElem )
5532 resultElems.push_back( resElem );
5533 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5534 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5535 if ( resElem != sourceElem )
5536 resultElems.push_back( resElem );
5537 // do not generate element groups from node ones
5538 if ( sourceElem->GetType() == SMDSAbs_Node &&
5539 elems( iElem )->GetType() != SMDSAbs_Node )
5542 // add resultElems to groups made by ones the sourceElem belongs to
5543 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5544 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5546 SMESHDS_GroupBase* oldGroup = gOldNew->first;
5547 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5549 SMDS_MeshGroup* & newGroup = gOldNew->second;
5550 if ( !newGroup )// create a new group
5553 string name = oldGroup->GetStoreName();
5554 if ( !targetMesh ) {
5558 while ( !groupNames.insert( name ).second ) // name exists
5564 TCollection_AsciiString nbStr(nb+1);
5565 name.resize( name.rfind('_')+1 );
5566 name += nbStr.ToCString();
5573 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5575 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5576 newGroup = & groupDS->SMDSGroup();
5577 newGroupIDs->push_back( id );
5580 // fill in a new group
5581 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5582 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5583 newGroup->Add( *resElemIt );
5586 } // loop on created elements
5587 }// loop on nodes and elements
5592 //================================================================================
5594 * \brief Return list of group of nodes close to each other within theTolerance
5595 * Search among theNodes or in the whole mesh if theNodes is empty using
5596 * an Octree algorithm
5598 //================================================================================
5600 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
5601 const double theTolerance,
5602 TListOfListOfNodes & theGroupsOfNodes)
5604 myLastCreatedElems.Clear();
5605 myLastCreatedNodes.Clear();
5607 if ( theNodes.empty() )
5608 { // get all nodes in the mesh
5609 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
5610 while ( nIt->more() )
5611 theNodes.insert( theNodes.end(),nIt->next());
5614 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
5618 //=======================================================================
5620 * \brief Implementation of search for the node closest to point
5622 //=======================================================================
5624 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
5626 //---------------------------------------------------------------------
5628 * \brief Constructor
5630 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
5632 myMesh = ( SMESHDS_Mesh* ) theMesh;
5634 TIDSortedNodeSet nodes;
5636 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
5637 while ( nIt->more() )
5638 nodes.insert( nodes.end(), nIt->next() );
5640 myOctreeNode = new SMESH_OctreeNode(nodes) ;
5642 // get max size of a leaf box
5643 SMESH_OctreeNode* tree = myOctreeNode;
5644 while ( !tree->isLeaf() )
5646 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5650 myHalfLeafSize = tree->maxSize() / 2.;
5653 //---------------------------------------------------------------------
5655 * \brief Move node and update myOctreeNode accordingly
5657 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
5659 myOctreeNode->UpdateByMoveNode( node, toPnt );
5660 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
5663 //---------------------------------------------------------------------
5665 * \brief Do it's job
5667 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
5669 SMDS_MeshNode tgtNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5670 map<double, const SMDS_MeshNode*> dist2Nodes;
5671 myOctreeNode->NodesAround( &tgtNode, dist2Nodes, myHalfLeafSize );
5672 if ( !dist2Nodes.empty() )
5673 return dist2Nodes.begin()->second;
5674 list<const SMDS_MeshNode*> nodes;
5675 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
5677 double minSqDist = DBL_MAX;
5678 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
5680 // sort leafs by their distance from thePnt
5681 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
5682 TDistTreeMap treeMap;
5683 list< SMESH_OctreeNode* > treeList;
5684 list< SMESH_OctreeNode* >::iterator trIt;
5685 treeList.push_back( myOctreeNode );
5687 SMDS_MeshNode pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5688 bool pointInside = myOctreeNode->isInside( &pointNode, myHalfLeafSize );
5689 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
5691 SMESH_OctreeNode* tree = *trIt;
5692 if ( !tree->isLeaf() ) // put children to the queue
5694 if ( pointInside && !tree->isInside( &pointNode, myHalfLeafSize )) continue;
5695 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5696 while ( cIt->more() )
5697 treeList.push_back( cIt->next() );
5699 else if ( tree->NbNodes() ) // put a tree to the treeMap
5701 const Bnd_B3d& box = tree->getBox();
5702 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
5703 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
5704 if ( !it_in.second ) // not unique distance to box center
5705 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
5708 // find distance after which there is no sense to check tree's
5709 double sqLimit = DBL_MAX;
5710 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
5711 if ( treeMap.size() > 5 ) {
5712 SMESH_OctreeNode* closestTree = sqDist_tree->second;
5713 const Bnd_B3d& box = closestTree->getBox();
5714 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
5715 sqLimit = limit * limit;
5717 // get all nodes from trees
5718 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
5719 if ( sqDist_tree->first > sqLimit )
5721 SMESH_OctreeNode* tree = sqDist_tree->second;
5722 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
5725 // find closest among nodes
5726 minSqDist = DBL_MAX;
5727 const SMDS_MeshNode* closestNode = 0;
5728 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
5729 for ( ; nIt != nodes.end(); ++nIt ) {
5730 double sqDist = thePnt.SquareDistance( SMESH_MeshEditor::TNodeXYZ( *nIt ) );
5731 if ( minSqDist > sqDist ) {
5739 //---------------------------------------------------------------------
5743 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
5745 //---------------------------------------------------------------------
5747 * \brief Return the node tree
5749 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
5752 SMESH_OctreeNode* myOctreeNode;
5753 SMESHDS_Mesh* myMesh;
5754 double myHalfLeafSize; // max size of a leaf box
5757 //=======================================================================
5759 * \brief Return SMESH_NodeSearcher
5761 //=======================================================================
5763 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
5765 return new SMESH_NodeSearcherImpl( GetMeshDS() );
5768 // ========================================================================
5769 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
5771 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
5772 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
5773 const double NodeRadius = 1e-9; // to enlarge bnd box of element
5775 //=======================================================================
5777 * \brief Octal tree of bounding boxes of elements
5779 //=======================================================================
5781 class ElementBndBoxTree : public SMESH_Octree
5785 ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType);
5786 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
5787 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
5788 ~ElementBndBoxTree();
5791 ElementBndBoxTree() {}
5792 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
5793 void buildChildrenData();
5794 Bnd_B3d* buildRootBox();
5796 //!< Bounding box of element
5797 struct ElementBox : public Bnd_B3d
5799 const SMDS_MeshElement* _element;
5800 int _refCount; // an ElementBox can be included in several tree branches
5801 ElementBox(const SMDS_MeshElement* elem);
5803 vector< ElementBox* > _elements;
5806 //================================================================================
5808 * \brief ElementBndBoxTree creation
5810 //================================================================================
5812 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType)
5813 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
5815 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
5816 _elements.reserve( nbElems );
5818 SMDS_ElemIteratorPtr elemIt = mesh.elementsIterator( elemType );
5819 while ( elemIt->more() )
5820 _elements.push_back( new ElementBox( elemIt->next() ));
5822 if ( _elements.size() > MaxNbElemsInLeaf )
5828 //================================================================================
5832 //================================================================================
5834 ElementBndBoxTree::~ElementBndBoxTree()
5836 for ( int i = 0; i < _elements.size(); ++i )
5837 if ( --_elements[i]->_refCount <= 0 )
5838 delete _elements[i];
5841 //================================================================================
5843 * \brief Return the maximal box
5845 //================================================================================
5847 Bnd_B3d* ElementBndBoxTree::buildRootBox()
5849 Bnd_B3d* box = new Bnd_B3d;
5850 for ( int i = 0; i < _elements.size(); ++i )
5851 box->Add( *_elements[i] );
5855 //================================================================================
5857 * \brief Redistrubute element boxes among children
5859 //================================================================================
5861 void ElementBndBoxTree::buildChildrenData()
5863 for ( int i = 0; i < _elements.size(); ++i )
5865 for (int j = 0; j < 8; j++)
5867 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
5869 _elements[i]->_refCount++;
5870 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
5873 _elements[i]->_refCount--;
5877 for (int j = 0; j < 8; j++)
5879 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
5880 if ( child->_elements.size() <= MaxNbElemsInLeaf )
5881 child->myIsLeaf = true;
5883 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
5884 child->_elements.resize( child->_elements.size() ); // compact
5888 //================================================================================
5890 * \brief Return elements which can include the point
5892 //================================================================================
5894 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
5895 TIDSortedElemSet& foundElems)
5897 if ( level() && getBox().IsOut( point.XYZ() ))
5902 for ( int i = 0; i < _elements.size(); ++i )
5903 if ( !_elements[i]->IsOut( point.XYZ() ))
5904 foundElems.insert( _elements[i]->_element );
5908 for (int i = 0; i < 8; i++)
5909 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
5913 //================================================================================
5915 * \brief Return elements which can be intersected by the line
5917 //================================================================================
5919 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
5920 TIDSortedElemSet& foundElems)
5922 if ( level() && getBox().IsOut( line ))
5927 for ( int i = 0; i < _elements.size(); ++i )
5928 if ( !_elements[i]->IsOut( line ))
5929 foundElems.insert( _elements[i]->_element );
5933 for (int i = 0; i < 8; i++)
5934 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
5938 //================================================================================
5940 * \brief Construct the element box
5942 //================================================================================
5944 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem)
5948 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
5949 while ( nIt->more() )
5950 Add( SMESH_MeshEditor::TNodeXYZ( cast2Node( nIt->next() )));
5951 Enlarge( NodeRadius );
5956 //=======================================================================
5958 * \brief Implementation of search for the elements by point and
5959 * of classification of point in 2D mesh
5961 //=======================================================================
5963 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
5965 SMESHDS_Mesh* _mesh;
5966 ElementBndBoxTree* _ebbTree;
5967 SMESH_NodeSearcherImpl* _nodeSearcher;
5968 SMDSAbs_ElementType _elementType;
5970 bool _outerFacesFound;
5971 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
5973 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh )
5974 : _mesh(&mesh),_ebbTree(0),_nodeSearcher(0), _tolerance(-1), _outerFacesFound(false) {}
5975 ~SMESH_ElementSearcherImpl()
5977 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
5978 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
5980 virtual int FindElementsByPoint(const gp_Pnt& point,
5981 SMDSAbs_ElementType type,
5982 vector< const SMDS_MeshElement* >& foundElements);
5983 virtual TopAbs_State GetPointState(const gp_Pnt& point);
5985 void GetElementsNearLine( const gp_Ax1& line,
5986 SMDSAbs_ElementType type,
5987 vector< const SMDS_MeshElement* >& foundElems);
5988 double getTolerance();
5989 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
5990 const double tolerance, double & param);
5991 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
5992 bool isOuterBoundary(const SMDS_MeshElement* face) const
5994 return _outerFaces.empty() || _outerFaces.count(face);
5996 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
5998 const SMDS_MeshElement* _face;
6000 bool _coincides; //!< the line lays in face plane
6001 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6002 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6004 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6007 TIDSortedElemSet _faces;
6008 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6009 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6013 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6015 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6016 << ", _coincides="<<i._coincides << ")";
6019 //=======================================================================
6021 * \brief define tolerance for search
6023 //=======================================================================
6025 double SMESH_ElementSearcherImpl::getTolerance()
6027 if ( _tolerance < 0 )
6029 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6032 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6034 double boxSize = _nodeSearcher->getTree()->maxSize();
6035 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6037 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6039 double boxSize = _ebbTree->maxSize();
6040 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6042 if ( _tolerance == 0 )
6044 // define tolerance by size of a most complex element
6045 int complexType = SMDSAbs_Volume;
6046 while ( complexType > SMDSAbs_All &&
6047 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6049 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6052 if ( complexType == int( SMDSAbs_Node ))
6054 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6056 if ( meshInfo.NbNodes() > 2 )
6057 elemSize = SMESH_MeshEditor::TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6061 const SMDS_MeshElement* elem =
6062 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ))->next();
6063 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6064 SMESH_MeshEditor::TNodeXYZ n1( cast2Node( nodeIt->next() ));
6065 while ( nodeIt->more() )
6067 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6068 elemSize = max( dist, elemSize );
6071 _tolerance = 1e-6 * elemSize;
6077 //================================================================================
6079 * \brief Find intersection of the line and an edge of face and return parameter on line
6081 //================================================================================
6083 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6084 const SMDS_MeshElement* face,
6091 GeomAPI_ExtremaCurveCurve anExtCC;
6092 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6094 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6095 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6097 GC_MakeSegment edge( SMESH_MeshEditor::TNodeXYZ( face->GetNode( i )),
6098 SMESH_MeshEditor::TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6099 anExtCC.Init( lineCurve, edge);
6100 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6102 Quantity_Parameter pl, pe;
6103 anExtCC.LowerDistanceParameters( pl, pe );
6105 if ( ++nbInts == 2 )
6109 if ( nbInts > 0 ) param /= nbInts;
6112 //================================================================================
6114 * \brief Find all faces belonging to the outer boundary of mesh
6116 //================================================================================
6118 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6120 if ( _outerFacesFound ) return;
6122 // Collect all outer faces by passing from one outer face to another via their links
6123 // and BTW find out if there are internal faces at all.
6125 // checked links and links where outer boundary meets internal one
6126 set< SMESH_TLink > visitedLinks, seamLinks;
6128 // links to treat with already visited faces sharing them
6129 list < TFaceLink > startLinks;
6131 // load startLinks with the first outerFace
6132 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6133 _outerFaces.insert( outerFace );
6135 TIDSortedElemSet emptySet;
6136 while ( !startLinks.empty() )
6138 const SMESH_TLink& link = startLinks.front()._link;
6139 TIDSortedElemSet& faces = startLinks.front()._faces;
6141 outerFace = *faces.begin();
6142 // find other faces sharing the link
6143 const SMDS_MeshElement* f;
6144 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6147 // select another outer face among the found
6148 const SMDS_MeshElement* outerFace2 = 0;
6149 if ( faces.size() == 2 )
6151 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6153 else if ( faces.size() > 2 )
6155 seamLinks.insert( link );
6157 // link direction within the outerFace
6158 gp_Vec n1n2( SMESH_MeshEditor::TNodeXYZ( link.node1()),
6159 SMESH_MeshEditor::TNodeXYZ( link.node2()));
6160 int i1 = outerFace->GetNodeIndex( link.node1() );
6161 int i2 = outerFace->GetNodeIndex( link.node2() );
6162 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6163 if ( rev ) n1n2.Reverse();
6165 gp_XYZ ofNorm, fNorm;
6166 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6168 // direction from the link inside outerFace
6169 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6170 // sort all other faces by angle with the dirInOF
6171 map< double, const SMDS_MeshElement* > angle2Face;
6172 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6173 for ( ; face != faces.end(); ++face )
6175 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6177 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6178 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6179 if ( angle < 0 ) angle += 2*PI;
6180 angle2Face.insert( make_pair( angle, *face ));
6182 if ( !angle2Face.empty() )
6183 outerFace2 = angle2Face.begin()->second;
6186 // store the found outer face and add its links to continue seaching from
6189 _outerFaces.insert( outerFace );
6190 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6191 for ( int i = 0; i < nbNodes; ++i )
6193 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6194 if ( visitedLinks.insert( link2 ).second )
6195 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6198 startLinks.pop_front();
6200 _outerFacesFound = true;
6202 if ( !seamLinks.empty() )
6204 // There are internal boundaries touching the outher one,
6205 // find all faces of internal boundaries in order to find
6206 // faces of boundaries of holes, if any.
6211 _outerFaces.clear();
6215 //=======================================================================
6217 * \brief Find elements of given type where the given point is IN or ON.
6218 * Returns nb of found elements and elements them-selves.
6220 * 'ALL' type means elements of any type excluding nodes and 0D elements
6222 //=======================================================================
6224 int SMESH_ElementSearcherImpl::
6225 FindElementsByPoint(const gp_Pnt& point,
6226 SMDSAbs_ElementType type,
6227 vector< const SMDS_MeshElement* >& foundElements)
6229 foundElements.clear();
6231 double tolerance = getTolerance();
6233 // =================================================================================
6234 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6236 if ( !_nodeSearcher )
6237 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6239 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6240 if ( !closeNode ) return foundElements.size();
6242 if ( point.Distance( SMESH_MeshEditor::TNodeXYZ( closeNode )) > tolerance )
6243 return foundElements.size(); // to far from any node
6245 if ( type == SMDSAbs_Node )
6247 foundElements.push_back( closeNode );
6251 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6252 while ( elemIt->more() )
6253 foundElements.push_back( elemIt->next() );
6256 // =================================================================================
6257 else // elements more complex than 0D
6259 if ( !_ebbTree || _elementType != type )
6261 if ( _ebbTree ) delete _ebbTree;
6262 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type );
6264 TIDSortedElemSet suspectElems;
6265 _ebbTree->getElementsNearPoint( point, suspectElems );
6266 TIDSortedElemSet::iterator elem = suspectElems.begin();
6267 for ( ; elem != suspectElems.end(); ++elem )
6268 if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6269 foundElements.push_back( *elem );
6271 return foundElements.size();
6274 //================================================================================
6276 * \brief Classify the given point in the closed 2D mesh
6278 //================================================================================
6280 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6282 double tolerance = getTolerance();
6283 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6285 if ( _ebbTree ) delete _ebbTree;
6286 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face );
6288 // Algo: analyse transition of a line starting at the point through mesh boundary;
6289 // try three lines parallel to axis of the coordinate system and perform rough
6290 // analysis. If solution is not clear perform thorough analysis.
6292 const int nbAxes = 3;
6293 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6294 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6295 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6296 multimap< int, int > nbInt2Axis; // to find the simplest case
6297 for ( int axis = 0; axis < nbAxes; ++axis )
6299 gp_Ax1 lineAxis( point, axisDir[axis]);
6300 gp_Lin line ( lineAxis );
6302 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6303 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6305 // Intersect faces with the line
6307 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6308 TIDSortedElemSet::iterator face = suspectFaces.begin();
6309 for ( ; face != suspectFaces.end(); ++face )
6313 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6314 gp_Pln facePlane( SMESH_MeshEditor::TNodeXYZ( (*face)->GetNode(0)), fNorm );
6316 // perform intersection
6317 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6318 if ( !intersection.IsDone() )
6320 if ( intersection.IsInQuadric() )
6322 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6324 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6326 gp_Pnt intersectionPoint = intersection.Point(1);
6327 if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6328 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6331 // Analyse intersections roughly
6333 int nbInter = u2inters.size();
6337 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6338 if ( nbInter == 1 ) // not closed mesh
6339 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6341 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6344 if ( (f<0) == (l<0) )
6347 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6348 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6349 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6352 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6354 if ( _outerFacesFound ) break; // pass to thorough analysis
6356 } // three attempts - loop on CS axes
6358 // Analyse intersections thoroughly.
6359 // We make two loops maximum, on the first one we only exclude touching intersections,
6360 // on the second, if situation is still unclear, we gather and use information on
6361 // position of faces (internal or outer). If faces position is already gathered,
6362 // we make the second loop right away.
6364 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6366 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6367 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6369 int axis = nb_axis->second;
6370 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6372 gp_Ax1 lineAxis( point, axisDir[axis]);
6373 gp_Lin line ( lineAxis );
6375 // add tangent intersections to u2inters
6377 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6378 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6379 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6380 u2inters.insert(make_pair( param, *tgtInt ));
6381 tangentInters[ axis ].clear();
6383 // Count intersections before and after the point excluding touching ones.
6384 // If hasPositionInfo we count intersections of outer boundary only
6386 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6387 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6388 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6389 bool ok = ! u_int1->second._coincides;
6390 while ( ok && u_int1 != u2inters.end() )
6392 double u = u_int1->first;
6393 bool touchingInt = false;
6394 if ( ++u_int2 != u2inters.end() )
6396 // skip intersections at the same point (if the line passes through edge or node)
6398 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6404 // skip tangent intersections
6406 const SMDS_MeshElement* prevFace = u_int1->second._face;
6407 while ( ok && u_int2->second._coincides )
6409 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6415 ok = ( u_int2 != u2inters.end() );
6420 // skip intersections at the same point after tangent intersections
6423 double u2 = u_int2->first;
6425 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6431 // decide if we skipped a touching intersection
6432 if ( nbSamePnt + nbTgt > 0 )
6434 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6435 map< double, TInters >::iterator u_int = u_int1;
6436 for ( ; u_int != u_int2; ++u_int )
6438 if ( u_int->second._coincides ) continue;
6439 double dot = u_int->second._faceNorm * line.Direction();
6440 if ( dot > maxDot ) maxDot = dot;
6441 if ( dot < minDot ) minDot = dot;
6443 touchingInt = ( minDot*maxDot < 0 );
6448 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6459 u_int1 = u_int2; // to next intersection
6461 } // loop on intersections with one line
6465 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6468 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6471 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6472 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6474 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6477 if ( (f<0) == (l<0) )
6480 if ( hasPositionInfo )
6481 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
6483 } // loop on intersections of the tree lines - thorough analysis
6485 if ( !hasPositionInfo )
6487 // gather info on faces position - is face in the outer boundary or not
6488 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
6489 findOuterBoundary( u2inters.begin()->second._face );
6492 } // two attempts - with and w/o faces position info in the mesh
6494 return TopAbs_UNKNOWN;
6497 //=======================================================================
6499 * \brief Return elements possibly intersecting the line
6501 //=======================================================================
6503 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
6504 SMDSAbs_ElementType type,
6505 vector< const SMDS_MeshElement* >& foundElems)
6507 if ( !_ebbTree || _elementType != type )
6509 if ( _ebbTree ) delete _ebbTree;
6510 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type );
6512 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
6513 _ebbTree->getElementsNearLine( line, suspectFaces );
6514 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
6517 //=======================================================================
6519 * \brief Return SMESH_ElementSearcher
6521 //=======================================================================
6523 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
6525 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
6528 //=======================================================================
6530 * \brief Return true if the point is IN or ON of the element
6532 //=======================================================================
6534 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
6536 if ( element->GetType() == SMDSAbs_Volume)
6538 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
6541 // get ordered nodes
6543 vector< gp_XYZ > xyz;
6545 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
6546 if ( element->IsQuadratic() )
6547 if (const SMDS_QuadraticFaceOfNodes* f=dynamic_cast<const SMDS_QuadraticFaceOfNodes*>(element))
6548 nodeIt = f->interlacedNodesElemIterator();
6549 else if (const SMDS_QuadraticEdge* e =dynamic_cast<const SMDS_QuadraticEdge*>(element))
6550 nodeIt = e->interlacedNodesElemIterator();
6552 while ( nodeIt->more() )
6553 xyz.push_back( TNodeXYZ( cast2Node( nodeIt->next() )));
6555 int i, nbNodes = element->NbNodes();
6557 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
6559 // compute face normal
6560 gp_Vec faceNorm(0,0,0);
6561 xyz.push_back( xyz.front() );
6562 for ( i = 0; i < nbNodes; ++i )
6564 gp_Vec edge1( xyz[i+1], xyz[i]);
6565 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
6566 faceNorm += edge1 ^ edge2;
6568 double normSize = faceNorm.Magnitude();
6569 if ( normSize <= tol )
6571 // degenerated face: point is out if it is out of all face edges
6572 for ( i = 0; i < nbNodes; ++i )
6574 SMDS_MeshNode n1( xyz[i].X(), xyz[i].Y(), xyz[i].Z() );
6575 SMDS_MeshNode n2( xyz[i+1].X(), xyz[i+1].Y(), xyz[i+1].Z() );
6576 SMDS_MeshEdge edge( &n1, &n2 );
6577 if ( !isOut( &edge, point, tol ))
6582 faceNorm /= normSize;
6584 // check if the point lays on face plane
6585 gp_Vec n2p( xyz[0], point );
6586 if ( fabs( n2p * faceNorm ) > tol )
6587 return true; // not on face plane
6589 // check if point is out of face boundary:
6590 // define it by closest transition of a ray point->infinity through face boundary
6591 // on the face plane.
6592 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
6593 // to find intersections of the ray with the boundary.
6595 gp_Vec plnNorm = ray ^ faceNorm;
6596 normSize = plnNorm.Magnitude();
6597 if ( normSize <= tol ) return false; // point coincides with the first node
6598 plnNorm /= normSize;
6599 // for each node of the face, compute its signed distance to the plane
6600 vector<double> dist( nbNodes + 1);
6601 for ( i = 0; i < nbNodes; ++i )
6603 gp_Vec n2p( xyz[i], point );
6604 dist[i] = n2p * plnNorm;
6606 dist.back() = dist.front();
6607 // find the closest intersection
6609 double rClosest, distClosest = 1e100;;
6611 for ( i = 0; i < nbNodes; ++i )
6614 if ( fabs( dist[i]) < tol )
6616 else if ( fabs( dist[i+1]) < tol )
6618 else if ( dist[i] * dist[i+1] < 0 )
6619 r = dist[i] / ( dist[i] - dist[i+1] );
6621 continue; // no intersection
6622 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
6623 gp_Vec p2int ( point, pInt);
6624 if ( p2int * ray > -tol ) // right half-space
6626 double intDist = p2int.SquareMagnitude();
6627 if ( intDist < distClosest )
6632 distClosest = intDist;
6637 return true; // no intesections - out
6639 // analyse transition
6640 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
6641 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
6642 gp_Vec p2int ( point, pClosest );
6643 bool out = (edgeNorm * p2int) < -tol;
6644 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
6647 // ray pass through a face node; analyze transition through an adjacent edge
6648 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
6649 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
6650 gp_Vec edgeAdjacent( p1, p2 );
6651 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
6652 bool out2 = (edgeNorm2 * p2int) < -tol;
6654 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
6655 return covexCorner ? (out || out2) : (out && out2);
6657 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
6659 // point is out of edge if it is NOT ON any straight part of edge
6660 // (we consider quadratic edge as being composed of two straight parts)
6661 for ( i = 1; i < nbNodes; ++i )
6663 gp_Vec edge( xyz[i-1], xyz[i]);
6664 gp_Vec n1p ( xyz[i-1], point);
6665 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
6668 gp_Vec n2p( xyz[i], point );
6669 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
6671 return false; // point is ON this part
6675 // Node or 0D element -------------------------------------------------------------------------
6677 gp_Vec n2p ( xyz[0], point );
6678 return n2p.Magnitude() <= tol;
6683 //=======================================================================
6684 //function : SimplifyFace
6686 //=======================================================================
6687 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
6688 vector<const SMDS_MeshNode *>& poly_nodes,
6689 vector<int>& quantities) const
6691 int nbNodes = faceNodes.size();
6696 set<const SMDS_MeshNode*> nodeSet;
6698 // get simple seq of nodes
6699 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6700 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6701 int iSimple = 0, nbUnique = 0;
6703 simpleNodes[iSimple++] = faceNodes[0];
6705 for (int iCur = 1; iCur < nbNodes; iCur++) {
6706 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6707 simpleNodes[iSimple++] = faceNodes[iCur];
6708 if (nodeSet.insert( faceNodes[iCur] ).second)
6712 int nbSimple = iSimple;
6713 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6723 bool foundLoop = (nbSimple > nbUnique);
6726 set<const SMDS_MeshNode*> loopSet;
6727 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6728 const SMDS_MeshNode* n = simpleNodes[iSimple];
6729 if (!loopSet.insert( n ).second) {
6733 int iC = 0, curLast = iSimple;
6734 for (; iC < curLast; iC++) {
6735 if (simpleNodes[iC] == n) break;
6737 int loopLen = curLast - iC;
6739 // create sub-element
6741 quantities.push_back(loopLen);
6742 for (; iC < curLast; iC++) {
6743 poly_nodes.push_back(simpleNodes[iC]);
6746 // shift the rest nodes (place from the first loop position)
6747 for (iC = curLast + 1; iC < nbSimple; iC++) {
6748 simpleNodes[iC - loopLen] = simpleNodes[iC];
6750 nbSimple -= loopLen;
6753 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6754 } // while (foundLoop)
6758 quantities.push_back(iSimple);
6759 for (int i = 0; i < iSimple; i++)
6760 poly_nodes.push_back(simpleNodes[i]);
6766 //=======================================================================
6767 //function : MergeNodes
6768 //purpose : In each group, the cdr of nodes are substituted by the first one
6770 //=======================================================================
6772 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6774 myLastCreatedElems.Clear();
6775 myLastCreatedNodes.Clear();
6777 SMESHDS_Mesh* aMesh = GetMeshDS();
6779 TNodeNodeMap nodeNodeMap; // node to replace - new node
6780 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6781 list< int > rmElemIds, rmNodeIds;
6783 // Fill nodeNodeMap and elems
6785 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6786 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6787 list<const SMDS_MeshNode*>& nodes = *grIt;
6788 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6789 const SMDS_MeshNode* nToKeep = *nIt;
6790 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6791 const SMDS_MeshNode* nToRemove = *nIt;
6792 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6793 if ( nToRemove != nToKeep ) {
6794 rmNodeIds.push_back( nToRemove->GetID() );
6795 AddToSameGroups( nToKeep, nToRemove, aMesh );
6798 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6799 while ( invElemIt->more() ) {
6800 const SMDS_MeshElement* elem = invElemIt->next();
6805 // Change element nodes or remove an element
6807 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6808 for ( ; eIt != elems.end(); eIt++ ) {
6809 const SMDS_MeshElement* elem = *eIt;
6810 int nbNodes = elem->NbNodes();
6811 int aShapeId = FindShape( elem );
6813 set<const SMDS_MeshNode*> nodeSet;
6814 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6815 int iUnique = 0, iCur = 0, nbRepl = 0;
6816 vector<int> iRepl( nbNodes );
6818 // get new seq of nodes
6819 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6820 while ( itN->more() ) {
6821 const SMDS_MeshNode* n =
6822 static_cast<const SMDS_MeshNode*>( itN->next() );
6824 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6825 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6827 // BUG 0020185: begin
6829 bool stopRecur = false;
6830 set<const SMDS_MeshNode*> nodesRecur;
6831 nodesRecur.insert(n);
6832 while (!stopRecur) {
6833 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6834 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6835 n = (*nnIt_i).second;
6836 if (!nodesRecur.insert(n).second) {
6837 // error: recursive dependancy
6846 iRepl[ nbRepl++ ] = iCur;
6848 curNodes[ iCur ] = n;
6849 bool isUnique = nodeSet.insert( n ).second;
6851 uniqueNodes[ iUnique++ ] = n;
6855 // Analyse element topology after replacement
6858 int nbUniqueNodes = nodeSet.size();
6859 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6860 // Polygons and Polyhedral volumes
6861 if (elem->IsPoly()) {
6863 if (elem->GetType() == SMDSAbs_Face) {
6865 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6867 for (; inode < nbNodes; inode++) {
6868 face_nodes[inode] = curNodes[inode];
6871 vector<const SMDS_MeshNode *> polygons_nodes;
6872 vector<int> quantities;
6873 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6877 for (int iface = 0; iface < nbNew - 1; iface++) {
6878 int nbNodes = quantities[iface];
6879 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6880 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6881 poly_nodes[ii] = polygons_nodes[inode];
6883 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6884 myLastCreatedElems.Append(newElem);
6886 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6888 aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6891 rmElemIds.push_back(elem->GetID());
6895 else if (elem->GetType() == SMDSAbs_Volume) {
6896 // Polyhedral volume
6897 if (nbUniqueNodes < 4) {
6898 rmElemIds.push_back(elem->GetID());
6901 // each face has to be analized in order to check volume validity
6902 const SMDS_PolyhedralVolumeOfNodes* aPolyedre =
6903 static_cast<const SMDS_PolyhedralVolumeOfNodes*>( elem );
6905 int nbFaces = aPolyedre->NbFaces();
6907 vector<const SMDS_MeshNode *> poly_nodes;
6908 vector<int> quantities;
6910 for (int iface = 1; iface <= nbFaces; iface++) {
6911 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6912 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6914 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6915 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6916 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6917 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6918 faceNode = (*nnIt).second;
6920 faceNodes[inode - 1] = faceNode;
6923 SimplifyFace(faceNodes, poly_nodes, quantities);
6926 if (quantities.size() > 3) {
6927 // to be done: remove coincident faces
6930 if (quantities.size() > 3)
6931 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6933 rmElemIds.push_back(elem->GetID());
6937 rmElemIds.push_back(elem->GetID());
6948 switch ( nbNodes ) {
6949 case 2: ///////////////////////////////////// EDGE
6950 isOk = false; break;
6951 case 3: ///////////////////////////////////// TRIANGLE
6952 isOk = false; break;
6954 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6956 else { //////////////////////////////////// QUADRANGLE
6957 if ( nbUniqueNodes < 3 )
6959 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6960 isOk = false; // opposite nodes stick
6963 case 6: ///////////////////////////////////// PENTAHEDRON
6964 if ( nbUniqueNodes == 4 ) {
6965 // ---------------------------------> tetrahedron
6967 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6968 // all top nodes stick: reverse a bottom
6969 uniqueNodes[ 0 ] = curNodes [ 1 ];
6970 uniqueNodes[ 1 ] = curNodes [ 0 ];
6972 else if (nbRepl == 3 &&
6973 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6974 // all bottom nodes stick: set a top before
6975 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6976 uniqueNodes[ 0 ] = curNodes [ 3 ];
6977 uniqueNodes[ 1 ] = curNodes [ 4 ];
6978 uniqueNodes[ 2 ] = curNodes [ 5 ];
6980 else if (nbRepl == 4 &&
6981 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6982 // a lateral face turns into a line: reverse a bottom
6983 uniqueNodes[ 0 ] = curNodes [ 1 ];
6984 uniqueNodes[ 1 ] = curNodes [ 0 ];
6989 else if ( nbUniqueNodes == 5 ) {
6990 // PENTAHEDRON --------------------> 2 tetrahedrons
6991 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6992 // a bottom node sticks with a linked top one
6994 SMDS_MeshElement* newElem =
6995 aMesh->AddVolume(curNodes[ 3 ],
6998 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6999 myLastCreatedElems.Append(newElem);
7001 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7002 // 2. : reverse a bottom
7003 uniqueNodes[ 0 ] = curNodes [ 1 ];
7004 uniqueNodes[ 1 ] = curNodes [ 0 ];
7014 if(elem->IsQuadratic()) { // Quadratic quadrangle
7027 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7028 uniqueNodes[0] = curNodes[0];
7029 uniqueNodes[1] = curNodes[2];
7030 uniqueNodes[2] = curNodes[3];
7031 uniqueNodes[3] = curNodes[5];
7032 uniqueNodes[4] = curNodes[6];
7033 uniqueNodes[5] = curNodes[7];
7036 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7037 uniqueNodes[0] = curNodes[0];
7038 uniqueNodes[1] = curNodes[1];
7039 uniqueNodes[2] = curNodes[2];
7040 uniqueNodes[3] = curNodes[4];
7041 uniqueNodes[4] = curNodes[5];
7042 uniqueNodes[5] = curNodes[6];
7045 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7046 uniqueNodes[0] = curNodes[1];
7047 uniqueNodes[1] = curNodes[2];
7048 uniqueNodes[2] = curNodes[3];
7049 uniqueNodes[3] = curNodes[5];
7050 uniqueNodes[4] = curNodes[6];
7051 uniqueNodes[5] = curNodes[0];
7054 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7055 uniqueNodes[0] = curNodes[0];
7056 uniqueNodes[1] = curNodes[1];
7057 uniqueNodes[2] = curNodes[3];
7058 uniqueNodes[3] = curNodes[4];
7059 uniqueNodes[4] = curNodes[6];
7060 uniqueNodes[5] = curNodes[7];
7063 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7064 uniqueNodes[0] = curNodes[0];
7065 uniqueNodes[1] = curNodes[2];
7066 uniqueNodes[2] = curNodes[3];
7067 uniqueNodes[3] = curNodes[1];
7068 uniqueNodes[4] = curNodes[6];
7069 uniqueNodes[5] = curNodes[7];
7072 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7073 uniqueNodes[0] = curNodes[0];
7074 uniqueNodes[1] = curNodes[1];
7075 uniqueNodes[2] = curNodes[2];
7076 uniqueNodes[3] = curNodes[4];
7077 uniqueNodes[4] = curNodes[5];
7078 uniqueNodes[5] = curNodes[7];
7081 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7082 uniqueNodes[0] = curNodes[0];
7083 uniqueNodes[1] = curNodes[1];
7084 uniqueNodes[2] = curNodes[3];
7085 uniqueNodes[3] = curNodes[4];
7086 uniqueNodes[4] = curNodes[2];
7087 uniqueNodes[5] = curNodes[7];
7090 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7091 uniqueNodes[0] = curNodes[0];
7092 uniqueNodes[1] = curNodes[1];
7093 uniqueNodes[2] = curNodes[2];
7094 uniqueNodes[3] = curNodes[4];
7095 uniqueNodes[4] = curNodes[5];
7096 uniqueNodes[5] = curNodes[3];
7102 //////////////////////////////////// HEXAHEDRON
7104 SMDS_VolumeTool hexa (elem);
7105 hexa.SetExternalNormal();
7106 if ( nbUniqueNodes == 4 && nbRepl == 6 ) {
7107 //////////////////////// ---> tetrahedron
7108 for ( int iFace = 0; iFace < 6; iFace++ ) {
7109 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7110 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7111 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7112 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7113 // one face turns into a point ...
7114 int iOppFace = hexa.GetOppFaceIndex( iFace );
7115 ind = hexa.GetFaceNodesIndices( iOppFace );
7117 iUnique = 2; // reverse a tetrahedron bottom
7118 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7119 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7121 else if ( iUnique >= 0 )
7122 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7124 if ( nbStick == 1 ) {
7125 // ... and the opposite one - into a triangle.
7127 ind = hexa.GetFaceNodesIndices( iFace );
7128 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7135 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7136 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7137 for ( int iFace = 0; iFace < 6; iFace++ ) {
7138 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7139 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7140 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7141 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7142 // one face turns into a point ...
7143 int iOppFace = hexa.GetOppFaceIndex( iFace );
7144 ind = hexa.GetFaceNodesIndices( iOppFace );
7146 iUnique = 2; // reverse a tetrahedron 1 bottom
7147 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7148 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7150 else if ( iUnique >= 0 )
7151 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7153 if ( nbStick == 0 ) {
7154 // ... and the opposite one is a quadrangle
7156 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7157 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7160 SMDS_MeshElement* newElem =
7161 aMesh->AddVolume(curNodes[ind[ 0 ]],
7164 curNodes[indTop[ 0 ]]);
7165 myLastCreatedElems.Append(newElem);
7167 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7174 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7175 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7176 // find indices of quad and tri faces
7177 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7178 for ( iFace = 0; iFace < 6; iFace++ ) {
7179 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7181 for ( iCur = 0; iCur < 4; iCur++ )
7182 nodeSet.insert( curNodes[ind[ iCur ]] );
7183 nbUniqueNodes = nodeSet.size();
7184 if ( nbUniqueNodes == 3 )
7185 iTriFace[ nbTri++ ] = iFace;
7186 else if ( nbUniqueNodes == 4 )
7187 iQuadFace[ nbQuad++ ] = iFace;
7189 if (nbQuad == 2 && nbTri == 4 &&
7190 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7191 // 2 opposite quadrangles stuck with a diagonal;
7192 // sample groups of merged indices: (0-4)(2-6)
7193 // --------------------------------------------> 2 tetrahedrons
7194 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7195 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7196 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7197 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7198 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7199 // stuck with 0-2 diagonal
7207 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7208 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7209 // stuck with 1-3 diagonal
7221 uniqueNodes[ 0 ] = curNodes [ i0 ];
7222 uniqueNodes[ 1 ] = curNodes [ i1d ];
7223 uniqueNodes[ 2 ] = curNodes [ i3d ];
7224 uniqueNodes[ 3 ] = curNodes [ i0t ];
7227 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7231 myLastCreatedElems.Append(newElem);
7233 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7236 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7237 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7238 // --------------------------------------------> prism
7239 // find 2 opposite triangles
7241 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7242 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7243 // find indices of kept and replaced nodes
7244 // and fill unique nodes of 2 opposite triangles
7245 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7246 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7247 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7248 // fill unique nodes
7251 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7252 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7253 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7255 // iCur of a linked node of the opposite face (make normals co-directed):
7256 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7257 // check that correspondent corners of triangles are linked
7258 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7261 uniqueNodes[ iUnique ] = n;
7262 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7271 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7277 } // switch ( nbNodes )
7279 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7282 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume) {
7283 // Change nodes of polyedre
7284 const SMDS_PolyhedralVolumeOfNodes* aPolyedre =
7285 static_cast<const SMDS_PolyhedralVolumeOfNodes*>( elem );
7287 int nbFaces = aPolyedre->NbFaces();
7289 vector<const SMDS_MeshNode *> poly_nodes;
7290 vector<int> quantities (nbFaces);
7292 for (int iface = 1; iface <= nbFaces; iface++) {
7293 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7294 quantities[iface - 1] = nbFaceNodes;
7296 for (inode = 1; inode <= nbFaceNodes; inode++) {
7297 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7299 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7300 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7301 curNode = (*nnIt).second;
7303 poly_nodes.push_back(curNode);
7306 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7310 // Change regular element or polygon
7311 aMesh->ChangeElementNodes( elem, & uniqueNodes[0], nbUniqueNodes );
7315 // Remove invalid regular element or invalid polygon
7316 rmElemIds.push_back( elem->GetID() );
7319 } // loop on elements
7321 // Remove equal nodes and bad elements
7323 Remove( rmNodeIds, true );
7324 Remove( rmElemIds, false );
7329 // ========================================================
7330 // class : SortableElement
7331 // purpose : allow sorting elements basing on their nodes
7332 // ========================================================
7333 class SortableElement : public set <const SMDS_MeshElement*>
7337 SortableElement( const SMDS_MeshElement* theElem )
7340 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7341 while ( nodeIt->more() )
7342 this->insert( nodeIt->next() );
7345 const SMDS_MeshElement* Get() const
7348 void Set(const SMDS_MeshElement* e) const
7353 mutable const SMDS_MeshElement* myElem;
7356 //=======================================================================
7357 //function : FindEqualElements
7358 //purpose : Return list of group of elements built on the same nodes.
7359 // Search among theElements or in the whole mesh if theElements is empty
7360 //=======================================================================
7361 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7362 TListOfListOfElementsID & theGroupsOfElementsID)
7364 myLastCreatedElems.Clear();
7365 myLastCreatedNodes.Clear();
7367 typedef set<const SMDS_MeshElement*> TElemsSet;
7368 typedef map< SortableElement, int > TMapOfNodeSet;
7369 typedef list<int> TGroupOfElems;
7372 if ( theElements.empty() )
7373 { // get all elements in the mesh
7374 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7375 while ( eIt->more() )
7376 elems.insert( elems.end(), eIt->next());
7379 elems = theElements;
7381 vector< TGroupOfElems > arrayOfGroups;
7382 TGroupOfElems groupOfElems;
7383 TMapOfNodeSet mapOfNodeSet;
7385 TElemsSet::iterator elemIt = elems.begin();
7386 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
7387 const SMDS_MeshElement* curElem = *elemIt;
7388 SortableElement SE(curElem);
7391 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7392 if( !(pp.second) ) {
7393 TMapOfNodeSet::iterator& itSE = pp.first;
7394 ind = (*itSE).second;
7395 arrayOfGroups[ind].push_back(curElem->GetID());
7398 groupOfElems.clear();
7399 groupOfElems.push_back(curElem->GetID());
7400 arrayOfGroups.push_back(groupOfElems);
7405 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7406 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7407 groupOfElems = *groupIt;
7408 if ( groupOfElems.size() > 1 ) {
7409 groupOfElems.sort();
7410 theGroupsOfElementsID.push_back(groupOfElems);
7415 //=======================================================================
7416 //function : MergeElements
7417 //purpose : In each given group, substitute all elements by the first one.
7418 //=======================================================================
7420 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7422 myLastCreatedElems.Clear();
7423 myLastCreatedNodes.Clear();
7425 typedef list<int> TListOfIDs;
7426 TListOfIDs rmElemIds; // IDs of elems to remove
7428 SMESHDS_Mesh* aMesh = GetMeshDS();
7430 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7431 while ( groupsIt != theGroupsOfElementsID.end() ) {
7432 TListOfIDs& aGroupOfElemID = *groupsIt;
7433 aGroupOfElemID.sort();
7434 int elemIDToKeep = aGroupOfElemID.front();
7435 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7436 aGroupOfElemID.pop_front();
7437 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7438 while ( idIt != aGroupOfElemID.end() ) {
7439 int elemIDToRemove = *idIt;
7440 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7441 // add the kept element in groups of removed one (PAL15188)
7442 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7443 rmElemIds.push_back( elemIDToRemove );
7449 Remove( rmElemIds, false );
7452 //=======================================================================
7453 //function : MergeEqualElements
7454 //purpose : Remove all but one of elements built on the same nodes.
7455 //=======================================================================
7457 void SMESH_MeshEditor::MergeEqualElements()
7459 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
7460 to merge equal elements in the whole mesh */
7461 TListOfListOfElementsID aGroupsOfElementsID;
7462 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7463 MergeElements(aGroupsOfElementsID);
7466 //=======================================================================
7467 //function : FindFaceInSet
7468 //purpose : Return a face having linked nodes n1 and n2 and which is
7469 // - not in avoidSet,
7470 // - in elemSet provided that !elemSet.empty()
7471 // i1 and i2 optionally returns indices of n1 and n2
7472 //=======================================================================
7474 const SMDS_MeshElement*
7475 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
7476 const SMDS_MeshNode* n2,
7477 const TIDSortedElemSet& elemSet,
7478 const TIDSortedElemSet& avoidSet,
7484 const SMDS_MeshElement* face = 0;
7486 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
7487 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
7489 const SMDS_MeshElement* elem = invElemIt->next();
7490 if (avoidSet.count( elem ))
7492 if ( !elemSet.empty() && !elemSet.count( elem ))
7495 i1 = elem->GetNodeIndex( n1 );
7496 // find a n2 linked to n1
7497 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
7498 for ( int di = -1; di < 2 && !face; di += 2 )
7500 i2 = (i1+di+nbN) % nbN;
7501 if ( elem->GetNode( i2 ) == n2 )
7504 if ( !face && elem->IsQuadratic())
7506 // analysis for quadratic elements using all nodes
7507 const SMDS_QuadraticFaceOfNodes* F =
7508 static_cast<const SMDS_QuadraticFaceOfNodes*>(elem);
7509 // use special nodes iterator
7510 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
7511 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
7512 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
7514 const SMDS_MeshNode* n = cast2Node( anIter->next() );
7515 if ( n1 == prevN && n2 == n )
7519 else if ( n2 == prevN && n1 == n )
7521 face = elem; swap( i1, i2 );
7527 if ( n1ind ) *n1ind = i1;
7528 if ( n2ind ) *n2ind = i2;
7532 //=======================================================================
7533 //function : findAdjacentFace
7535 //=======================================================================
7537 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7538 const SMDS_MeshNode* n2,
7539 const SMDS_MeshElement* elem)
7541 TIDSortedElemSet elemSet, avoidSet;
7543 avoidSet.insert ( elem );
7544 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
7547 //=======================================================================
7548 //function : FindFreeBorder
7550 //=======================================================================
7552 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7554 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7555 const SMDS_MeshNode* theSecondNode,
7556 const SMDS_MeshNode* theLastNode,
7557 list< const SMDS_MeshNode* > & theNodes,
7558 list< const SMDS_MeshElement* >& theFaces)
7560 if ( !theFirstNode || !theSecondNode )
7562 // find border face between theFirstNode and theSecondNode
7563 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7567 theFaces.push_back( curElem );
7568 theNodes.push_back( theFirstNode );
7569 theNodes.push_back( theSecondNode );
7571 //vector<const SMDS_MeshNode*> nodes;
7572 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7573 TIDSortedElemSet foundElems;
7574 bool needTheLast = ( theLastNode != 0 );
7576 while ( nStart != theLastNode ) {
7577 if ( nStart == theFirstNode )
7578 return !needTheLast;
7580 // find all free border faces sharing form nStart
7582 list< const SMDS_MeshElement* > curElemList;
7583 list< const SMDS_MeshNode* > nStartList;
7584 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7585 while ( invElemIt->more() ) {
7586 const SMDS_MeshElement* e = invElemIt->next();
7587 if ( e == curElem || foundElems.insert( e ).second ) {
7589 int iNode = 0, nbNodes = e->NbNodes();
7590 //const SMDS_MeshNode* nodes[nbNodes+1];
7591 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7593 if(e->IsQuadratic()) {
7594 const SMDS_QuadraticFaceOfNodes* F =
7595 static_cast<const SMDS_QuadraticFaceOfNodes*>(e);
7596 // use special nodes iterator
7597 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
7598 while( anIter->more() ) {
7599 nodes[ iNode++ ] = anIter->next();
7603 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7604 while ( nIt->more() )
7605 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7607 nodes[ iNode ] = nodes[ 0 ];
7609 for ( iNode = 0; iNode < nbNodes; iNode++ )
7610 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7611 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7612 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7614 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7615 curElemList.push_back( e );
7619 // analyse the found
7621 int nbNewBorders = curElemList.size();
7622 if ( nbNewBorders == 0 ) {
7623 // no free border furthermore
7624 return !needTheLast;
7626 else if ( nbNewBorders == 1 ) {
7627 // one more element found
7629 nStart = nStartList.front();
7630 curElem = curElemList.front();
7631 theFaces.push_back( curElem );
7632 theNodes.push_back( nStart );
7635 // several continuations found
7636 list< const SMDS_MeshElement* >::iterator curElemIt;
7637 list< const SMDS_MeshNode* >::iterator nStartIt;
7638 // check if one of them reached the last node
7639 if ( needTheLast ) {
7640 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7641 curElemIt!= curElemList.end();
7642 curElemIt++, nStartIt++ )
7643 if ( *nStartIt == theLastNode ) {
7644 theFaces.push_back( *curElemIt );
7645 theNodes.push_back( *nStartIt );
7649 // find the best free border by the continuations
7650 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7651 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7652 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7653 curElemIt!= curElemList.end();
7654 curElemIt++, nStartIt++ )
7656 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7657 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7658 // find one more free border
7659 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7663 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7664 // choice: clear a worse one
7665 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7666 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7667 contNodes[ iWorse ].clear();
7668 contFaces[ iWorse ].clear();
7671 if ( contNodes[0].empty() && contNodes[1].empty() )
7674 // append the best free border
7675 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7676 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7677 theNodes.pop_back(); // remove nIgnore
7678 theNodes.pop_back(); // remove nStart
7679 theFaces.pop_back(); // remove curElem
7680 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7681 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7682 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7683 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7686 } // several continuations found
7687 } // while ( nStart != theLastNode )
7692 //=======================================================================
7693 //function : CheckFreeBorderNodes
7694 //purpose : Return true if the tree nodes are on a free border
7695 //=======================================================================
7697 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7698 const SMDS_MeshNode* theNode2,
7699 const SMDS_MeshNode* theNode3)
7701 list< const SMDS_MeshNode* > nodes;
7702 list< const SMDS_MeshElement* > faces;
7703 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7706 //=======================================================================
7707 //function : SewFreeBorder
7709 //=======================================================================
7711 SMESH_MeshEditor::Sew_Error
7712 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7713 const SMDS_MeshNode* theBordSecondNode,
7714 const SMDS_MeshNode* theBordLastNode,
7715 const SMDS_MeshNode* theSideFirstNode,
7716 const SMDS_MeshNode* theSideSecondNode,
7717 const SMDS_MeshNode* theSideThirdNode,
7718 const bool theSideIsFreeBorder,
7719 const bool toCreatePolygons,
7720 const bool toCreatePolyedrs)
7722 myLastCreatedElems.Clear();
7723 myLastCreatedNodes.Clear();
7725 MESSAGE("::SewFreeBorder()");
7726 Sew_Error aResult = SEW_OK;
7728 // ====================================
7729 // find side nodes and elements
7730 // ====================================
7732 list< const SMDS_MeshNode* > nSide[ 2 ];
7733 list< const SMDS_MeshElement* > eSide[ 2 ];
7734 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7735 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7739 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7740 nSide[0], eSide[0])) {
7741 MESSAGE(" Free Border 1 not found " );
7742 aResult = SEW_BORDER1_NOT_FOUND;
7744 if (theSideIsFreeBorder) {
7747 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7748 nSide[1], eSide[1])) {
7749 MESSAGE(" Free Border 2 not found " );
7750 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7753 if ( aResult != SEW_OK )
7756 if (!theSideIsFreeBorder) {
7760 // -------------------------------------------------------------------------
7762 // 1. If nodes to merge are not coincident, move nodes of the free border
7763 // from the coord sys defined by the direction from the first to last
7764 // nodes of the border to the correspondent sys of the side 2
7765 // 2. On the side 2, find the links most co-directed with the correspondent
7766 // links of the free border
7767 // -------------------------------------------------------------------------
7769 // 1. Since sewing may brake if there are volumes to split on the side 2,
7770 // we wont move nodes but just compute new coordinates for them
7771 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7772 TNodeXYZMap nBordXYZ;
7773 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7774 list< const SMDS_MeshNode* >::iterator nBordIt;
7776 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7777 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7778 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7779 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7780 double tol2 = 1.e-8;
7781 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7782 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7783 // Need node movement.
7785 // find X and Z axes to create trsf
7786 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7788 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7790 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7793 gp_Ax3 toBordAx( Pb1, Zb, X );
7794 gp_Ax3 fromSideAx( Ps1, Zs, X );
7795 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7797 gp_Trsf toBordSys, fromSide2Sys;
7798 toBordSys.SetTransformation( toBordAx );
7799 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7800 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7803 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7804 const SMDS_MeshNode* n = *nBordIt;
7805 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7806 toBordSys.Transforms( xyz );
7807 fromSide2Sys.Transforms( xyz );
7808 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7812 // just insert nodes XYZ in the nBordXYZ map
7813 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7814 const SMDS_MeshNode* n = *nBordIt;
7815 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7819 // 2. On the side 2, find the links most co-directed with the correspondent
7820 // links of the free border
7822 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7823 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7824 sideNodes.push_back( theSideFirstNode );
7826 bool hasVolumes = false;
7827 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7828 set<long> foundSideLinkIDs, checkedLinkIDs;
7829 SMDS_VolumeTool volume;
7830 //const SMDS_MeshNode* faceNodes[ 4 ];
7832 const SMDS_MeshNode* sideNode;
7833 const SMDS_MeshElement* sideElem;
7834 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7835 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7836 nBordIt = bordNodes.begin();
7838 // border node position and border link direction to compare with
7839 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7840 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7841 // choose next side node by link direction or by closeness to
7842 // the current border node:
7843 bool searchByDir = ( *nBordIt != theBordLastNode );
7845 // find the next node on the Side 2
7847 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7849 checkedLinkIDs.clear();
7850 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7852 // loop on inverse elements of current node (prevSideNode) on the Side 2
7853 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7854 while ( invElemIt->more() )
7856 const SMDS_MeshElement* elem = invElemIt->next();
7857 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7858 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7859 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7860 bool isVolume = volume.Set( elem );
7861 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7862 if ( isVolume ) // --volume
7864 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7865 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7866 if(elem->IsQuadratic()) {
7867 const SMDS_QuadraticFaceOfNodes* F =
7868 static_cast<const SMDS_QuadraticFaceOfNodes*>(elem);
7869 // use special nodes iterator
7870 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
7871 while( anIter->more() ) {
7872 nodes[ iNode ] = anIter->next();
7873 if ( nodes[ iNode++ ] == prevSideNode )
7874 iPrevNode = iNode - 1;
7878 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7879 while ( nIt->more() ) {
7880 nodes[ iNode ] = cast2Node( nIt->next() );
7881 if ( nodes[ iNode++ ] == prevSideNode )
7882 iPrevNode = iNode - 1;
7885 // there are 2 links to check
7890 // loop on links, to be precise, on the second node of links
7891 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7892 const SMDS_MeshNode* n = nodes[ iNode ];
7894 if ( !volume.IsLinked( n, prevSideNode ))
7898 if ( iNode ) // a node before prevSideNode
7899 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7900 else // a node after prevSideNode
7901 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7903 // check if this link was already used
7904 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7905 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7906 if (!isJustChecked &&
7907 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7909 // test a link geometrically
7910 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7911 bool linkIsBetter = false;
7912 double dot = 0.0, dist = 0.0;
7913 if ( searchByDir ) { // choose most co-directed link
7914 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7915 linkIsBetter = ( dot > maxDot );
7917 else { // choose link with the node closest to bordPos
7918 dist = ( nextXYZ - bordPos ).SquareModulus();
7919 linkIsBetter = ( dist < minDist );
7921 if ( linkIsBetter ) {
7930 } // loop on inverse elements of prevSideNode
7933 MESSAGE(" Cant find path by links of the Side 2 ");
7934 return SEW_BAD_SIDE_NODES;
7936 sideNodes.push_back( sideNode );
7937 sideElems.push_back( sideElem );
7938 foundSideLinkIDs.insert ( linkID );
7939 prevSideNode = sideNode;
7941 if ( *nBordIt == theBordLastNode )
7942 searchByDir = false;
7944 // find the next border link to compare with
7945 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7946 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7947 // move to next border node if sideNode is before forward border node (bordPos)
7948 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7949 prevBordNode = *nBordIt;
7951 bordPos = nBordXYZ[ *nBordIt ];
7952 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7953 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7957 while ( sideNode != theSideSecondNode );
7959 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7960 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7961 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7963 } // end nodes search on the side 2
7965 // ============================
7966 // sew the border to the side 2
7967 // ============================
7969 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7970 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7972 TListOfListOfNodes nodeGroupsToMerge;
7973 if ( nbNodes[0] == nbNodes[1] ||
7974 ( theSideIsFreeBorder && !theSideThirdNode)) {
7976 // all nodes are to be merged
7978 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7979 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7980 nIt[0]++, nIt[1]++ )
7982 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7983 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7984 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7989 // insert new nodes into the border and the side to get equal nb of segments
7991 // get normalized parameters of nodes on the borders
7992 //double param[ 2 ][ maxNbNodes ];
7994 param[0] = new double [ maxNbNodes ];
7995 param[1] = new double [ maxNbNodes ];
7997 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7998 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7999 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8000 const SMDS_MeshNode* nPrev = *nIt;
8001 double bordLength = 0;
8002 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8003 const SMDS_MeshNode* nCur = *nIt;
8004 gp_XYZ segment (nCur->X() - nPrev->X(),
8005 nCur->Y() - nPrev->Y(),
8006 nCur->Z() - nPrev->Z());
8007 double segmentLen = segment.Modulus();
8008 bordLength += segmentLen;
8009 param[ iBord ][ iNode ] = bordLength;
8012 // normalize within [0,1]
8013 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8014 param[ iBord ][ iNode ] /= bordLength;
8018 // loop on border segments
8019 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8020 int i[ 2 ] = { 0, 0 };
8021 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8022 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8024 TElemOfNodeListMap insertMap;
8025 TElemOfNodeListMap::iterator insertMapIt;
8027 // key: elem to insert nodes into
8028 // value: 2 nodes to insert between + nodes to be inserted
8030 bool next[ 2 ] = { false, false };
8032 // find min adjacent segment length after sewing
8033 double nextParam = 10., prevParam = 0;
8034 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8035 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8036 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8037 if ( i[ iBord ] > 0 )
8038 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8040 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8041 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8042 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8044 // choose to insert or to merge nodes
8045 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8046 if ( Abs( du ) <= minSegLen * 0.2 ) {
8049 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8050 const SMDS_MeshNode* n0 = *nIt[0];
8051 const SMDS_MeshNode* n1 = *nIt[1];
8052 nodeGroupsToMerge.back().push_back( n1 );
8053 nodeGroupsToMerge.back().push_back( n0 );
8054 // position of node of the border changes due to merge
8055 param[ 0 ][ i[0] ] += du;
8056 // move n1 for the sake of elem shape evaluation during insertion.
8057 // n1 will be removed by MergeNodes() anyway
8058 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8059 next[0] = next[1] = true;
8064 int intoBord = ( du < 0 ) ? 0 : 1;
8065 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8066 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8067 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8068 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8069 if ( intoBord == 1 ) {
8070 // move node of the border to be on a link of elem of the side
8071 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8072 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8073 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8074 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8075 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8077 insertMapIt = insertMap.find( elem );
8078 bool notFound = ( insertMapIt == insertMap.end() );
8079 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8081 // insert into another link of the same element:
8082 // 1. perform insertion into the other link of the elem
8083 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8084 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8085 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8086 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8087 // 2. perform insertion into the link of adjacent faces
8089 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8091 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8095 if (toCreatePolyedrs) {
8096 // perform insertion into the links of adjacent volumes
8097 UpdateVolumes(n12, n22, nodeList);
8099 // 3. find an element appeared on n1 and n2 after the insertion
8100 insertMap.erase( elem );
8101 elem = findAdjacentFace( n1, n2, 0 );
8103 if ( notFound || otherLink ) {
8104 // add element and nodes of the side into the insertMap
8105 insertMapIt = insertMap.insert
8106 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8107 (*insertMapIt).second.push_back( n1 );
8108 (*insertMapIt).second.push_back( n2 );
8110 // add node to be inserted into elem
8111 (*insertMapIt).second.push_back( nIns );
8112 next[ 1 - intoBord ] = true;
8115 // go to the next segment
8116 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8117 if ( next[ iBord ] ) {
8118 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8120 nPrev[ iBord ] = *nIt[ iBord ];
8121 nIt[ iBord ]++; i[ iBord ]++;
8125 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8127 // perform insertion of nodes into elements
8129 for (insertMapIt = insertMap.begin();
8130 insertMapIt != insertMap.end();
8133 const SMDS_MeshElement* elem = (*insertMapIt).first;
8134 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8135 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8136 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8138 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8140 if ( !theSideIsFreeBorder ) {
8141 // look for and insert nodes into the faces adjacent to elem
8143 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8145 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8150 if (toCreatePolyedrs) {
8151 // perform insertion into the links of adjacent volumes
8152 UpdateVolumes(n1, n2, nodeList);
8158 } // end: insert new nodes
8160 MergeNodes ( nodeGroupsToMerge );
8165 //=======================================================================
8166 //function : InsertNodesIntoLink
8167 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8168 // and theBetweenNode2 and split theElement
8169 //=======================================================================
8171 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8172 const SMDS_MeshNode* theBetweenNode1,
8173 const SMDS_MeshNode* theBetweenNode2,
8174 list<const SMDS_MeshNode*>& theNodesToInsert,
8175 const bool toCreatePoly)
8177 if ( theFace->GetType() != SMDSAbs_Face ) return;
8179 // find indices of 2 link nodes and of the rest nodes
8180 int iNode = 0, il1, il2, i3, i4;
8181 il1 = il2 = i3 = i4 = -1;
8182 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8183 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8185 if(theFace->IsQuadratic()) {
8186 const SMDS_QuadraticFaceOfNodes* F =
8187 static_cast<const SMDS_QuadraticFaceOfNodes*>(theFace);
8188 // use special nodes iterator
8189 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
8190 while( anIter->more() ) {
8191 const SMDS_MeshNode* n = anIter->next();
8192 if ( n == theBetweenNode1 )
8194 else if ( n == theBetweenNode2 )
8200 nodes[ iNode++ ] = n;
8204 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8205 while ( nodeIt->more() ) {
8206 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8207 if ( n == theBetweenNode1 )
8209 else if ( n == theBetweenNode2 )
8215 nodes[ iNode++ ] = n;
8218 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8221 // arrange link nodes to go one after another regarding the face orientation
8222 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8223 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8228 aNodesToInsert.reverse();
8230 // check that not link nodes of a quadrangles are in good order
8231 int nbFaceNodes = theFace->NbNodes();
8232 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8238 if (toCreatePoly || theFace->IsPoly()) {
8241 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8243 // add nodes of face up to first node of link
8246 if(theFace->IsQuadratic()) {
8247 const SMDS_QuadraticFaceOfNodes* F =
8248 static_cast<const SMDS_QuadraticFaceOfNodes*>(theFace);
8249 // use special nodes iterator
8250 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
8251 while( anIter->more() && !isFLN ) {
8252 const SMDS_MeshNode* n = anIter->next();
8253 poly_nodes[iNode++] = n;
8254 if (n == nodes[il1]) {
8258 // add nodes to insert
8259 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8260 for (; nIt != aNodesToInsert.end(); nIt++) {
8261 poly_nodes[iNode++] = *nIt;
8263 // add nodes of face starting from last node of link
8264 while ( anIter->more() ) {
8265 poly_nodes[iNode++] = anIter->next();
8269 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8270 while ( nodeIt->more() && !isFLN ) {
8271 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8272 poly_nodes[iNode++] = n;
8273 if (n == nodes[il1]) {
8277 // add nodes to insert
8278 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8279 for (; nIt != aNodesToInsert.end(); nIt++) {
8280 poly_nodes[iNode++] = *nIt;
8282 // add nodes of face starting from last node of link
8283 while ( nodeIt->more() ) {
8284 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8285 poly_nodes[iNode++] = n;
8289 // edit or replace the face
8290 SMESHDS_Mesh *aMesh = GetMeshDS();
8292 if (theFace->IsPoly()) {
8293 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8296 int aShapeId = FindShape( theFace );
8298 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8299 myLastCreatedElems.Append(newElem);
8300 if ( aShapeId && newElem )
8301 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8303 aMesh->RemoveElement(theFace);
8308 if( !theFace->IsQuadratic() ) {
8310 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8311 int nbLinkNodes = 2 + aNodesToInsert.size();
8312 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8313 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8314 linkNodes[ 0 ] = nodes[ il1 ];
8315 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8316 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8317 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8318 linkNodes[ iNode++ ] = *nIt;
8320 // decide how to split a quadrangle: compare possible variants
8321 // and choose which of splits to be a quadrangle
8322 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8323 if ( nbFaceNodes == 3 ) {
8324 iBestQuad = nbSplits;
8327 else if ( nbFaceNodes == 4 ) {
8328 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8329 double aBestRate = DBL_MAX;
8330 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8332 double aBadRate = 0;
8333 // evaluate elements quality
8334 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8335 if ( iSplit == iQuad ) {
8336 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8340 aBadRate += getBadRate( &quad, aCrit );
8343 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8345 nodes[ iSplit < iQuad ? i4 : i3 ]);
8346 aBadRate += getBadRate( &tria, aCrit );
8350 if ( aBadRate < aBestRate ) {
8352 aBestRate = aBadRate;
8357 // create new elements
8358 SMESHDS_Mesh *aMesh = GetMeshDS();
8359 int aShapeId = FindShape( theFace );
8362 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8363 SMDS_MeshElement* newElem = 0;
8364 if ( iSplit == iBestQuad )
8365 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8370 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8372 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8373 myLastCreatedElems.Append(newElem);
8374 if ( aShapeId && newElem )
8375 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8378 // change nodes of theFace
8379 const SMDS_MeshNode* newNodes[ 4 ];
8380 newNodes[ 0 ] = linkNodes[ i1 ];
8381 newNodes[ 1 ] = linkNodes[ i2 ];
8382 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8383 newNodes[ 3 ] = nodes[ i4 ];
8384 aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8385 } // end if(!theFace->IsQuadratic())
8386 else { // theFace is quadratic
8387 // we have to split theFace on simple triangles and one simple quadrangle
8389 int nbshift = tmp*2;
8390 // shift nodes in nodes[] by nbshift
8392 for(i=0; i<nbshift; i++) {
8393 const SMDS_MeshNode* n = nodes[0];
8394 for(j=0; j<nbFaceNodes-1; j++) {
8395 nodes[j] = nodes[j+1];
8397 nodes[nbFaceNodes-1] = n;
8399 il1 = il1 - nbshift;
8400 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8401 // n0 n1 n2 n0 n1 n2
8402 // +-----+-----+ +-----+-----+
8411 // create new elements
8412 SMESHDS_Mesh *aMesh = GetMeshDS();
8413 int aShapeId = FindShape( theFace );
8416 if(nbFaceNodes==6) { // quadratic triangle
8417 SMDS_MeshElement* newElem =
8418 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8419 myLastCreatedElems.Append(newElem);
8420 if ( aShapeId && newElem )
8421 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8422 if(theFace->IsMediumNode(nodes[il1])) {
8423 // create quadrangle
8424 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8425 myLastCreatedElems.Append(newElem);
8426 if ( aShapeId && newElem )
8427 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8433 // create quadrangle
8434 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8435 myLastCreatedElems.Append(newElem);
8436 if ( aShapeId && newElem )
8437 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8443 else { // nbFaceNodes==8 - quadratic quadrangle
8444 SMDS_MeshElement* newElem =
8445 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8446 myLastCreatedElems.Append(newElem);
8447 if ( aShapeId && newElem )
8448 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8449 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8450 myLastCreatedElems.Append(newElem);
8451 if ( aShapeId && newElem )
8452 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8453 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8454 myLastCreatedElems.Append(newElem);
8455 if ( aShapeId && newElem )
8456 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8457 if(theFace->IsMediumNode(nodes[il1])) {
8458 // create quadrangle
8459 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8460 myLastCreatedElems.Append(newElem);
8461 if ( aShapeId && newElem )
8462 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8468 // create quadrangle
8469 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8470 myLastCreatedElems.Append(newElem);
8471 if ( aShapeId && newElem )
8472 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8478 // create needed triangles using n1,n2,n3 and inserted nodes
8479 int nbn = 2 + aNodesToInsert.size();
8480 //const SMDS_MeshNode* aNodes[nbn];
8481 vector<const SMDS_MeshNode*> aNodes(nbn);
8482 aNodes[0] = nodes[n1];
8483 aNodes[nbn-1] = nodes[n2];
8484 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8485 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8486 aNodes[iNode++] = *nIt;
8488 for(i=1; i<nbn; i++) {
8489 SMDS_MeshElement* newElem =
8490 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8491 myLastCreatedElems.Append(newElem);
8492 if ( aShapeId && newElem )
8493 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8495 // remove old quadratic face
8496 aMesh->RemoveElement(theFace);
8500 //=======================================================================
8501 //function : UpdateVolumes
8503 //=======================================================================
8504 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8505 const SMDS_MeshNode* theBetweenNode2,
8506 list<const SMDS_MeshNode*>& theNodesToInsert)
8508 myLastCreatedElems.Clear();
8509 myLastCreatedNodes.Clear();
8511 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8512 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8513 const SMDS_MeshElement* elem = invElemIt->next();
8515 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8516 SMDS_VolumeTool aVolume (elem);
8517 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8520 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8521 int iface, nbFaces = aVolume.NbFaces();
8522 vector<const SMDS_MeshNode *> poly_nodes;
8523 vector<int> quantities (nbFaces);
8525 for (iface = 0; iface < nbFaces; iface++) {
8526 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8527 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8528 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8530 for (int inode = 0; inode < nbFaceNodes; inode++) {
8531 poly_nodes.push_back(faceNodes[inode]);
8533 if (nbInserted == 0) {
8534 if (faceNodes[inode] == theBetweenNode1) {
8535 if (faceNodes[inode + 1] == theBetweenNode2) {
8536 nbInserted = theNodesToInsert.size();
8538 // add nodes to insert
8539 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8540 for (; nIt != theNodesToInsert.end(); nIt++) {
8541 poly_nodes.push_back(*nIt);
8545 else if (faceNodes[inode] == theBetweenNode2) {
8546 if (faceNodes[inode + 1] == theBetweenNode1) {
8547 nbInserted = theNodesToInsert.size();
8549 // add nodes to insert in reversed order
8550 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8552 for (; nIt != theNodesToInsert.begin(); nIt--) {
8553 poly_nodes.push_back(*nIt);
8555 poly_nodes.push_back(*nIt);
8562 quantities[iface] = nbFaceNodes + nbInserted;
8565 // Replace or update the volume
8566 SMESHDS_Mesh *aMesh = GetMeshDS();
8568 if (elem->IsPoly()) {
8569 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8573 int aShapeId = FindShape( elem );
8575 SMDS_MeshElement* newElem =
8576 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8577 myLastCreatedElems.Append(newElem);
8578 if (aShapeId && newElem)
8579 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8581 aMesh->RemoveElement(elem);
8586 //=======================================================================
8588 * \brief Convert elements contained in a submesh to quadratic
8589 * \retval int - nb of checked elements
8591 //=======================================================================
8593 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8594 SMESH_MesherHelper& theHelper,
8595 const bool theForce3d)
8598 if( !theSm ) return nbElem;
8600 const bool notFromGroups = false;
8601 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8602 while(ElemItr->more())
8605 const SMDS_MeshElement* elem = ElemItr->next();
8606 if( !elem || elem->IsQuadratic() ) continue;
8608 int id = elem->GetID();
8609 int nbNodes = elem->NbNodes();
8610 vector<const SMDS_MeshNode *> aNds (nbNodes);
8612 for(int i = 0; i < nbNodes; i++)
8614 aNds[i] = elem->GetNode(i);
8616 SMDSAbs_ElementType aType = elem->GetType();
8618 GetMeshDS()->RemoveFreeElement(elem, theSm, notFromGroups);
8620 const SMDS_MeshElement* NewElem = 0;
8626 NewElem = theHelper.AddEdge(aNds[0], aNds[1], id, theForce3d);
8634 NewElem = theHelper.AddFace(aNds[0], aNds[1], aNds[2], id, theForce3d);
8637 NewElem = theHelper.AddFace(aNds[0], aNds[1], aNds[2], aNds[3], id, theForce3d);
8644 case SMDSAbs_Volume :
8649 NewElem = theHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3], id, theForce3d);
8652 NewElem = theHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3], aNds[4], id, theForce3d);
8655 NewElem = theHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3], aNds[4], aNds[5], id, theForce3d);
8658 NewElem = theHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3],
8659 aNds[4], aNds[5], aNds[6], aNds[7], id, theForce3d);
8669 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8671 theSm->AddElement( NewElem );
8676 //=======================================================================
8677 //function : ConvertToQuadratic
8679 //=======================================================================
8680 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
8682 SMESHDS_Mesh* meshDS = GetMeshDS();
8684 SMESH_MesherHelper aHelper(*myMesh);
8685 aHelper.SetIsQuadratic( true );
8686 const bool notFromGroups = false;
8688 int nbCheckedElems = 0;
8689 if ( myMesh->HasShapeToMesh() )
8691 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8693 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8694 while ( smIt->more() ) {
8695 SMESH_subMesh* sm = smIt->next();
8696 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8697 aHelper.SetSubShape( sm->GetSubShape() );
8698 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8703 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8704 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8706 SMESHDS_SubMesh *smDS = 0;
8707 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8708 while(aEdgeItr->more())
8710 const SMDS_MeshEdge* edge = aEdgeItr->next();
8711 if(edge && !edge->IsQuadratic())
8713 int id = edge->GetID();
8714 const SMDS_MeshNode* n1 = edge->GetNode(0);
8715 const SMDS_MeshNode* n2 = edge->GetNode(1);
8717 meshDS->RemoveFreeElement(edge, smDS, notFromGroups);
8719 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8720 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8723 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8724 while(aFaceItr->more())
8726 const SMDS_MeshFace* face = aFaceItr->next();
8727 if(!face || face->IsQuadratic() ) continue;
8729 int id = face->GetID();
8730 int nbNodes = face->NbNodes();
8731 vector<const SMDS_MeshNode *> aNds (nbNodes);
8733 for(int i = 0; i < nbNodes; i++)
8735 aNds[i] = face->GetNode(i);
8738 meshDS->RemoveFreeElement(face, smDS, notFromGroups);
8740 SMDS_MeshFace * NewFace = 0;
8744 NewFace = aHelper.AddFace(aNds[0], aNds[1], aNds[2], id, theForce3d);
8747 NewFace = aHelper.AddFace(aNds[0], aNds[1], aNds[2], aNds[3], id, theForce3d);
8752 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8754 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8755 while(aVolumeItr->more())
8757 const SMDS_MeshVolume* volume = aVolumeItr->next();
8758 if(!volume || volume->IsQuadratic() ) continue;
8760 int id = volume->GetID();
8761 int nbNodes = volume->NbNodes();
8762 vector<const SMDS_MeshNode *> aNds (nbNodes);
8764 for(int i = 0; i < nbNodes; i++)
8766 aNds[i] = volume->GetNode(i);
8769 meshDS->RemoveFreeElement(volume, smDS, notFromGroups);
8771 SMDS_MeshVolume * NewVolume = 0;
8775 NewVolume = aHelper.AddVolume(aNds[0], aNds[1], aNds[2],
8776 aNds[3], id, theForce3d );
8779 NewVolume = aHelper.AddVolume(aNds[0], aNds[1], aNds[2],
8780 aNds[3], aNds[4], id, theForce3d);
8783 NewVolume = aHelper.AddVolume(aNds[0], aNds[1], aNds[2],
8784 aNds[3], aNds[4], aNds[5], id, theForce3d);
8787 NewVolume = aHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3],
8788 aNds[4], aNds[5], aNds[6], aNds[7], id, theForce3d);
8793 ReplaceElemInGroups(volume, NewVolume, meshDS);
8796 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
8797 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8798 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8799 aHelper.FixQuadraticElements();
8803 //=======================================================================
8805 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
8806 * \retval int - nb of checked elements
8808 //=======================================================================
8810 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
8811 SMDS_ElemIteratorPtr theItr,
8812 const int theShapeID)
8815 SMESHDS_Mesh* meshDS = GetMeshDS();
8816 const bool notFromGroups = false;
8818 while( theItr->more() )
8820 const SMDS_MeshElement* elem = theItr->next();
8822 if( elem && elem->IsQuadratic())
8824 int id = elem->GetID();
8825 int nbNodes = elem->NbNodes();
8826 vector<const SMDS_MeshNode *> aNds, mediumNodes;
8827 aNds.reserve( nbNodes );
8828 mediumNodes.reserve( nbNodes );
8830 for(int i = 0; i < nbNodes; i++)
8832 const SMDS_MeshNode* n = elem->GetNode(i);
8834 if( elem->IsMediumNode( n ) )
8835 mediumNodes.push_back( n );
8837 aNds.push_back( n );
8839 if( aNds.empty() ) continue;
8840 SMDSAbs_ElementType aType = elem->GetType();
8842 //remove old quadratic element
8843 meshDS->RemoveFreeElement( elem, theSm, notFromGroups );
8845 SMDS_MeshElement * NewElem = AddElement( aNds, aType, false, id );
8846 ReplaceElemInGroups(elem, NewElem, meshDS);
8847 if( theSm && NewElem )
8848 theSm->AddElement( NewElem );
8850 // remove medium nodes
8851 vector<const SMDS_MeshNode*>::iterator nIt = mediumNodes.begin();
8852 for ( ; nIt != mediumNodes.end(); ++nIt ) {
8853 const SMDS_MeshNode* n = *nIt;
8854 if ( n->NbInverseElements() == 0 ) {
8855 if ( n->GetPosition()->GetShapeId() != theShapeID )
8856 meshDS->RemoveFreeNode( n, meshDS->MeshElements
8857 ( n->GetPosition()->GetShapeId() ));
8859 meshDS->RemoveFreeNode( n, theSm );
8867 //=======================================================================
8868 //function : ConvertFromQuadratic
8870 //=======================================================================
8871 bool SMESH_MeshEditor::ConvertFromQuadratic()
8873 int nbCheckedElems = 0;
8874 if ( myMesh->HasShapeToMesh() )
8876 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8878 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8879 while ( smIt->more() ) {
8880 SMESH_subMesh* sm = smIt->next();
8881 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
8882 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
8888 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
8889 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8891 SMESHDS_SubMesh *aSM = 0;
8892 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
8898 //=======================================================================
8899 //function : SewSideElements
8901 //=======================================================================
8903 SMESH_MeshEditor::Sew_Error
8904 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
8905 TIDSortedElemSet& theSide2,
8906 const SMDS_MeshNode* theFirstNode1,
8907 const SMDS_MeshNode* theFirstNode2,
8908 const SMDS_MeshNode* theSecondNode1,
8909 const SMDS_MeshNode* theSecondNode2)
8911 myLastCreatedElems.Clear();
8912 myLastCreatedNodes.Clear();
8914 MESSAGE ("::::SewSideElements()");
8915 if ( theSide1.size() != theSide2.size() )
8916 return SEW_DIFF_NB_OF_ELEMENTS;
8918 Sew_Error aResult = SEW_OK;
8920 // 1. Build set of faces representing each side
8921 // 2. Find which nodes of the side 1 to merge with ones on the side 2
8922 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
8924 // =======================================================================
8925 // 1. Build set of faces representing each side:
8926 // =======================================================================
8927 // a. build set of nodes belonging to faces
8928 // b. complete set of faces: find missing fices whose nodes are in set of nodes
8929 // c. create temporary faces representing side of volumes if correspondent
8930 // face does not exist
8932 SMESHDS_Mesh* aMesh = GetMeshDS();
8933 SMDS_Mesh aTmpFacesMesh;
8934 set<const SMDS_MeshElement*> faceSet1, faceSet2;
8935 set<const SMDS_MeshElement*> volSet1, volSet2;
8936 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
8937 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
8938 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
8939 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
8940 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
8941 int iSide, iFace, iNode;
8943 for ( iSide = 0; iSide < 2; iSide++ ) {
8944 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
8945 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
8946 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
8947 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
8948 set<const SMDS_MeshElement*>::iterator vIt;
8949 TIDSortedElemSet::iterator eIt;
8950 set<const SMDS_MeshNode*>::iterator nIt;
8952 // check that given nodes belong to given elements
8953 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
8954 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
8955 int firstIndex = -1, secondIndex = -1;
8956 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8957 const SMDS_MeshElement* elem = *eIt;
8958 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
8959 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
8960 if ( firstIndex > -1 && secondIndex > -1 ) break;
8962 if ( firstIndex < 0 || secondIndex < 0 ) {
8963 // we can simply return until temporary faces created
8964 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
8967 // -----------------------------------------------------------
8968 // 1a. Collect nodes of existing faces
8969 // and build set of face nodes in order to detect missing
8970 // faces corresponing to sides of volumes
8971 // -----------------------------------------------------------
8973 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
8975 // loop on the given element of a side
8976 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8977 //const SMDS_MeshElement* elem = *eIt;
8978 const SMDS_MeshElement* elem = *eIt;
8979 if ( elem->GetType() == SMDSAbs_Face ) {
8980 faceSet->insert( elem );
8981 set <const SMDS_MeshNode*> faceNodeSet;
8982 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
8983 while ( nodeIt->more() ) {
8984 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8985 nodeSet->insert( n );
8986 faceNodeSet.insert( n );
8988 setOfFaceNodeSet.insert( faceNodeSet );
8990 else if ( elem->GetType() == SMDSAbs_Volume )
8991 volSet->insert( elem );
8993 // ------------------------------------------------------------------------------
8994 // 1b. Complete set of faces: find missing fices whose nodes are in set of nodes
8995 // ------------------------------------------------------------------------------
8997 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
8998 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
8999 while ( fIt->more() ) { // loop on faces sharing a node
9000 const SMDS_MeshElement* f = fIt->next();
9001 if ( faceSet->find( f ) == faceSet->end() ) {
9002 // check if all nodes are in nodeSet and
9003 // complete setOfFaceNodeSet if they are
9004 set <const SMDS_MeshNode*> faceNodeSet;
9005 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9006 bool allInSet = true;
9007 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9008 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9009 if ( nodeSet->find( n ) == nodeSet->end() )
9012 faceNodeSet.insert( n );
9015 faceSet->insert( f );
9016 setOfFaceNodeSet.insert( faceNodeSet );
9022 // -------------------------------------------------------------------------
9023 // 1c. Create temporary faces representing sides of volumes if correspondent
9024 // face does not exist
9025 // -------------------------------------------------------------------------
9027 if ( !volSet->empty() ) {
9028 //int nodeSetSize = nodeSet->size();
9030 // loop on given volumes
9031 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9032 SMDS_VolumeTool vol (*vIt);
9033 // loop on volume faces: find free faces
9034 // --------------------------------------
9035 list<const SMDS_MeshElement* > freeFaceList;
9036 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9037 if ( !vol.IsFreeFace( iFace ))
9039 // check if there is already a face with same nodes in a face set
9040 const SMDS_MeshElement* aFreeFace = 0;
9041 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9042 int nbNodes = vol.NbFaceNodes( iFace );
9043 set <const SMDS_MeshNode*> faceNodeSet;
9044 vol.GetFaceNodes( iFace, faceNodeSet );
9045 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9047 // no such a face is given but it still can exist, check it
9048 if ( nbNodes == 3 ) {
9049 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9051 else if ( nbNodes == 4 ) {
9052 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9055 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9056 aFreeFace = aMesh->FindFace(poly_nodes);
9060 // create a temporary face
9061 if ( nbNodes == 3 ) {
9062 aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9064 else if ( nbNodes == 4 ) {
9065 aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9068 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9069 aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9073 freeFaceList.push_back( aFreeFace );
9075 } // loop on faces of a volume
9077 // choose one of several free faces
9078 // --------------------------------------
9079 if ( freeFaceList.size() > 1 ) {
9080 // choose a face having max nb of nodes shared by other elems of a side
9081 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9082 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9083 while ( fIt != freeFaceList.end() ) { // loop on free faces
9084 int nbSharedNodes = 0;
9085 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9086 while ( nodeIt->more() ) { // loop on free face nodes
9087 const SMDS_MeshNode* n =
9088 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9089 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9090 while ( invElemIt->more() ) {
9091 const SMDS_MeshElement* e = invElemIt->next();
9092 if ( faceSet->find( e ) != faceSet->end() )
9094 if ( elemSet->find( e ) != elemSet->end() )
9098 if ( nbSharedNodes >= maxNbNodes ) {
9099 maxNbNodes = nbSharedNodes;
9103 freeFaceList.erase( fIt++ ); // here fIt++ occures before erase
9105 if ( freeFaceList.size() > 1 )
9107 // could not choose one face, use another way
9108 // choose a face most close to the bary center of the opposite side
9109 gp_XYZ aBC( 0., 0., 0. );
9110 set <const SMDS_MeshNode*> addedNodes;
9111 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9112 eIt = elemSet2->begin();
9113 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9114 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9115 while ( nodeIt->more() ) { // loop on free face nodes
9116 const SMDS_MeshNode* n =
9117 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9118 if ( addedNodes.insert( n ).second )
9119 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9122 aBC /= addedNodes.size();
9123 double minDist = DBL_MAX;
9124 fIt = freeFaceList.begin();
9125 while ( fIt != freeFaceList.end() ) { // loop on free faces
9127 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9128 while ( nodeIt->more() ) { // loop on free face nodes
9129 const SMDS_MeshNode* n =
9130 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9131 gp_XYZ p( n->X(),n->Y(),n->Z() );
9132 dist += ( aBC - p ).SquareModulus();
9134 if ( dist < minDist ) {
9136 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9139 fIt = freeFaceList.erase( fIt++ );
9142 } // choose one of several free faces of a volume
9144 if ( freeFaceList.size() == 1 ) {
9145 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9146 faceSet->insert( aFreeFace );
9147 // complete a node set with nodes of a found free face
9148 // for ( iNode = 0; iNode < ; iNode++ )
9149 // nodeSet->insert( fNodes[ iNode ] );
9152 } // loop on volumes of a side
9154 // // complete a set of faces if new nodes in a nodeSet appeared
9155 // // ----------------------------------------------------------
9156 // if ( nodeSetSize != nodeSet->size() ) {
9157 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9158 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9159 // while ( fIt->more() ) { // loop on faces sharing a node
9160 // const SMDS_MeshElement* f = fIt->next();
9161 // if ( faceSet->find( f ) == faceSet->end() ) {
9162 // // check if all nodes are in nodeSet and
9163 // // complete setOfFaceNodeSet if they are
9164 // set <const SMDS_MeshNode*> faceNodeSet;
9165 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9166 // bool allInSet = true;
9167 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9168 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9169 // if ( nodeSet->find( n ) == nodeSet->end() )
9170 // allInSet = false;
9172 // faceNodeSet.insert( n );
9174 // if ( allInSet ) {
9175 // faceSet->insert( f );
9176 // setOfFaceNodeSet.insert( faceNodeSet );
9182 } // Create temporary faces, if there are volumes given
9185 if ( faceSet1.size() != faceSet2.size() ) {
9186 // delete temporary faces: they are in reverseElements of actual nodes
9187 SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9188 while ( tmpFaceIt->more() )
9189 aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9190 MESSAGE("Diff nb of faces");
9191 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9194 // ============================================================
9195 // 2. Find nodes to merge:
9196 // bind a node to remove to a node to put instead
9197 // ============================================================
9199 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9200 if ( theFirstNode1 != theFirstNode2 )
9201 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
9202 if ( theSecondNode1 != theSecondNode2 )
9203 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
9205 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9206 set< long > linkIdSet; // links to process
9207 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9209 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9210 list< NLink > linkList[2];
9211 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9212 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9213 // loop on links in linkList; find faces by links and append links
9214 // of the found faces to linkList
9215 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9216 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9217 NLink link[] = { *linkIt[0], *linkIt[1] };
9218 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9219 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
9222 // by links, find faces in the face sets,
9223 // and find indices of link nodes in the found faces;
9224 // in a face set, there is only one or no face sharing a link
9225 // ---------------------------------------------------------------
9227 const SMDS_MeshElement* face[] = { 0, 0 };
9228 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
9229 vector<const SMDS_MeshNode*> fnodes1(9);
9230 vector<const SMDS_MeshNode*> fnodes2(9);
9231 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
9232 vector<const SMDS_MeshNode*> notLinkNodes1(6);
9233 vector<const SMDS_MeshNode*> notLinkNodes2(6);
9234 int iLinkNode[2][2];
9235 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9236 const SMDS_MeshNode* n1 = link[iSide].first;
9237 const SMDS_MeshNode* n2 = link[iSide].second;
9238 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9239 set< const SMDS_MeshElement* > fMap;
9240 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
9241 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
9242 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9243 while ( fIt->more() ) { // loop on faces sharing a node
9244 const SMDS_MeshElement* f = fIt->next();
9245 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9246 ! fMap.insert( f ).second ) // f encounters twice
9248 if ( face[ iSide ] ) {
9249 MESSAGE( "2 faces per link " );
9250 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
9254 faceSet->erase( f );
9255 // get face nodes and find ones of a link
9260 fnodes1.resize(f->NbNodes()+1);
9261 notLinkNodes1.resize(f->NbNodes()-2);
9264 fnodes2.resize(f->NbNodes()+1);
9265 notLinkNodes2.resize(f->NbNodes()-2);
9268 if(!f->IsQuadratic()) {
9269 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
9270 while ( nIt->more() ) {
9271 const SMDS_MeshNode* n =
9272 static_cast<const SMDS_MeshNode*>( nIt->next() );
9274 iLinkNode[ iSide ][ 0 ] = iNode;
9276 else if ( n == n2 ) {
9277 iLinkNode[ iSide ][ 1 ] = iNode;
9279 //else if ( notLinkNodes[ iSide ][ 0 ] )
9280 // notLinkNodes[ iSide ][ 1 ] = n;
9282 // notLinkNodes[ iSide ][ 0 ] = n;
9286 notLinkNodes1[nbl] = n;
9287 //notLinkNodes1.push_back(n);
9289 notLinkNodes2[nbl] = n;
9290 //notLinkNodes2.push_back(n);
9292 //faceNodes[ iSide ][ iNode++ ] = n;
9294 fnodes1[iNode++] = n;
9297 fnodes2[iNode++] = n;
9301 else { // f->IsQuadratic()
9302 const SMDS_QuadraticFaceOfNodes* F =
9303 static_cast<const SMDS_QuadraticFaceOfNodes*>(f);
9304 // use special nodes iterator
9305 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
9306 while ( anIter->more() ) {
9307 const SMDS_MeshNode* n =
9308 static_cast<const SMDS_MeshNode*>( anIter->next() );
9310 iLinkNode[ iSide ][ 0 ] = iNode;
9312 else if ( n == n2 ) {
9313 iLinkNode[ iSide ][ 1 ] = iNode;
9318 notLinkNodes1[nbl] = n;
9321 notLinkNodes2[nbl] = n;
9325 fnodes1[iNode++] = n;
9328 fnodes2[iNode++] = n;
9332 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
9334 fnodes1[iNode] = fnodes1[0];
9337 fnodes2[iNode] = fnodes1[0];
9344 // check similarity of elements of the sides
9345 if (aResult == SEW_OK && ( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9346 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9347 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9348 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9351 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9353 break; // do not return because it s necessary to remove tmp faces
9356 // set nodes to merge
9357 // -------------------
9359 if ( face[0] && face[1] ) {
9360 int nbNodes = face[0]->NbNodes();
9361 if ( nbNodes != face[1]->NbNodes() ) {
9362 MESSAGE("Diff nb of face nodes");
9363 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9364 break; // do not return because it s necessary to remove tmp faces
9366 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
9367 if ( nbNodes == 3 ) {
9368 //nReplaceMap.insert( TNodeNodeMap::value_type
9369 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
9370 nReplaceMap.insert( TNodeNodeMap::value_type
9371 ( notLinkNodes1[0], notLinkNodes2[0] ));
9374 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9375 // analyse link orientation in faces
9376 int i1 = iLinkNode[ iSide ][ 0 ];
9377 int i2 = iLinkNode[ iSide ][ 1 ];
9378 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9379 // if notLinkNodes are the first and the last ones, then
9380 // their order does not correspond to the link orientation
9381 if (( i1 == 1 && i2 == 2 ) ||
9382 ( i1 == 2 && i2 == 1 ))
9383 reverse[ iSide ] = !reverse[ iSide ];
9385 if ( reverse[0] == reverse[1] ) {
9386 //nReplaceMap.insert( TNodeNodeMap::value_type
9387 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
9388 //nReplaceMap.insert( TNodeNodeMap::value_type
9389 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
9390 for(int nn=0; nn<nbNodes-2; nn++) {
9391 nReplaceMap.insert( TNodeNodeMap::value_type
9392 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
9396 //nReplaceMap.insert( TNodeNodeMap::value_type
9397 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
9398 //nReplaceMap.insert( TNodeNodeMap::value_type
9399 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
9400 for(int nn=0; nn<nbNodes-2; nn++) {
9401 nReplaceMap.insert( TNodeNodeMap::value_type
9402 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
9407 // add other links of the faces to linkList
9408 // -----------------------------------------
9410 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
9411 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9412 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
9413 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
9414 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9415 if ( !iter_isnew.second ) { // already in a set: no need to process
9416 linkIdSet.erase( iter_isnew.first );
9418 else // new in set == encountered for the first time: add
9420 //const SMDS_MeshNode* n1 = nodes[ iNode ];
9421 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
9422 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
9423 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
9424 linkList[0].push_back ( NLink( n1, n2 ));
9425 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9429 } // loop on link lists
9431 if ( aResult == SEW_OK &&
9432 ( linkIt[0] != linkList[0].end() ||
9433 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9434 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9435 " " << (faceSetPtr[1]->empty()));
9436 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9439 // ====================================================================
9440 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9441 // ====================================================================
9443 // delete temporary faces: they are in reverseElements of actual nodes
9444 SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9445 while ( tmpFaceIt->more() )
9446 aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9448 if ( aResult != SEW_OK)
9451 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9452 // loop on nodes replacement map
9453 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9454 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9455 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9456 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9457 nodeIDsToRemove.push_back( nToRemove->GetID() );
9458 // loop on elements sharing nToRemove
9459 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9460 while ( invElemIt->more() ) {
9461 const SMDS_MeshElement* e = invElemIt->next();
9462 // get a new suite of nodes: make replacement
9463 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9464 vector< const SMDS_MeshNode*> nodes( nbNodes );
9465 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9466 while ( nIt->more() ) {
9467 const SMDS_MeshNode* n =
9468 static_cast<const SMDS_MeshNode*>( nIt->next() );
9469 nnIt = nReplaceMap.find( n );
9470 if ( nnIt != nReplaceMap.end() ) {
9476 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9477 // elemIDsToRemove.push_back( e->GetID() );
9480 aMesh->ChangeElementNodes( e, & nodes[0], nbNodes );
9484 Remove( nodeIDsToRemove, true );
9489 //================================================================================
9491 * \brief Find corresponding nodes in two sets of faces
9492 * \param theSide1 - first face set
9493 * \param theSide2 - second first face
9494 * \param theFirstNode1 - a boundary node of set 1
9495 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9496 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9497 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9498 * \param nReplaceMap - output map of corresponding nodes
9499 * \retval bool - is a success or not
9501 //================================================================================
9504 //#define DEBUG_MATCHING_NODES
9507 SMESH_MeshEditor::Sew_Error
9508 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9509 set<const SMDS_MeshElement*>& theSide2,
9510 const SMDS_MeshNode* theFirstNode1,
9511 const SMDS_MeshNode* theFirstNode2,
9512 const SMDS_MeshNode* theSecondNode1,
9513 const SMDS_MeshNode* theSecondNode2,
9514 TNodeNodeMap & nReplaceMap)
9516 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9518 nReplaceMap.clear();
9519 if ( theFirstNode1 != theFirstNode2 )
9520 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9521 if ( theSecondNode1 != theSecondNode2 )
9522 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9524 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9525 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9527 list< NLink > linkList[2];
9528 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9529 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9531 // loop on links in linkList; find faces by links and append links
9532 // of the found faces to linkList
9533 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9534 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9535 NLink link[] = { *linkIt[0], *linkIt[1] };
9536 if ( linkSet.find( link[0] ) == linkSet.end() )
9539 // by links, find faces in the face sets,
9540 // and find indices of link nodes in the found faces;
9541 // in a face set, there is only one or no face sharing a link
9542 // ---------------------------------------------------------------
9544 const SMDS_MeshElement* face[] = { 0, 0 };
9545 list<const SMDS_MeshNode*> notLinkNodes[2];
9546 //bool reverse[] = { false, false }; // order of notLinkNodes
9548 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9550 const SMDS_MeshNode* n1 = link[iSide].first;
9551 const SMDS_MeshNode* n2 = link[iSide].second;
9552 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9553 set< const SMDS_MeshElement* > facesOfNode1;
9554 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9556 // during a loop of the first node, we find all faces around n1,
9557 // during a loop of the second node, we find one face sharing both n1 and n2
9558 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9559 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9560 while ( fIt->more() ) { // loop on faces sharing a node
9561 const SMDS_MeshElement* f = fIt->next();
9562 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9563 ! facesOfNode1.insert( f ).second ) // f encounters twice
9565 if ( face[ iSide ] ) {
9566 MESSAGE( "2 faces per link " );
9567 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9570 faceSet->erase( f );
9572 // get not link nodes
9573 int nbN = f->NbNodes();
9574 if ( f->IsQuadratic() )
9576 nbNodes[ iSide ] = nbN;
9577 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9578 int i1 = f->GetNodeIndex( n1 );
9579 int i2 = f->GetNodeIndex( n2 );
9580 int iEnd = nbN, iBeg = -1, iDelta = 1;
9581 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9583 std::swap( iEnd, iBeg ); iDelta = -1;
9588 if ( i == iEnd ) i = iBeg + iDelta;
9589 if ( i == i1 ) break;
9590 nodes.push_back ( f->GetNode( i ) );
9596 // check similarity of elements of the sides
9597 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9598 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9599 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9600 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9603 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9607 // set nodes to merge
9608 // -------------------
9610 if ( face[0] && face[1] ) {
9611 if ( nbNodes[0] != nbNodes[1] ) {
9612 MESSAGE("Diff nb of face nodes");
9613 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9615 #ifdef DEBUG_MATCHING_NODES
9616 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9617 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9618 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9620 int nbN = nbNodes[0];
9622 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9623 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9624 for ( int i = 0 ; i < nbN - 2; ++i ) {
9625 #ifdef DEBUG_MATCHING_NODES
9626 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9628 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9632 // add other links of the face 1 to linkList
9633 // -----------------------------------------
9635 const SMDS_MeshElement* f0 = face[0];
9636 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9637 for ( int i = 0; i < nbN; i++ )
9639 const SMDS_MeshNode* n2 = f0->GetNode( i );
9640 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9641 linkSet.insert( SMESH_TLink( n1, n2 ));
9642 if ( !iter_isnew.second ) { // already in a set: no need to process
9643 linkSet.erase( iter_isnew.first );
9645 else // new in set == encountered for the first time: add
9647 #ifdef DEBUG_MATCHING_NODES
9648 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9649 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9651 linkList[0].push_back ( NLink( n1, n2 ));
9652 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9657 } // loop on link lists
9662 //================================================================================
9664 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9665 \param theElems - the list of elements (edges or faces) to be replicated
9666 The nodes for duplication could be found from these elements
9667 \param theNodesNot - list of nodes to NOT replicate
9668 \param theAffectedElems - the list of elements (cells and edges) to which the
9669 replicated nodes should be associated to.
9670 \return TRUE if operation has been completed successfully, FALSE otherwise
9672 //================================================================================
9674 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
9675 const TIDSortedElemSet& theNodesNot,
9676 const TIDSortedElemSet& theAffectedElems )
9678 myLastCreatedElems.Clear();
9679 myLastCreatedNodes.Clear();
9681 if ( theElems.size() == 0 )
9684 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9689 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9690 // duplicate elements and nodes
9691 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
9692 // replce nodes by duplications
9693 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
9697 //================================================================================
9699 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9700 \param theMeshDS - mesh instance
9701 \param theElems - the elements replicated or modified (nodes should be changed)
9702 \param theNodesNot - nodes to NOT replicate
9703 \param theNodeNodeMap - relation of old node to new created node
9704 \param theIsDoubleElem - flag os to replicate element or modify
9705 \return TRUE if operation has been completed successfully, FALSE otherwise
9707 //================================================================================
9709 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
9710 const TIDSortedElemSet& theElems,
9711 const TIDSortedElemSet& theNodesNot,
9712 std::map< const SMDS_MeshNode*,
9713 const SMDS_MeshNode* >& theNodeNodeMap,
9714 const bool theIsDoubleElem )
9716 // iterate on through element and duplicate them (by nodes duplication)
9718 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9719 for ( ; elemItr != theElems.end(); ++elemItr )
9721 const SMDS_MeshElement* anElem = *elemItr;
9725 bool isDuplicate = false;
9726 // duplicate nodes to duplicate element
9727 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
9728 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9730 while ( anIter->more() )
9733 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9734 SMDS_MeshNode* aNewNode = aCurrNode;
9735 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
9736 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
9737 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
9740 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
9741 theNodeNodeMap[ aCurrNode ] = aNewNode;
9742 myLastCreatedNodes.Append( aNewNode );
9744 isDuplicate |= (aCurrNode != aNewNode);
9745 newNodes[ ind++ ] = aNewNode;
9750 if ( theIsDoubleElem )
9751 myLastCreatedElems.Append( AddElement(newNodes, anElem->GetType(), anElem->IsPoly()) );
9753 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
9760 //================================================================================
9762 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9763 \param theNodes - identifiers of nodes to be doubled
9764 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
9765 nodes. If list of element identifiers is empty then nodes are doubled but
9766 they not assigned to elements
9767 \return TRUE if operation has been completed successfully, FALSE otherwise
9769 //================================================================================
9771 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
9772 const std::list< int >& theListOfModifiedElems )
9774 myLastCreatedElems.Clear();
9775 myLastCreatedNodes.Clear();
9777 if ( theListOfNodes.size() == 0 )
9780 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9784 // iterate through nodes and duplicate them
9786 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9788 std::list< int >::const_iterator aNodeIter;
9789 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
9791 int aCurr = *aNodeIter;
9792 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
9798 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
9801 anOldNodeToNewNode[ aNode ] = aNewNode;
9802 myLastCreatedNodes.Append( aNewNode );
9806 // Create map of new nodes for modified elements
9808 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
9810 std::list< int >::const_iterator anElemIter;
9811 for ( anElemIter = theListOfModifiedElems.begin();
9812 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
9814 int aCurr = *anElemIter;
9815 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
9819 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
9821 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9823 while ( anIter->more() )
9825 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9826 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
9828 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
9829 aNodeArr[ ind++ ] = aNewNode;
9832 aNodeArr[ ind++ ] = aCurrNode;
9834 anElemToNodes[ anElem ] = aNodeArr;
9837 // Change nodes of elements
9839 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
9840 anElemToNodesIter = anElemToNodes.begin();
9841 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
9843 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
9844 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
9846 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
9854 //================================================================================
9856 \brief Check if element located inside shape
9857 \return TRUE if IN or ON shape, FALSE otherwise
9859 //================================================================================
9861 template<class Classifier>
9862 bool isInside(const SMDS_MeshElement* theElem,
9863 Classifier& theClassifier,
9864 const double theTol)
9866 gp_XYZ centerXYZ (0, 0, 0);
9867 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
9868 while (aNodeItr->more())
9869 centerXYZ += SMESH_MeshEditor::TNodeXYZ(cast2Node( aNodeItr->next()));
9871 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
9872 theClassifier.Perform(aPnt, theTol);
9873 TopAbs_State aState = theClassifier.State();
9874 return (aState == TopAbs_IN || aState == TopAbs_ON );
9877 //================================================================================
9879 * \brief Classifier of the 3D point on the TopoDS_Face
9880 * with interaface suitable for isInside()
9882 //================================================================================
9884 struct _FaceClassifier
9886 Extrema_ExtPS _extremum;
9887 BRepAdaptor_Surface _surface;
9888 TopAbs_State _state;
9890 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
9892 _extremum.Initialize( _surface,
9893 _surface.FirstUParameter(), _surface.LastUParameter(),
9894 _surface.FirstVParameter(), _surface.LastVParameter(),
9895 _surface.Tolerance(), _surface.Tolerance() );
9897 void Perform(const gp_Pnt& aPnt, double theTol)
9899 _state = TopAbs_OUT;
9900 _extremum.Perform(aPnt);
9901 if ( _extremum.IsDone() )
9902 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
9903 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9905 TopAbs_State State() const
9912 //================================================================================
9914 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9915 \param theElems - group of of elements (edges or faces) to be replicated
9916 \param theNodesNot - group of nodes not to replicate
9917 \param theShape - shape to detect affected elements (element which geometric center
9918 located on or inside shape).
9919 The replicated nodes should be associated to affected elements.
9920 \return TRUE if operation has been completed successfully, FALSE otherwise
9922 //================================================================================
9924 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
9925 const TIDSortedElemSet& theNodesNot,
9926 const TopoDS_Shape& theShape )
9928 if ( theShape.IsNull() )
9931 const double aTol = Precision::Confusion();
9932 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9933 auto_ptr<_FaceClassifier> aFaceClassifier;
9934 if ( theShape.ShapeType() == TopAbs_SOLID )
9936 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9937 bsc3d->PerformInfinitePoint(aTol);
9939 else if (theShape.ShapeType() == TopAbs_FACE )
9941 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9944 // iterates on indicated elements and get elements by back references from their nodes
9945 TIDSortedElemSet anAffected;
9946 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9947 for ( ; elemItr != theElems.end(); ++elemItr )
9949 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9953 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9954 while ( nodeItr->more() )
9956 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9957 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9959 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9960 while ( backElemItr->more() )
9962 const SMDS_MeshElement* curElem = backElemItr->next();
9963 if ( curElem && theElems.find(curElem) == theElems.end() &&
9965 isInside( curElem, *bsc3d, aTol ) :
9966 isInside( curElem, *aFaceClassifier, aTol )))
9967 anAffected.insert( curElem );
9971 return DoubleNodes( theElems, theNodesNot, anAffected );
9974 //================================================================================
9976 * \brief Generated skin mesh (containing 2D cells) from 3D mesh
9977 * The created 2D mesh elements based on nodes of free faces of boundary volumes
9978 * \return TRUE if operation has been completed successfully, FALSE otherwise
9980 //================================================================================
9982 bool SMESH_MeshEditor::Make2DMeshFrom3D()
9984 // iterates on volume elements and detect all free faces on them
9985 SMESHDS_Mesh* aMesh = GetMeshDS();
9989 int nbFree = 0, nbExisted = 0, nbCreated = 0;
9990 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
9993 const SMDS_MeshVolume* volume = vIt->next();
9994 SMDS_VolumeTool vTool( volume );
9995 vTool.SetExternalNormal();
9996 const bool isPoly = volume->IsPoly();
9997 const bool isQuad = volume->IsQuadratic();
9998 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
10000 if (!vTool.IsFreeFace(iface))
10003 vector<const SMDS_MeshNode *> nodes;
10004 int nbFaceNodes = vTool.NbFaceNodes(iface);
10005 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
10007 for ( ; inode < nbFaceNodes; inode += isQuad ? 2 : 1)
10008 nodes.push_back(faceNodes[inode]);
10010 for ( inode = 1; inode < nbFaceNodes; inode += 2)
10011 nodes.push_back(faceNodes[inode]);
10013 // add new face based on volume nodes
10014 if (aMesh->FindFace( nodes ) ) {
10016 continue; // face already exsist
10018 myLastCreatedElems.Append( AddElement(nodes, SMDSAbs_Face, isPoly && iface == 1) );
10022 return ( nbFree==(nbExisted+nbCreated) );