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
1245 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1247 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1248 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1249 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1250 bool hasFacet( const TTriangleFacet& facet ) const
1252 const int* tetConn = _connectivity;
1253 for ( ; tetConn[0] >= 0; tetConn += 4 )
1254 if (( facet.contains( tetConn[0] ) +
1255 facet.contains( tetConn[1] ) +
1256 facet.contains( tetConn[2] ) +
1257 facet.contains( tetConn[3] )) == 3 )
1263 //=======================================================================
1265 * \brief return TSplitMethod for the given element
1267 //=======================================================================
1269 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1271 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1273 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1274 // an edge and a face barycenter; tertaherdons are based on triangles and
1275 // a volume barycenter
1276 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1278 // Find out how adjacent volumes are split
1280 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1281 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1282 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1284 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1285 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1286 if ( nbNodes < 4 ) continue;
1288 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1289 const int* nInd = vol.GetFaceNodesIndices( iF );
1292 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1293 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1294 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1295 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1299 int iCom = 0; // common node of triangle faces to split into
1300 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1302 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1303 nInd[ iQ * ( (iCom+1)%nbNodes )],
1304 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1305 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1306 nInd[ iQ * ( (iCom+2)%nbNodes )],
1307 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1308 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1310 triaSplits.push_back( t012 );
1311 triaSplits.push_back( t023 );
1316 if ( !triaSplits.empty() )
1317 hasAdjacentSplits = true;
1320 // Among variants of split method select one compliant with adjacent volumes
1322 TSplitMethod method;
1323 if ( !vol.Element()->IsPoly() && !is24TetMode )
1325 int nbVariants = 2, nbTet = 0;
1326 const int** connVariants = 0;
1327 switch ( vol.Element()->GetEntityType() )
1329 case SMDSEntity_Hexa:
1330 case SMDSEntity_Quad_Hexa:
1331 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1332 connVariants = theHexTo5, nbTet = 5;
1334 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1336 case SMDSEntity_Pyramid:
1337 case SMDSEntity_Quad_Pyramid:
1338 connVariants = thePyraTo2; nbTet = 2;
1340 case SMDSEntity_Penta:
1341 case SMDSEntity_Quad_Penta:
1342 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1347 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1349 // check method compliancy with adjacent tetras,
1350 // all found splits must be among facets of tetras described by this method
1351 method = TSplitMethod( nbTet, connVariants[variant] );
1352 if ( hasAdjacentSplits && method._nbTetra > 0 )
1354 bool facetCreated = true;
1355 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1357 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1358 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1359 facetCreated = method.hasFacet( *facet );
1361 if ( !facetCreated )
1362 method = TSplitMethod(0); // incompatible method
1366 if ( method._nbTetra < 1 )
1368 // No standard method is applicable, use a generic solution:
1369 // each facet of a volume is split into triangles and
1370 // each of triangles and a volume barycenter form a tetrahedron.
1372 int* connectivity = new int[ maxTetConnSize + 1 ];
1373 method._connectivity = connectivity;
1374 method._ownConn = true;
1375 method._baryNode = true;
1378 int baryCenInd = vol.NbNodes();
1379 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1381 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1382 const int* nInd = vol.GetFaceNodesIndices( iF );
1383 // find common node of triangle facets of tetra to create
1384 int iCommon = 0; // index in linear numeration
1385 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1386 if ( !triaSplits.empty() )
1389 const TTriangleFacet* facet = &triaSplits.front();
1390 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1391 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1392 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1395 else if ( nbNodes > 3 && !is24TetMode )
1397 // find the best method of splitting into triangles by aspect ratio
1398 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1399 map< double, int > badness2iCommon;
1400 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1401 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1402 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1403 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1405 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1406 nodes[ iQ*((iLast-1)%nbNodes)],
1407 nodes[ iQ*((iLast )%nbNodes)]);
1408 double badness = getBadRate( &tria, aspectRatio );
1409 badness2iCommon.insert( make_pair( badness, iCommon ));
1411 // use iCommon with lowest badness
1412 iCommon = badness2iCommon.begin()->second;
1414 if ( iCommon >= nbNodes )
1415 iCommon = 0; // something wrong
1417 // fill connectivity of tetrahedra based on a current face
1418 int nbTet = nbNodes - 2;
1419 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1421 method._faceBaryNode.insert( make_pair( iF, (const SMDS_MeshNode*)0 ));
1422 int faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1424 for ( int i = 0; i < nbTet; ++i )
1426 int i1 = i, i2 = (i+1) % nbNodes;
1427 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1428 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1429 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1430 connectivity[ connSize++ ] = faceBaryCenInd;
1431 connectivity[ connSize++ ] = baryCenInd;
1436 for ( int i = 0; i < nbTet; ++i )
1438 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1439 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1440 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1441 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1442 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1443 connectivity[ connSize++ ] = baryCenInd;
1446 method._nbTetra += nbTet;
1448 connectivity[ connSize++ ] = -1;
1452 //================================================================================
1454 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1456 //================================================================================
1458 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1460 // find the tetrahedron including the three nodes of facet
1461 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1462 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1463 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1464 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1465 while ( volIt1->more() )
1467 const SMDS_MeshElement* v = volIt1->next();
1468 if ( v->GetEntityType() != ( v->IsQuadratic() ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra ))
1470 SMDS_ElemIteratorPtr volIt2 = n2->GetInverseElementIterator(SMDSAbs_Volume);
1471 while ( volIt2->more() )
1472 if ( v != volIt2->next() )
1474 SMDS_ElemIteratorPtr volIt3 = n3->GetInverseElementIterator(SMDSAbs_Volume);
1475 while ( volIt3->more() )
1476 if ( v == volIt3->next() )
1482 //=======================================================================
1484 * \brief A key of a face of volume
1486 //=======================================================================
1488 struct TVolumeFaceKey: pair< int, pair< int, int> >
1490 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1492 TIDSortedNodeSet sortedNodes;
1493 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1494 int nbNodes = vol.NbFaceNodes( iF );
1495 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1496 for ( int i = 0; i < nbNodes; i += iQ )
1497 sortedNodes.insert( fNodes[i] );
1498 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1499 first = (*(n++))->GetID();
1500 second.first = (*(n++))->GetID();
1501 second.second = (*(n++))->GetID();
1506 //=======================================================================
1507 //function : SplitVolumesIntoTetra
1508 //purpose : Split volumic elements into tetrahedra.
1509 //=======================================================================
1511 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1512 const int theMethodFlags)
1514 // std-like iterator on coordinates of nodes of mesh element
1515 typedef SMDS_StdIterator< TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1516 NXyzIterator xyzEnd;
1518 SMDS_VolumeTool volTool;
1519 SMESH_MesherHelper helper( *GetMesh());
1521 SMESHDS_SubMesh* subMesh = GetMeshDS()->MeshElements(1);
1522 SMESHDS_SubMesh* fSubMesh = subMesh;
1524 SMESH_SequenceOfElemPtr newNodes, newElems;
1526 // map face of volume to it's baricenrtic node
1527 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1530 TIDSortedElemSet::const_iterator elem = theElems.begin();
1531 for ( ; elem != theElems.end(); ++elem )
1533 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1534 if ( geomType <= SMDSEntity_Quad_Tetra )
1535 continue; // tetra or face or ...
1537 if ( !volTool.Set( *elem )) continue; // not volume? strange...
1539 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1540 if ( splitMethod._nbTetra < 1 ) continue;
1542 // find submesh to add new tetras to
1543 if ( !subMesh || !subMesh->Contains( *elem ))
1545 int shapeID = FindShape( *elem );
1546 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1547 subMesh = GetMeshDS()->MeshElements( shapeID );
1550 if ( (*elem)->IsQuadratic() )
1553 // add quadratic links to the helper
1554 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1556 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1557 for ( int iN = 0; iN < volTool.NbFaceNodes( iF ); iN += iQ )
1558 helper.AddTLinkNode( fNodes[iF], fNodes[iF+2], fNodes[iF+1] );
1560 helper.SetIsQuadratic( true );
1565 helper.SetIsQuadratic( false );
1567 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1568 if ( splitMethod._baryNode )
1570 // make a node at barycenter
1571 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1572 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1573 nodes.push_back( gcNode );
1574 newNodes.Append( gcNode );
1576 if ( !splitMethod._faceBaryNode.empty() )
1578 // make or find baricentric nodes of faces
1579 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1580 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1582 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1583 volFace2BaryNode.insert
1584 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), (const SMDS_MeshNode*)0) ).first;
1587 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1588 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1590 nodes.push_back( iF_n->second = f_n->second );
1595 helper.SetElementsOnShape( true );
1596 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1597 const int* tetConn = splitMethod._connectivity;
1598 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1599 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1600 nodes[ tetConn[1] ],
1601 nodes[ tetConn[2] ],
1602 nodes[ tetConn[3] ]));
1604 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1606 // Split faces on sides of the split volume
1608 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1609 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1611 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1612 if ( nbNodes < 4 ) continue;
1614 // find an existing face
1615 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1616 volTool.GetFaceNodes( iF ) + nbNodes*iQ );
1617 while ( const SMDS_MeshElement* face = GetMeshDS()->FindFace( fNodes ))
1620 helper.SetElementsOnShape( false );
1621 vector< const SMDS_MeshElement* > triangles;
1623 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1624 if ( iF_n != splitMethod._faceBaryNode.end() )
1626 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1628 const SMDS_MeshNode* n1 = fNodes[iN];
1629 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%nbNodes*iQ];
1630 const SMDS_MeshNode *n3 = iF_n->second;
1631 if ( !volTool.IsFaceExternal( iF ))
1633 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1638 // among possible triangles create ones discribed by split method
1639 const int* nInd = volTool.GetFaceNodesIndices( iF );
1640 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1641 int iCom = 0; // common node of triangle faces to split into
1642 list< TTriangleFacet > facets;
1643 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1645 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1646 nInd[ iQ * ( (iCom+1)%nbNodes )],
1647 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1648 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1649 nInd[ iQ * ( (iCom+2)%nbNodes )],
1650 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1651 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1653 facets.push_back( t012 );
1654 facets.push_back( t023 );
1655 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1656 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1657 nInd[ iQ * ((iLast-1)%nbNodes )],
1658 nInd[ iQ * ((iLast )%nbNodes )]));
1662 list< TTriangleFacet >::iterator facet = facets.begin();
1663 for ( ; facet != facets.end(); ++facet )
1665 if ( !volTool.IsFaceExternal( iF ))
1666 swap( facet->_n2, facet->_n3 );
1667 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1668 volNodes[ facet->_n2 ],
1669 volNodes[ facet->_n3 ]));
1672 // find submesh to add new triangles in
1673 if ( !fSubMesh || !fSubMesh->Contains( face ))
1675 int shapeID = FindShape( face );
1676 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1678 for ( int i = 0; i < triangles.size(); ++i )
1680 if ( !triangles.back() ) continue;
1682 fSubMesh->AddElement( triangles.back());
1683 newElems.Append( triangles.back() );
1685 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1686 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1689 } // loop on volume faces to split them into triangles
1691 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1693 } // loop on volumes to split
1695 myLastCreatedNodes = newNodes;
1696 myLastCreatedElems = newElems;
1699 //=======================================================================
1700 //function : AddToSameGroups
1701 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1702 //=======================================================================
1704 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1705 const SMDS_MeshElement* elemInGroups,
1706 SMESHDS_Mesh * aMesh)
1708 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1709 if (!groups.empty()) {
1710 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1711 for ( ; grIt != groups.end(); grIt++ ) {
1712 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1713 if ( group && group->Contains( elemInGroups ))
1714 group->SMDSGroup().Add( elemToAdd );
1720 //=======================================================================
1721 //function : RemoveElemFromGroups
1722 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1723 //=======================================================================
1724 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1725 SMESHDS_Mesh * aMesh)
1727 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1728 if (!groups.empty())
1730 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1731 for (; GrIt != groups.end(); GrIt++)
1733 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1734 if (!grp || grp->IsEmpty()) continue;
1735 grp->SMDSGroup().Remove(removeelem);
1740 //================================================================================
1742 * \brief Replace elemToRm by elemToAdd in the all groups
1744 //================================================================================
1746 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1747 const SMDS_MeshElement* elemToAdd,
1748 SMESHDS_Mesh * aMesh)
1750 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1751 if (!groups.empty()) {
1752 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1753 for ( ; grIt != groups.end(); grIt++ ) {
1754 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1755 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1756 group->SMDSGroup().Add( elemToAdd );
1761 //================================================================================
1763 * \brief Replace elemToRm by elemToAdd in the all groups
1765 //================================================================================
1767 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1768 const vector<const SMDS_MeshElement*>& elemToAdd,
1769 SMESHDS_Mesh * aMesh)
1771 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1772 if (!groups.empty())
1774 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1775 for ( ; grIt != groups.end(); grIt++ ) {
1776 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1777 if ( group && group->SMDSGroup().Remove( elemToRm ) )
1778 for ( int i = 0; i < elemToAdd.size(); ++i )
1779 group->SMDSGroup().Add( elemToAdd[ i ] );
1784 //=======================================================================
1785 //function : QuadToTri
1786 //purpose : Cut quadrangles into triangles.
1787 // theCrit is used to select a diagonal to cut
1788 //=======================================================================
1790 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1791 const bool the13Diag)
1793 myLastCreatedElems.Clear();
1794 myLastCreatedNodes.Clear();
1796 MESSAGE( "::QuadToTri()" );
1798 SMESHDS_Mesh * aMesh = GetMeshDS();
1800 Handle(Geom_Surface) surface;
1801 SMESH_MesherHelper helper( *GetMesh() );
1803 TIDSortedElemSet::iterator itElem;
1804 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1805 const SMDS_MeshElement* elem = *itElem;
1806 if ( !elem || elem->GetType() != SMDSAbs_Face )
1808 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1809 if(!isquad) continue;
1811 if(elem->NbNodes()==4) {
1812 // retrieve element nodes
1813 const SMDS_MeshNode* aNodes [4];
1814 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1816 while ( itN->more() )
1817 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1819 int aShapeId = FindShape( elem );
1820 const SMDS_MeshElement* newElem = 0;
1822 aMesh->ChangeElementNodes( elem, aNodes, 3 );
1823 newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1826 aMesh->ChangeElementNodes( elem, &aNodes[1], 3 );
1827 newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1829 myLastCreatedElems.Append(newElem);
1830 // put a new triangle on the same shape and add to the same groups
1832 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1833 AddToSameGroups( newElem, elem, aMesh );
1836 // Quadratic quadrangle
1838 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1840 // get surface elem is on
1841 int aShapeId = FindShape( elem );
1842 if ( aShapeId != helper.GetSubShapeID() ) {
1846 shape = aMesh->IndexToShape( aShapeId );
1847 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1848 TopoDS_Face face = TopoDS::Face( shape );
1849 surface = BRep_Tool::Surface( face );
1850 if ( !surface.IsNull() )
1851 helper.SetSubShape( shape );
1855 const SMDS_MeshNode* aNodes [8];
1856 const SMDS_MeshNode* inFaceNode = 0;
1857 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1859 while ( itN->more() ) {
1860 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1861 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
1862 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1864 inFaceNode = aNodes[ i-1 ];
1868 // find middle point for (0,1,2,3)
1869 // and create a node in this point;
1871 if ( surface.IsNull() ) {
1873 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1877 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
1880 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
1882 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1884 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1885 myLastCreatedNodes.Append(newN);
1887 // create a new element
1888 const SMDS_MeshElement* newElem = 0;
1889 const SMDS_MeshNode* N[6];
1897 newElem = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1898 aNodes[6], aNodes[7], newN );
1907 newElem = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1908 aNodes[7], aNodes[4], newN );
1910 myLastCreatedElems.Append(newElem);
1911 aMesh->ChangeElementNodes( elem, N, 6 );
1912 // put a new triangle on the same shape and add to the same groups
1914 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1915 AddToSameGroups( newElem, elem, aMesh );
1922 //=======================================================================
1923 //function : getAngle
1925 //=======================================================================
1927 double getAngle(const SMDS_MeshElement * tr1,
1928 const SMDS_MeshElement * tr2,
1929 const SMDS_MeshNode * n1,
1930 const SMDS_MeshNode * n2)
1932 double angle = 2*PI; // bad angle
1935 SMESH::Controls::TSequenceOfXYZ P1, P2;
1936 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
1937 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
1940 if(!tr1->IsQuadratic())
1941 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
1943 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
1944 if ( N1.SquareMagnitude() <= gp::Resolution() )
1946 if(!tr2->IsQuadratic())
1947 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
1949 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
1950 if ( N2.SquareMagnitude() <= gp::Resolution() )
1953 // find the first diagonal node n1 in the triangles:
1954 // take in account a diagonal link orientation
1955 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
1956 for ( int t = 0; t < 2; t++ ) {
1957 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
1958 int i = 0, iDiag = -1;
1959 while ( it->more()) {
1960 const SMDS_MeshElement *n = it->next();
1961 if ( n == n1 || n == n2 )
1965 if ( i - iDiag == 1 )
1966 nFirst[ t ] = ( n == n1 ? n2 : n1 );
1974 if ( nFirst[ 0 ] == nFirst[ 1 ] )
1977 angle = N1.Angle( N2 );
1982 // =================================================
1983 // class generating a unique ID for a pair of nodes
1984 // and able to return nodes by that ID
1985 // =================================================
1989 LinkID_Gen( const SMESHDS_Mesh* theMesh )
1990 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
1993 long GetLinkID (const SMDS_MeshNode * n1,
1994 const SMDS_MeshNode * n2) const
1996 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
1999 bool GetNodes (const long theLinkID,
2000 const SMDS_MeshNode* & theNode1,
2001 const SMDS_MeshNode* & theNode2) const
2003 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2004 if ( !theNode1 ) return false;
2005 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2006 if ( !theNode2 ) return false;
2012 const SMESHDS_Mesh* myMesh;
2017 //=======================================================================
2018 //function : TriToQuad
2019 //purpose : Fuse neighbour triangles into quadrangles.
2020 // theCrit is used to select a neighbour to fuse with.
2021 // theMaxAngle is a max angle between element normals at which
2022 // fusion is still performed.
2023 //=======================================================================
2025 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2026 SMESH::Controls::NumericalFunctorPtr theCrit,
2027 const double theMaxAngle)
2029 myLastCreatedElems.Clear();
2030 myLastCreatedNodes.Clear();
2032 MESSAGE( "::TriToQuad()" );
2034 if ( !theCrit.get() )
2037 SMESHDS_Mesh * aMesh = GetMeshDS();
2039 // Prepare data for algo: build
2040 // 1. map of elements with their linkIDs
2041 // 2. map of linkIDs with their elements
2043 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2044 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2045 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2046 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2048 TIDSortedElemSet::iterator itElem;
2049 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2050 const SMDS_MeshElement* elem = *itElem;
2051 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2052 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2053 if(!IsTria) continue;
2055 // retrieve element nodes
2056 const SMDS_MeshNode* aNodes [4];
2057 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2060 aNodes[ i++ ] = cast2Node( itN->next() );
2061 aNodes[ 3 ] = aNodes[ 0 ];
2064 for ( i = 0; i < 3; i++ ) {
2065 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2066 // check if elements sharing a link can be fused
2067 itLE = mapLi_listEl.find( link );
2068 if ( itLE != mapLi_listEl.end() ) {
2069 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2071 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2072 //if ( FindShape( elem ) != FindShape( elem2 ))
2073 // continue; // do not fuse triangles laying on different shapes
2074 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2075 continue; // avoid making badly shaped quads
2076 (*itLE).second.push_back( elem );
2079 mapLi_listEl[ link ].push_back( elem );
2081 mapEl_setLi [ elem ].insert( link );
2084 // Clean the maps from the links shared by a sole element, ie
2085 // links to which only one element is bound in mapLi_listEl
2087 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2088 int nbElems = (*itLE).second.size();
2089 if ( nbElems < 2 ) {
2090 const SMDS_MeshElement* elem = (*itLE).second.front();
2091 SMESH_TLink link = (*itLE).first;
2092 mapEl_setLi[ elem ].erase( link );
2093 if ( mapEl_setLi[ elem ].empty() )
2094 mapEl_setLi.erase( elem );
2098 // Algo: fuse triangles into quadrangles
2100 while ( ! mapEl_setLi.empty() ) {
2101 // Look for the start element:
2102 // the element having the least nb of shared links
2103 const SMDS_MeshElement* startElem = 0;
2105 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2106 int nbLinks = (*itEL).second.size();
2107 if ( nbLinks < minNbLinks ) {
2108 startElem = (*itEL).first;
2109 minNbLinks = nbLinks;
2110 if ( minNbLinks == 1 )
2115 // search elements to fuse starting from startElem or links of elements
2116 // fused earlyer - startLinks
2117 list< SMESH_TLink > startLinks;
2118 while ( startElem || !startLinks.empty() ) {
2119 while ( !startElem && !startLinks.empty() ) {
2120 // Get an element to start, by a link
2121 SMESH_TLink linkId = startLinks.front();
2122 startLinks.pop_front();
2123 itLE = mapLi_listEl.find( linkId );
2124 if ( itLE != mapLi_listEl.end() ) {
2125 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2126 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2127 for ( ; itE != listElem.end() ; itE++ )
2128 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2130 mapLi_listEl.erase( itLE );
2135 // Get candidates to be fused
2136 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2137 const SMESH_TLink *link12, *link13;
2139 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2140 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2141 ASSERT( !setLi.empty() );
2142 set< SMESH_TLink >::iterator itLi;
2143 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2145 const SMESH_TLink & link = (*itLi);
2146 itLE = mapLi_listEl.find( link );
2147 if ( itLE == mapLi_listEl.end() )
2150 const SMDS_MeshElement* elem = (*itLE).second.front();
2152 elem = (*itLE).second.back();
2153 mapLi_listEl.erase( itLE );
2154 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2165 // add other links of elem to list of links to re-start from
2166 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2167 set< SMESH_TLink >::iterator it;
2168 for ( it = links.begin(); it != links.end(); it++ ) {
2169 const SMESH_TLink& link2 = (*it);
2170 if ( link2 != link )
2171 startLinks.push_back( link2 );
2175 // Get nodes of possible quadrangles
2176 const SMDS_MeshNode *n12 [4], *n13 [4];
2177 bool Ok12 = false, Ok13 = false;
2178 const SMDS_MeshNode *linkNode1, *linkNode2;
2180 linkNode1 = link12->first;
2181 linkNode2 = link12->second;
2182 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2186 linkNode1 = link13->first;
2187 linkNode2 = link13->second;
2188 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2192 // Choose a pair to fuse
2193 if ( Ok12 && Ok13 ) {
2194 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2195 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2196 double aBadRate12 = getBadRate( &quad12, theCrit );
2197 double aBadRate13 = getBadRate( &quad13, theCrit );
2198 if ( aBadRate13 < aBadRate12 )
2205 // and remove fused elems and removed links from the maps
2206 mapEl_setLi.erase( tr1 );
2208 mapEl_setLi.erase( tr2 );
2209 mapLi_listEl.erase( *link12 );
2210 if(tr1->NbNodes()==3) {
2211 if( tr1->GetID() < tr2->GetID() ) {
2212 aMesh->ChangeElementNodes( tr1, n12, 4 );
2213 myLastCreatedElems.Append(tr1);
2214 aMesh->RemoveElement( tr2 );
2217 aMesh->ChangeElementNodes( tr2, n12, 4 );
2218 myLastCreatedElems.Append(tr2);
2219 aMesh->RemoveElement( tr1);
2223 const SMDS_MeshNode* N1 [6];
2224 const SMDS_MeshNode* N2 [6];
2225 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2226 // now we receive following N1 and N2 (using numeration as above image)
2227 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2228 // i.e. first nodes from both arrays determ new diagonal
2229 const SMDS_MeshNode* aNodes[8];
2238 if( tr1->GetID() < tr2->GetID() ) {
2239 GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 );
2240 myLastCreatedElems.Append(tr1);
2241 GetMeshDS()->RemoveElement( tr2 );
2244 GetMeshDS()->ChangeElementNodes( tr2, aNodes, 8 );
2245 myLastCreatedElems.Append(tr2);
2246 GetMeshDS()->RemoveElement( tr1 );
2248 // remove middle node (9)
2249 GetMeshDS()->RemoveNode( N1[4] );
2253 mapEl_setLi.erase( tr3 );
2254 mapLi_listEl.erase( *link13 );
2255 if(tr1->NbNodes()==3) {
2256 if( tr1->GetID() < tr2->GetID() ) {
2257 aMesh->ChangeElementNodes( tr1, n13, 4 );
2258 myLastCreatedElems.Append(tr1);
2259 aMesh->RemoveElement( tr3 );
2262 aMesh->ChangeElementNodes( tr3, n13, 4 );
2263 myLastCreatedElems.Append(tr3);
2264 aMesh->RemoveElement( tr1 );
2268 const SMDS_MeshNode* N1 [6];
2269 const SMDS_MeshNode* N2 [6];
2270 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2271 // now we receive following N1 and N2 (using numeration as above image)
2272 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2273 // i.e. first nodes from both arrays determ new diagonal
2274 const SMDS_MeshNode* aNodes[8];
2283 if( tr1->GetID() < tr2->GetID() ) {
2284 GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 );
2285 myLastCreatedElems.Append(tr1);
2286 GetMeshDS()->RemoveElement( tr3 );
2289 GetMeshDS()->ChangeElementNodes( tr3, aNodes, 8 );
2290 myLastCreatedElems.Append(tr3);
2291 GetMeshDS()->RemoveElement( tr1 );
2293 // remove middle node (9)
2294 GetMeshDS()->RemoveNode( N1[4] );
2298 // Next element to fuse: the rejected one
2300 startElem = Ok12 ? tr3 : tr2;
2302 } // if ( startElem )
2303 } // while ( startElem || !startLinks.empty() )
2304 } // while ( ! mapEl_setLi.empty() )
2310 /*#define DUMPSO(txt) \
2311 // cout << txt << endl;
2312 //=============================================================================
2316 //=============================================================================
2317 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2321 int tmp = idNodes[ i1 ];
2322 idNodes[ i1 ] = idNodes[ i2 ];
2323 idNodes[ i2 ] = tmp;
2324 gp_Pnt Ptmp = P[ i1 ];
2327 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2330 //=======================================================================
2331 //function : SortQuadNodes
2332 //purpose : Set 4 nodes of a quadrangle face in a good order.
2333 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2335 //=======================================================================
2337 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2342 for ( i = 0; i < 4; i++ ) {
2343 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2345 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2348 gp_Vec V1(P[0], P[1]);
2349 gp_Vec V2(P[0], P[2]);
2350 gp_Vec V3(P[0], P[3]);
2352 gp_Vec Cross1 = V1 ^ V2;
2353 gp_Vec Cross2 = V2 ^ V3;
2356 if (Cross1.Dot(Cross2) < 0)
2361 if (Cross1.Dot(Cross2) < 0)
2365 swap ( i, i + 1, idNodes, P );
2367 // for ( int ii = 0; ii < 4; ii++ ) {
2368 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2369 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2375 //=======================================================================
2376 //function : SortHexaNodes
2377 //purpose : Set 8 nodes of a hexahedron in a good order.
2378 // Return success status
2379 //=======================================================================
2381 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2386 DUMPSO( "INPUT: ========================================");
2387 for ( i = 0; i < 8; i++ ) {
2388 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2389 if ( !n ) return false;
2390 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2391 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2393 DUMPSO( "========================================");
2396 set<int> faceNodes; // ids of bottom face nodes, to be found
2397 set<int> checkedId1; // ids of tried 2-nd nodes
2398 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2399 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2400 int iMin, iLoop1 = 0;
2402 // Loop to try the 2-nd nodes
2404 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2406 // Find not checked 2-nd node
2407 for ( i = 1; i < 8; i++ )
2408 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2409 int id1 = idNodes[i];
2410 swap ( 1, i, idNodes, P );
2411 checkedId1.insert ( id1 );
2415 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2416 // ie that all but meybe one (id3 which is on the same face) nodes
2417 // lay on the same side from the triangle plane.
2419 bool manyInPlane = false; // more than 4 nodes lay in plane
2421 while ( ++iLoop2 < 6 ) {
2423 // get 1-2-3 plane coeffs
2424 Standard_Real A, B, C, D;
2425 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2426 if ( N.SquareMagnitude() > gp::Resolution() )
2428 gp_Pln pln ( P[0], N );
2429 pln.Coefficients( A, B, C, D );
2431 // find the node (iMin) closest to pln
2432 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2434 for ( i = 3; i < 8; i++ ) {
2435 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2436 if ( fabs( dist[i] ) < minDist ) {
2437 minDist = fabs( dist[i] );
2440 if ( fabs( dist[i] ) <= tol )
2441 idInPln.insert( idNodes[i] );
2444 // there should not be more than 4 nodes in bottom plane
2445 if ( idInPln.size() > 1 )
2447 DUMPSO( "### idInPln.size() = " << idInPln.size());
2448 // idInPlane does not contain the first 3 nodes
2449 if ( manyInPlane || idInPln.size() == 5)
2450 return false; // all nodes in one plane
2453 // set the 1-st node to be not in plane
2454 for ( i = 3; i < 8; i++ ) {
2455 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2456 DUMPSO( "### Reset 0-th node");
2457 swap( 0, i, idNodes, P );
2462 // reset to re-check second nodes
2463 leastDist = DBL_MAX;
2467 break; // from iLoop2;
2470 // check that the other 4 nodes are on the same side
2471 bool sameSide = true;
2472 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2473 for ( i = 3; sameSide && i < 8; i++ ) {
2475 sameSide = ( isNeg == dist[i] <= 0.);
2478 // keep best solution
2479 if ( sameSide && minDist < leastDist ) {
2480 leastDist = minDist;
2482 faceNodes.insert( idNodes[ 1 ] );
2483 faceNodes.insert( idNodes[ 2 ] );
2484 faceNodes.insert( idNodes[ iMin ] );
2485 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2486 << " leastDist = " << leastDist);
2487 if ( leastDist <= DBL_MIN )
2492 // set next 3-d node to check
2493 int iNext = 2 + iLoop2;
2495 DUMPSO( "Try 2-nd");
2496 swap ( 2, iNext, idNodes, P );
2498 } // while ( iLoop2 < 6 )
2501 if ( faceNodes.empty() ) return false;
2503 // Put the faceNodes in proper places
2504 for ( i = 4; i < 8; i++ ) {
2505 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2506 // find a place to put
2508 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2510 DUMPSO( "Set faceNodes");
2511 swap ( iTo, i, idNodes, P );
2516 // Set nodes of the found bottom face in good order
2517 DUMPSO( " Found bottom face: ");
2518 i = SortQuadNodes( theMesh, idNodes );
2520 gp_Pnt Ptmp = P[ i ];
2525 // for ( int ii = 0; ii < 4; ii++ ) {
2526 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2527 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2530 // Gravity center of the top and bottom faces
2531 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2532 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2534 // Get direction from the bottom to the top face
2535 gp_Vec upDir ( aGCb, aGCt );
2536 Standard_Real upDirSize = upDir.Magnitude();
2537 if ( upDirSize <= gp::Resolution() ) return false;
2540 // Assure that the bottom face normal points up
2541 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2542 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2543 if ( Nb.Dot( upDir ) < 0 ) {
2544 DUMPSO( "Reverse bottom face");
2545 swap( 1, 3, idNodes, P );
2548 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2549 Standard_Real minDist = DBL_MAX;
2550 for ( i = 4; i < 8; i++ ) {
2551 // projection of P[i] to the plane defined by P[0] and upDir
2552 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2553 Standard_Real sqDist = P[0].SquareDistance( Pp );
2554 if ( sqDist < minDist ) {
2559 DUMPSO( "Set 4-th");
2560 swap ( 4, iMin, idNodes, P );
2562 // Set nodes of the top face in good order
2563 DUMPSO( "Sort top face");
2564 i = SortQuadNodes( theMesh, &idNodes[4] );
2567 gp_Pnt Ptmp = P[ i ];
2572 // Assure that direction of the top face normal is from the bottom face
2573 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2574 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2575 if ( Nt.Dot( upDir ) < 0 ) {
2576 DUMPSO( "Reverse top face");
2577 swap( 5, 7, idNodes, P );
2580 // DUMPSO( "OUTPUT: ========================================");
2581 // for ( i = 0; i < 8; i++ ) {
2582 // float *p = ugrid->GetPoint(idNodes[i]);
2583 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2589 //================================================================================
2591 * \brief Return nodes linked to the given one
2592 * \param theNode - the node
2593 * \param linkedNodes - the found nodes
2594 * \param type - the type of elements to check
2596 * Medium nodes are ignored
2598 //================================================================================
2600 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2601 TIDSortedElemSet & linkedNodes,
2602 SMDSAbs_ElementType type )
2604 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2605 while ( elemIt->more() )
2607 const SMDS_MeshElement* elem = elemIt->next();
2608 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2609 if ( elem->GetType() == SMDSAbs_Volume )
2611 SMDS_VolumeTool vol( elem );
2612 while ( nodeIt->more() ) {
2613 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2614 if ( theNode != n && vol.IsLinked( theNode, n ))
2615 linkedNodes.insert( n );
2620 for ( int i = 0; nodeIt->more(); ++i ) {
2621 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2622 if ( n == theNode ) {
2623 int iBefore = i - 1;
2625 if ( elem->IsQuadratic() ) {
2626 int nb = elem->NbNodes() / 2;
2627 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2628 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2630 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2631 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2638 //=======================================================================
2639 //function : laplacianSmooth
2640 //purpose : pulls theNode toward the center of surrounding nodes directly
2641 // connected to that node along an element edge
2642 //=======================================================================
2644 void laplacianSmooth(const SMDS_MeshNode* theNode,
2645 const Handle(Geom_Surface)& theSurface,
2646 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2648 // find surrounding nodes
2650 TIDSortedElemSet nodeSet;
2651 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2653 // compute new coodrs
2655 double coord[] = { 0., 0., 0. };
2656 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2657 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2658 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2659 if ( theSurface.IsNull() ) { // smooth in 3D
2660 coord[0] += node->X();
2661 coord[1] += node->Y();
2662 coord[2] += node->Z();
2664 else { // smooth in 2D
2665 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2666 gp_XY* uv = theUVMap[ node ];
2667 coord[0] += uv->X();
2668 coord[1] += uv->Y();
2671 int nbNodes = nodeSet.size();
2674 coord[0] /= nbNodes;
2675 coord[1] /= nbNodes;
2677 if ( !theSurface.IsNull() ) {
2678 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2679 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2680 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2686 coord[2] /= nbNodes;
2690 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2693 //=======================================================================
2694 //function : centroidalSmooth
2695 //purpose : pulls theNode toward the element-area-weighted centroid of the
2696 // surrounding elements
2697 //=======================================================================
2699 void centroidalSmooth(const SMDS_MeshNode* theNode,
2700 const Handle(Geom_Surface)& theSurface,
2701 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2703 gp_XYZ aNewXYZ(0.,0.,0.);
2704 SMESH::Controls::Area anAreaFunc;
2705 double totalArea = 0.;
2710 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2711 while ( elemIt->more() )
2713 const SMDS_MeshElement* elem = elemIt->next();
2716 gp_XYZ elemCenter(0.,0.,0.);
2717 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2718 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2719 int nn = elem->NbNodes();
2720 if(elem->IsQuadratic()) nn = nn/2;
2722 //while ( itN->more() ) {
2724 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2726 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2727 aNodePoints.push_back( aP );
2728 if ( !theSurface.IsNull() ) { // smooth in 2D
2729 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2730 gp_XY* uv = theUVMap[ aNode ];
2731 aP.SetCoord( uv->X(), uv->Y(), 0. );
2735 double elemArea = anAreaFunc.GetValue( aNodePoints );
2736 totalArea += elemArea;
2738 aNewXYZ += elemCenter * elemArea;
2740 aNewXYZ /= totalArea;
2741 if ( !theSurface.IsNull() ) {
2742 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2743 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2748 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2751 //=======================================================================
2752 //function : getClosestUV
2753 //purpose : return UV of closest projection
2754 //=======================================================================
2756 static bool getClosestUV (Extrema_GenExtPS& projector,
2757 const gp_Pnt& point,
2760 projector.Perform( point );
2761 if ( projector.IsDone() ) {
2762 double u, v, minVal = DBL_MAX;
2763 for ( int i = projector.NbExt(); i > 0; i-- )
2764 if ( projector.Value( i ) < minVal ) {
2765 minVal = projector.Value( i );
2766 projector.Point( i ).Parameter( u, v );
2768 result.SetCoord( u, v );
2774 //=======================================================================
2776 //purpose : Smooth theElements during theNbIterations or until a worst
2777 // element has aspect ratio <= theTgtAspectRatio.
2778 // Aspect Ratio varies in range [1.0, inf].
2779 // If theElements is empty, the whole mesh is smoothed.
2780 // theFixedNodes contains additionally fixed nodes. Nodes built
2781 // on edges and boundary nodes are always fixed.
2782 //=======================================================================
2784 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
2785 set<const SMDS_MeshNode*> & theFixedNodes,
2786 const SmoothMethod theSmoothMethod,
2787 const int theNbIterations,
2788 double theTgtAspectRatio,
2791 myLastCreatedElems.Clear();
2792 myLastCreatedNodes.Clear();
2794 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2796 if ( theTgtAspectRatio < 1.0 )
2797 theTgtAspectRatio = 1.0;
2799 const double disttol = 1.e-16;
2801 SMESH::Controls::AspectRatio aQualityFunc;
2803 SMESHDS_Mesh* aMesh = GetMeshDS();
2805 if ( theElems.empty() ) {
2806 // add all faces to theElems
2807 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2808 while ( fIt->more() ) {
2809 const SMDS_MeshElement* face = fIt->next();
2810 theElems.insert( face );
2813 // get all face ids theElems are on
2814 set< int > faceIdSet;
2815 TIDSortedElemSet::iterator itElem;
2817 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2818 int fId = FindShape( *itElem );
2819 // check that corresponding submesh exists and a shape is face
2821 faceIdSet.find( fId ) == faceIdSet.end() &&
2822 aMesh->MeshElements( fId )) {
2823 TopoDS_Shape F = aMesh->IndexToShape( fId );
2824 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2825 faceIdSet.insert( fId );
2828 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2830 // ===============================================
2831 // smooth elements on each TopoDS_Face separately
2832 // ===============================================
2834 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2835 for ( ; fId != faceIdSet.rend(); ++fId ) {
2836 // get face surface and submesh
2837 Handle(Geom_Surface) surface;
2838 SMESHDS_SubMesh* faceSubMesh = 0;
2840 double fToler2 = 0, vPeriod = 0., uPeriod = 0., f,l;
2841 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2842 bool isUPeriodic = false, isVPeriodic = false;
2844 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
2845 surface = BRep_Tool::Surface( face );
2846 faceSubMesh = aMesh->MeshElements( *fId );
2847 fToler2 = BRep_Tool::Tolerance( face );
2848 fToler2 *= fToler2 * 10.;
2849 isUPeriodic = surface->IsUPeriodic();
2851 vPeriod = surface->UPeriod();
2852 isVPeriodic = surface->IsVPeriodic();
2854 uPeriod = surface->VPeriod();
2855 surface->Bounds( u1, u2, v1, v2 );
2857 // ---------------------------------------------------------
2858 // for elements on a face, find movable and fixed nodes and
2859 // compute UV for them
2860 // ---------------------------------------------------------
2861 bool checkBoundaryNodes = false;
2862 bool isQuadratic = false;
2863 set<const SMDS_MeshNode*> setMovableNodes;
2864 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
2865 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
2866 list< const SMDS_MeshElement* > elemsOnFace;
2868 Extrema_GenExtPS projector;
2869 GeomAdaptor_Surface surfAdaptor;
2870 if ( !surface.IsNull() ) {
2871 surfAdaptor.Load( surface );
2872 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
2874 int nbElemOnFace = 0;
2875 itElem = theElems.begin();
2876 // loop on not yet smoothed elements: look for elems on a face
2877 while ( itElem != theElems.end() ) {
2878 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
2879 break; // all elements found
2881 const SMDS_MeshElement* elem = *itElem;
2882 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
2883 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
2887 elemsOnFace.push_back( elem );
2888 theElems.erase( itElem++ );
2892 isQuadratic = elem->IsQuadratic();
2894 // get movable nodes of elem
2895 const SMDS_MeshNode* node;
2896 SMDS_TypeOfPosition posType;
2897 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2898 int nn = 0, nbn = elem->NbNodes();
2899 if(elem->IsQuadratic())
2901 while ( nn++ < nbn ) {
2902 node = static_cast<const SMDS_MeshNode*>( itN->next() );
2903 const SMDS_PositionPtr& pos = node->GetPosition();
2904 posType = pos.get() ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
2905 if (posType != SMDS_TOP_EDGE &&
2906 posType != SMDS_TOP_VERTEX &&
2907 theFixedNodes.find( node ) == theFixedNodes.end())
2909 // check if all faces around the node are on faceSubMesh
2910 // because a node on edge may be bound to face
2911 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
2913 if ( faceSubMesh ) {
2914 while ( eIt->more() && all ) {
2915 const SMDS_MeshElement* e = eIt->next();
2916 all = faceSubMesh->Contains( e );
2920 setMovableNodes.insert( node );
2922 checkBoundaryNodes = true;
2924 if ( posType == SMDS_TOP_3DSPACE )
2925 checkBoundaryNodes = true;
2928 if ( surface.IsNull() )
2931 // get nodes to check UV
2932 list< const SMDS_MeshNode* > uvCheckNodes;
2933 itN = elem->nodesIterator();
2934 nn = 0; nbn = elem->NbNodes();
2935 if(elem->IsQuadratic())
2937 while ( nn++ < nbn ) {
2938 node = static_cast<const SMDS_MeshNode*>( itN->next() );
2939 if ( uvMap.find( node ) == uvMap.end() )
2940 uvCheckNodes.push_back( node );
2941 // add nodes of elems sharing node
2942 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
2943 // while ( eIt->more() ) {
2944 // const SMDS_MeshElement* e = eIt->next();
2945 // if ( e != elem ) {
2946 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
2947 // while ( nIt->more() ) {
2948 // const SMDS_MeshNode* n =
2949 // static_cast<const SMDS_MeshNode*>( nIt->next() );
2950 // if ( uvMap.find( n ) == uvMap.end() )
2951 // uvCheckNodes.push_back( n );
2957 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
2958 for ( ; n != uvCheckNodes.end(); ++n ) {
2961 const SMDS_PositionPtr& pos = node->GetPosition();
2962 posType = pos.get() ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
2964 switch ( posType ) {
2965 case SMDS_TOP_FACE: {
2966 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos.get();
2967 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
2970 case SMDS_TOP_EDGE: {
2971 TopoDS_Shape S = aMesh->IndexToShape( pos->GetShapeId() );
2972 Handle(Geom2d_Curve) pcurve;
2973 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
2974 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
2975 if ( !pcurve.IsNull() ) {
2976 double u = (( SMDS_EdgePosition* ) pos.get() )->GetUParameter();
2977 uv = pcurve->Value( u ).XY();
2981 case SMDS_TOP_VERTEX: {
2982 TopoDS_Shape S = aMesh->IndexToShape( pos->GetShapeId() );
2983 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
2984 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
2989 // check existing UV
2990 bool project = true;
2991 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
2992 double dist1 = DBL_MAX, dist2 = 0;
2993 if ( posType != SMDS_TOP_3DSPACE ) {
2994 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
2995 project = dist1 > fToler2;
2997 if ( project ) { // compute new UV
2999 if ( !getClosestUV( projector, pNode, newUV )) {
3000 MESSAGE("Node Projection Failed " << node);
3004 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3006 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3008 if ( posType != SMDS_TOP_3DSPACE )
3009 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3010 if ( dist2 < dist1 )
3014 // store UV in the map
3015 listUV.push_back( uv );
3016 uvMap.insert( make_pair( node, &listUV.back() ));
3018 } // loop on not yet smoothed elements
3020 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3021 checkBoundaryNodes = true;
3023 // fix nodes on mesh boundary
3025 if ( checkBoundaryNodes ) {
3026 map< NLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3027 map< NLink, int >::iterator link_nb;
3028 // put all elements links to linkNbMap
3029 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3030 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3031 const SMDS_MeshElement* elem = (*elemIt);
3032 int nbn = elem->NbNodes();
3033 if(elem->IsQuadratic())
3035 // loop on elem links: insert them in linkNbMap
3036 const SMDS_MeshNode* curNode, *prevNode = elem->GetNodeWrap( nbn );
3037 for ( int iN = 0; iN < nbn; ++iN ) {
3038 curNode = elem->GetNode( iN );
3040 if ( curNode < prevNode ) link = make_pair( curNode , prevNode );
3041 else link = make_pair( prevNode , curNode );
3043 link_nb = linkNbMap.find( link );
3044 if ( link_nb == linkNbMap.end() )
3045 linkNbMap.insert( make_pair ( link, 1 ));
3050 // remove nodes that are in links encountered only once from setMovableNodes
3051 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3052 if ( link_nb->second == 1 ) {
3053 setMovableNodes.erase( link_nb->first.first );
3054 setMovableNodes.erase( link_nb->first.second );
3059 // -----------------------------------------------------
3060 // for nodes on seam edge, compute one more UV ( uvMap2 );
3061 // find movable nodes linked to nodes on seam and which
3062 // are to be smoothed using the second UV ( uvMap2 )
3063 // -----------------------------------------------------
3065 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3066 if ( !surface.IsNull() ) {
3067 TopExp_Explorer eExp( face, TopAbs_EDGE );
3068 for ( ; eExp.More(); eExp.Next() ) {
3069 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3070 if ( !BRep_Tool::IsClosed( edge, face ))
3072 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3073 if ( !sm ) continue;
3074 // find out which parameter varies for a node on seam
3077 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3078 if ( pcurve.IsNull() ) continue;
3079 uv1 = pcurve->Value( f );
3081 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3082 if ( pcurve.IsNull() ) continue;
3083 uv2 = pcurve->Value( f );
3084 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3086 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3087 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3089 // get nodes on seam and its vertices
3090 list< const SMDS_MeshNode* > seamNodes;
3091 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3092 while ( nSeamIt->more() ) {
3093 const SMDS_MeshNode* node = nSeamIt->next();
3094 if ( !isQuadratic || !IsMedium( node ))
3095 seamNodes.push_back( node );
3097 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3098 for ( ; vExp.More(); vExp.Next() ) {
3099 sm = aMesh->MeshElements( vExp.Current() );
3101 nSeamIt = sm->GetNodes();
3102 while ( nSeamIt->more() )
3103 seamNodes.push_back( nSeamIt->next() );
3106 // loop on nodes on seam
3107 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3108 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3109 const SMDS_MeshNode* nSeam = *noSeIt;
3110 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3111 if ( n_uv == uvMap.end() )
3114 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3115 // set the second UV
3116 listUV.push_back( *n_uv->second );
3117 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3118 if ( uvMap2.empty() )
3119 uvMap2 = uvMap; // copy the uvMap contents
3120 uvMap2[ nSeam ] = &listUV.back();
3122 // collect movable nodes linked to ones on seam in nodesNearSeam
3123 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3124 while ( eIt->more() ) {
3125 const SMDS_MeshElement* e = eIt->next();
3126 int nbUseMap1 = 0, nbUseMap2 = 0;
3127 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3128 int nn = 0, nbn = e->NbNodes();
3129 if(e->IsQuadratic()) nbn = nbn/2;
3130 while ( nn++ < nbn )
3132 const SMDS_MeshNode* n =
3133 static_cast<const SMDS_MeshNode*>( nIt->next() );
3135 setMovableNodes.find( n ) == setMovableNodes.end() )
3137 // add only nodes being closer to uv2 than to uv1
3138 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3139 0.5 * ( n->Y() + nSeam->Y() ),
3140 0.5 * ( n->Z() + nSeam->Z() ));
3142 getClosestUV( projector, pMid, uv );
3143 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3144 nodesNearSeam.insert( n );
3150 // for centroidalSmooth all element nodes must
3151 // be on one side of a seam
3152 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3153 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3155 while ( nn++ < nbn ) {
3156 const SMDS_MeshNode* n =
3157 static_cast<const SMDS_MeshNode*>( nIt->next() );
3158 setMovableNodes.erase( n );
3162 } // loop on nodes on seam
3163 } // loop on edge of a face
3164 } // if ( !face.IsNull() )
3166 if ( setMovableNodes.empty() ) {
3167 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3168 continue; // goto next face
3176 double maxRatio = -1., maxDisplacement = -1.;
3177 set<const SMDS_MeshNode*>::iterator nodeToMove;
3178 for ( it = 0; it < theNbIterations; it++ ) {
3179 maxDisplacement = 0.;
3180 nodeToMove = setMovableNodes.begin();
3181 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3182 const SMDS_MeshNode* node = (*nodeToMove);
3183 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3186 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3187 if ( theSmoothMethod == LAPLACIAN )
3188 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3190 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3192 // node displacement
3193 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3194 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3195 if ( aDispl > maxDisplacement )
3196 maxDisplacement = aDispl;
3198 // no node movement => exit
3199 //if ( maxDisplacement < 1.e-16 ) {
3200 if ( maxDisplacement < disttol ) {
3201 MESSAGE("-- no node movement --");
3205 // check elements quality
3207 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3208 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3209 const SMDS_MeshElement* elem = (*elemIt);
3210 if ( !elem || elem->GetType() != SMDSAbs_Face )
3212 SMESH::Controls::TSequenceOfXYZ aPoints;
3213 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3214 double aValue = aQualityFunc.GetValue( aPoints );
3215 if ( aValue > maxRatio )
3219 if ( maxRatio <= theTgtAspectRatio ) {
3220 MESSAGE("-- quality achived --");
3223 if (it+1 == theNbIterations) {
3224 MESSAGE("-- Iteration limit exceeded --");
3226 } // smoothing iterations
3228 MESSAGE(" Face id: " << *fId <<
3229 " Nb iterstions: " << it <<
3230 " Displacement: " << maxDisplacement <<
3231 " Aspect Ratio " << maxRatio);
3233 // ---------------------------------------
3234 // new nodes positions are computed,
3235 // record movement in DS and set new UV
3236 // ---------------------------------------
3237 nodeToMove = setMovableNodes.begin();
3238 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3239 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3240 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3241 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3242 if ( node_uv != uvMap.end() ) {
3243 gp_XY* uv = node_uv->second;
3245 ( SMDS_PositionPtr( new SMDS_FacePosition( *fId, uv->X(), uv->Y() )));
3249 // move medium nodes of quadratic elements
3252 SMESH_MesherHelper helper( *GetMesh() );
3253 if ( !face.IsNull() )
3254 helper.SetSubShape( face );
3255 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3256 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3257 const SMDS_QuadraticFaceOfNodes* QF =
3258 dynamic_cast<const SMDS_QuadraticFaceOfNodes*> (*elemIt);
3260 vector<const SMDS_MeshNode*> Ns;
3261 Ns.reserve(QF->NbNodes()+1);
3262 SMDS_NodeIteratorPtr anIter = QF->interlacedNodesIterator();
3263 while ( anIter->more() )
3264 Ns.push_back( anIter->next() );
3265 Ns.push_back( Ns[0] );
3267 for(int i=0; i<QF->NbNodes(); i=i+2) {
3268 if ( !surface.IsNull() ) {
3269 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3270 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3271 gp_XY uv = ( uv1 + uv2 ) / 2.;
3272 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3273 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3276 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3277 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3278 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3280 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3281 fabs( Ns[i+1]->Y() - y ) > disttol ||
3282 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3283 // we have to move i+1 node
3284 aMesh->MoveNode( Ns[i+1], x, y, z );
3291 } // loop on face ids
3295 //=======================================================================
3296 //function : isReverse
3297 //purpose : Return true if normal of prevNodes is not co-directied with
3298 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3299 // iNotSame is where prevNodes and nextNodes are different
3300 //=======================================================================
3302 static bool isReverse(vector<const SMDS_MeshNode*> prevNodes,
3303 vector<const SMDS_MeshNode*> nextNodes,
3307 int iBeforeNotSame = ( iNotSame == 0 ? nbNodes - 1 : iNotSame - 1 );
3308 int iAfterNotSame = ( iNotSame + 1 == nbNodes ? 0 : iNotSame + 1 );
3310 const SMDS_MeshNode* nB = prevNodes[ iBeforeNotSame ];
3311 const SMDS_MeshNode* nA = prevNodes[ iAfterNotSame ];
3312 const SMDS_MeshNode* nP = prevNodes[ iNotSame ];
3313 const SMDS_MeshNode* nN = nextNodes[ iNotSame ];
3315 gp_Pnt pB ( nB->X(), nB->Y(), nB->Z() );
3316 gp_Pnt pA ( nA->X(), nA->Y(), nA->Z() );
3317 gp_Pnt pP ( nP->X(), nP->Y(), nP->Z() );
3318 gp_Pnt pN ( nN->X(), nN->Y(), nN->Z() );
3320 gp_Vec vB ( pP, pB ), vA ( pP, pA ), vN ( pP, pN );
3322 return (vA ^ vB) * vN < 0.0;
3325 //=======================================================================
3327 * \brief Create elements by sweeping an element
3328 * \param elem - element to sweep
3329 * \param newNodesItVec - nodes generated from each node of the element
3330 * \param newElems - generated elements
3331 * \param nbSteps - number of sweeping steps
3332 * \param srcElements - to append elem for each generated element
3334 //=======================================================================
3336 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3337 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3338 list<const SMDS_MeshElement*>& newElems,
3340 SMESH_SequenceOfElemPtr& srcElements)
3342 SMESHDS_Mesh* aMesh = GetMeshDS();
3344 // Loop on elem nodes:
3345 // find new nodes and detect same nodes indices
3346 int nbNodes = elem->NbNodes();
3347 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3348 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3349 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3350 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3352 int iNode, nbSame = 0, iNotSameNode = 0, iSameNode = 0;
3353 vector<int> sames(nbNodes);
3354 vector<bool> issimple(nbNodes);
3356 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3357 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3358 const SMDS_MeshNode* node = nnIt->first;
3359 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3360 if ( listNewNodes.empty() ) {
3364 issimple[iNode] = (listNewNodes.size()==nbSteps); // is node medium
3366 itNN[ iNode ] = listNewNodes.begin();
3367 prevNod[ iNode ] = node;
3368 nextNod[ iNode ] = listNewNodes.front();
3369 if( !elem->IsQuadratic() || !issimple[iNode] ) {
3370 if ( prevNod[ iNode ] != nextNod [ iNode ])
3371 iNotSameNode = iNode;
3375 sames[nbSame++] = iNode;
3380 //cout<<" nbSame = "<<nbSame<<endl;
3381 if ( nbSame == nbNodes || nbSame > 2) {
3382 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3383 //INFOS( " Too many same nodes of element " << elem->GetID() );
3387 // if( elem->IsQuadratic() && nbSame>0 ) {
3388 // MESSAGE( "Can not rotate quadratic element " << elem->GetID() );
3392 int iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3393 int nbBaseNodes = ( elem->IsQuadratic() ? nbNodes/2 : nbNodes );
3395 iBeforeSame = ( iSameNode == 0 ? nbBaseNodes - 1 : iSameNode - 1 );
3396 iAfterSame = ( iSameNode + 1 == nbBaseNodes ? 0 : iSameNode + 1 );
3397 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3401 //cout<<" prevNod[0]="<< prevNod[0]<<" prevNod[1]="<< prevNod[1]
3402 // <<" prevNod[2]="<< prevNod[2]<<" prevNod[3]="<< prevNod[4]
3403 // <<" prevNod[4]="<< prevNod[4]<<" prevNod[5]="<< prevNod[5]
3404 // <<" prevNod[6]="<< prevNod[6]<<" prevNod[7]="<< prevNod[7]<<endl;
3406 // check element orientation
3408 if ( nbNodes > 2 && !isReverse( prevNod, nextNod, nbNodes, iNotSameNode )) {
3409 //MESSAGE("Reversed elem " << elem );
3413 std::swap( iBeforeSame, iAfterSame );
3416 // make new elements
3417 for (int iStep = 0; iStep < nbSteps; iStep++ ) {
3419 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3420 if(issimple[iNode]) {
3421 nextNod[ iNode ] = *itNN[ iNode ];
3425 if( elem->GetType()==SMDSAbs_Node ) {
3426 // we have to use two nodes
3427 midlNod[ iNode ] = *itNN[ iNode ];
3429 nextNod[ iNode ] = *itNN[ iNode ];
3432 else if(!elem->IsQuadratic() || elem->IsMediumNode(prevNod[iNode]) ) {
3433 // we have to use each second node
3435 nextNod[ iNode ] = *itNN[ iNode ];
3439 // we have to use two nodes
3440 midlNod[ iNode ] = *itNN[ iNode ];
3442 nextNod[ iNode ] = *itNN[ iNode ];
3447 SMDS_MeshElement* aNewElem = 0;
3448 if(!elem->IsPoly()) {
3449 switch ( nbNodes ) {
3453 if ( nbSame == 0 ) {
3455 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3457 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3463 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3464 nextNod[ 1 ], nextNod[ 0 ] );
3466 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3467 nextNod[ iNotSameNode ] );
3471 case 3: { // TRIANGLE or quadratic edge
3472 if(elem->GetType() == SMDSAbs_Face) { // TRIANGLE
3474 if ( nbSame == 0 ) // --- pentahedron
3475 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3476 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ] );
3478 else if ( nbSame == 1 ) // --- pyramid
3479 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3480 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3481 nextNod[ iSameNode ]);
3483 else // 2 same nodes: --- tetrahedron
3484 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3485 nextNod[ iNotSameNode ]);
3487 else { // quadratic edge
3488 if(nbSame==0) { // quadratic quadrangle
3489 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], nextNod[1], prevNod[1],
3490 midlNod[0], nextNod[2], midlNod[1], prevNod[2]);
3492 else if(nbSame==1) { // quadratic triangle
3494 return; // medium node on axis
3496 else if(sames[0]==0) {
3497 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3498 nextNod[2], midlNod[1], prevNod[2]);
3500 else { // sames[0]==1
3501 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3502 midlNod[0], nextNod[2], prevNod[2]);
3511 case 4: { // QUADRANGLE
3513 if ( nbSame == 0 ) // --- hexahedron
3514 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], prevNod[ 3 ],
3515 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ], nextNod[ 3 ]);
3517 else if ( nbSame == 1 ) { // --- pyramid + pentahedron
3518 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3519 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3520 nextNod[ iSameNode ]);
3521 newElems.push_back( aNewElem );
3522 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3523 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3524 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3526 else if ( nbSame == 2 ) { // pentahedron
3527 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3528 // iBeforeSame is same too
3529 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3530 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3531 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3533 // iAfterSame is same too
3534 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3535 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3536 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3540 case 6: { // quadratic triangle
3541 // create pentahedron with 15 nodes
3543 if(i0>0) { // reversed case
3544 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[2], prevNod[1],
3545 nextNod[0], nextNod[2], nextNod[1],
3546 prevNod[5], prevNod[4], prevNod[3],
3547 nextNod[5], nextNod[4], nextNod[3],
3548 midlNod[0], midlNod[2], midlNod[1]);
3550 else { // not reversed case
3551 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3552 nextNod[0], nextNod[1], nextNod[2],
3553 prevNod[3], prevNod[4], prevNod[5],
3554 nextNod[3], nextNod[4], nextNod[5],
3555 midlNod[0], midlNod[1], midlNod[2]);
3558 else if(nbSame==1) {
3559 // 2d order pyramid of 13 nodes
3560 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5,
3561 // int n12,int n23,int n34,int n41,
3562 // int n15,int n25,int n35,int n45, int ID);
3564 int n1,n4,n41,n15,n45;
3565 if(i0>0) { // reversed case
3566 n1 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3567 n4 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3573 n1 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3574 n4 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3579 aNewElem = aMesh->AddVolume(prevNod[n1], nextNod[n1],
3580 nextNod[n4], prevNod[n4], prevNod[n5],
3581 midlNod[n1], nextNod[n41],
3582 midlNod[n4], prevNod[n41],
3583 prevNod[n15], nextNod[n15],
3584 nextNod[n45], prevNod[n45]);
3586 else if(nbSame==2) {
3587 // 2d order tetrahedron of 10 nodes
3588 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4,
3589 // int n12,int n23,int n31,
3590 // int n14,int n24,int n34, int ID);
3591 int n1 = iNotSameNode;
3592 int n2,n3,n12,n23,n31;
3593 if(i0>0) { // reversed case
3594 n2 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3595 n3 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3601 n2 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3602 n3 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3607 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3608 prevNod[n12], prevNod[n23], prevNod[n31],
3609 midlNod[n1], nextNod[n12], nextNod[n31]);
3613 case 8: { // quadratic quadrangle
3615 // create hexahedron with 20 nodes
3616 if(i0>0) { // reversed case
3617 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[3], prevNod[2], prevNod[1],
3618 nextNod[0], nextNod[3], nextNod[2], nextNod[1],
3619 prevNod[7], prevNod[6], prevNod[5], prevNod[4],
3620 nextNod[7], nextNod[6], nextNod[5], nextNod[4],
3621 midlNod[0], midlNod[3], midlNod[2], midlNod[1]);
3623 else { // not reversed case
3624 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3625 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3626 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3627 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3628 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3631 else if(nbSame==1) {
3632 // --- pyramid + pentahedron - can not be created since it is needed
3633 // additional middle node ot the center of face
3634 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3637 else if(nbSame==2) {
3638 // 2d order Pentahedron with 15 nodes
3639 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6,
3640 // int n12,int n23,int n31,int n45,int n56,int n64,
3641 // int n14,int n25,int n36, int ID);
3643 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3644 // iBeforeSame is same too
3651 // iAfterSame is same too
3657 int n12,n45,n14,n25;
3658 if(i0>0) { //reversed case
3670 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3671 prevNod[n4], prevNod[n5], nextNod[n5],
3672 prevNod[n12], midlNod[n2], nextNod[n12],
3673 prevNod[n45], midlNod[n5], nextNod[n45],
3674 prevNod[n14], prevNod[n25], nextNod[n25]);
3679 // realized for extrusion only
3680 //vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3681 //vector<int> quantities (nbNodes + 2);
3683 //quantities[0] = nbNodes; // bottom of prism
3684 //for (int inode = 0; inode < nbNodes; inode++) {
3685 // polyedre_nodes[inode] = prevNod[inode];
3688 //quantities[1] = nbNodes; // top of prism
3689 //for (int inode = 0; inode < nbNodes; inode++) {
3690 // polyedre_nodes[nbNodes + inode] = nextNod[inode];
3693 //for (int iface = 0; iface < nbNodes; iface++) {
3694 // quantities[iface + 2] = 4;
3695 // int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3696 // polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3697 // polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3698 // polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3699 // polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3701 //aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3708 // realized for extrusion only
3709 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3710 vector<int> quantities (nbNodes + 2);
3712 quantities[0] = nbNodes; // bottom of prism
3713 for (int inode = 0; inode < nbNodes; inode++) {
3714 polyedre_nodes[inode] = prevNod[inode];
3717 quantities[1] = nbNodes; // top of prism
3718 for (int inode = 0; inode < nbNodes; inode++) {
3719 polyedre_nodes[nbNodes + inode] = nextNod[inode];
3722 for (int iface = 0; iface < nbNodes; iface++) {
3723 quantities[iface + 2] = 4;
3724 int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3725 polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3726 polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3727 polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3728 polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3730 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3734 newElems.push_back( aNewElem );
3735 myLastCreatedElems.Append(aNewElem);
3736 srcElements.Append( elem );
3739 // set new prev nodes
3740 for ( iNode = 0; iNode < nbNodes; iNode++ )
3741 prevNod[ iNode ] = nextNod[ iNode ];
3746 //=======================================================================
3748 * \brief Create 1D and 2D elements around swept elements
3749 * \param mapNewNodes - source nodes and ones generated from them
3750 * \param newElemsMap - source elements and ones generated from them
3751 * \param elemNewNodesMap - nodes generated from each node of each element
3752 * \param elemSet - all swept elements
3753 * \param nbSteps - number of sweeping steps
3754 * \param srcElements - to append elem for each generated element
3756 //=======================================================================
3758 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3759 TElemOfElemListMap & newElemsMap,
3760 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3761 TIDSortedElemSet& elemSet,
3763 SMESH_SequenceOfElemPtr& srcElements)
3765 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3766 SMESHDS_Mesh* aMesh = GetMeshDS();
3768 // Find nodes belonging to only one initial element - sweep them to get edges.
3770 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3771 for ( ; nList != mapNewNodes.end(); nList++ ) {
3772 const SMDS_MeshNode* node =
3773 static_cast<const SMDS_MeshNode*>( nList->first );
3774 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3775 int nbInitElems = 0;
3776 const SMDS_MeshElement* el = 0;
3777 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3778 while ( eIt->more() && nbInitElems < 2 ) {
3780 SMDSAbs_ElementType type = el->GetType();
3781 if ( type == SMDSAbs_Volume || type < highType ) continue;
3782 if ( type > highType ) {
3786 if ( elemSet.find(el) != elemSet.end() )
3789 if ( nbInitElems < 2 ) {
3790 bool NotCreateEdge = el && el->IsQuadratic() && el->IsMediumNode(node);
3791 if(!NotCreateEdge) {
3792 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3793 list<const SMDS_MeshElement*> newEdges;
3794 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3799 // Make a ceiling for each element ie an equal element of last new nodes.
3800 // Find free links of faces - make edges and sweep them into faces.
3802 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3803 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3804 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) {
3805 const SMDS_MeshElement* elem = itElem->first;
3806 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3808 if ( elem->GetType() == SMDSAbs_Edge ) {
3809 // create a ceiling edge
3810 if (!elem->IsQuadratic()) {
3811 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3812 vecNewNodes[ 1 ]->second.back())) {
3813 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3814 vecNewNodes[ 1 ]->second.back()));
3815 srcElements.Append( myLastCreatedElems.Last() );
3819 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3820 vecNewNodes[ 1 ]->second.back(),
3821 vecNewNodes[ 2 ]->second.back())) {
3822 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3823 vecNewNodes[ 1 ]->second.back(),
3824 vecNewNodes[ 2 ]->second.back()));
3825 srcElements.Append( myLastCreatedElems.Last() );
3829 if ( elem->GetType() != SMDSAbs_Face )
3832 if(itElem->second.size()==0) continue;
3834 bool hasFreeLinks = false;
3836 TIDSortedElemSet avoidSet;
3837 avoidSet.insert( elem );
3839 set<const SMDS_MeshNode*> aFaceLastNodes;
3840 int iNode, nbNodes = vecNewNodes.size();
3841 if(!elem->IsQuadratic()) {
3842 // loop on the face nodes
3843 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3844 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3845 // look for free links of the face
3846 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
3847 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3848 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3849 // check if a link is free
3850 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3851 hasFreeLinks = true;
3852 // make an edge and a ceiling for a new edge
3853 if ( !aMesh->FindEdge( n1, n2 )) {
3854 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
3855 srcElements.Append( myLastCreatedElems.Last() );
3857 n1 = vecNewNodes[ iNode ]->second.back();
3858 n2 = vecNewNodes[ iNext ]->second.back();
3859 if ( !aMesh->FindEdge( n1, n2 )) {
3860 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
3861 srcElements.Append( myLastCreatedElems.Last() );
3866 else { // elem is quadratic face
3867 int nbn = nbNodes/2;
3868 for ( iNode = 0; iNode < nbn; iNode++ ) {
3869 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3870 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
3871 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3872 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3873 // check if a link is free
3874 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3875 hasFreeLinks = true;
3876 // make an edge and a ceiling for a new edge
3878 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
3879 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3880 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
3881 srcElements.Append( myLastCreatedElems.Last() );
3883 n1 = vecNewNodes[ iNode ]->second.back();
3884 n2 = vecNewNodes[ iNext ]->second.back();
3885 n3 = vecNewNodes[ iNode+nbn ]->second.back();
3886 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3887 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
3888 srcElements.Append( myLastCreatedElems.Last() );
3892 for ( iNode = nbn; iNode < 2*nbn; iNode++ ) {
3893 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3897 // sweep free links into faces
3899 if ( hasFreeLinks ) {
3900 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
3901 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
3903 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
3904 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3905 initNodeSet.insert( vecNewNodes[ iNode ]->first );
3906 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
3908 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
3909 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
3911 while ( iVol++ < volNb ) v++;
3912 // find indices of free faces of a volume and their source edges
3913 list< int > freeInd;
3914 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
3915 SMDS_VolumeTool vTool( *v );
3916 int iF, nbF = vTool.NbFaces();
3917 for ( iF = 0; iF < nbF; iF ++ ) {
3918 if (vTool.IsFreeFace( iF ) &&
3919 vTool.GetFaceNodes( iF, faceNodeSet ) &&
3920 initNodeSet != faceNodeSet) // except an initial face
3922 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
3924 freeInd.push_back( iF );
3925 // find source edge of a free face iF
3926 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
3927 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
3928 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
3929 initNodeSet.begin(), initNodeSet.end(),
3930 commonNodes.begin());
3931 if ( (*v)->IsQuadratic() )
3932 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
3934 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
3936 if ( !srcEdges.back() )
3938 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
3939 << iF << " of volume #" << vTool.ID() << endl;
3944 if ( freeInd.empty() )
3947 // create faces for all steps;
3948 // if such a face has been already created by sweep of edge,
3949 // assure that its orientation is OK
3950 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
3952 vTool.SetExternalNormal();
3953 list< int >::iterator ind = freeInd.begin();
3954 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
3955 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
3957 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
3958 int nbn = vTool.NbFaceNodes( *ind );
3960 case 3: { ///// triangle
3961 const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
3963 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
3964 else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 ))
3965 aMesh->ChangeElementNodes( f, nodes, nbn );
3968 case 4: { ///// quadrangle
3969 const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
3971 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ));
3972 else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 ))
3973 aMesh->ChangeElementNodes( f, nodes, nbn );
3977 if( (*v)->IsQuadratic() ) {
3978 if(nbn==6) { /////// quadratic triangle
3979 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4],
3980 nodes[1], nodes[3], nodes[5] );
3982 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
3983 nodes[1], nodes[3], nodes[5]));
3985 else if ( nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) {
3986 const SMDS_MeshNode** tmpnodes = new const SMDS_MeshNode*[6];
3987 tmpnodes[0] = nodes[0];
3988 tmpnodes[1] = nodes[2];
3989 tmpnodes[2] = nodes[4];
3990 tmpnodes[3] = nodes[1];
3991 tmpnodes[4] = nodes[3];
3992 tmpnodes[5] = nodes[5];
3993 aMesh->ChangeElementNodes( f, tmpnodes, nbn );
3996 else { /////// quadratic quadrangle
3997 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
3998 nodes[1], nodes[3], nodes[5], nodes[7] );
4000 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4001 nodes[1], nodes[3], nodes[5], nodes[7]));
4003 else if ( nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) {
4004 const SMDS_MeshNode** tmpnodes = new const SMDS_MeshNode*[8];
4005 tmpnodes[0] = nodes[0];
4006 tmpnodes[1] = nodes[2];
4007 tmpnodes[2] = nodes[4];
4008 tmpnodes[3] = nodes[6];
4009 tmpnodes[4] = nodes[1];
4010 tmpnodes[5] = nodes[3];
4011 tmpnodes[6] = nodes[5];
4012 tmpnodes[7] = nodes[7];
4013 aMesh->ChangeElementNodes( f, tmpnodes, nbn );
4017 else { //////// polygon
4018 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
4019 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4021 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4022 else if ( nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 1 ))
4023 aMesh->ChangeElementNodes( f, nodes, nbn );
4026 while ( srcElements.Length() < myLastCreatedElems.Length() )
4027 srcElements.Append( *srcEdge );
4029 } // loop on free faces
4031 // go to the next volume
4033 while ( iVol++ < nbVolumesByStep ) v++;
4036 } // sweep free links into faces
4038 // Make a ceiling face with a normal external to a volume
4040 SMDS_VolumeTool lastVol( itElem->second.back() );
4042 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4044 lastVol.SetExternalNormal();
4045 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4046 int nbn = lastVol.NbFaceNodes( iF );
4049 if (!hasFreeLinks ||
4050 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4051 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4054 if (!hasFreeLinks ||
4055 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4056 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ));
4059 if(itElem->second.back()->IsQuadratic()) {
4061 if (!hasFreeLinks ||
4062 !aMesh->FindFace(nodes[0], nodes[2], nodes[4],
4063 nodes[1], nodes[3], nodes[5]) ) {
4064 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4065 nodes[1], nodes[3], nodes[5]));
4069 if (!hasFreeLinks ||
4070 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4071 nodes[1], nodes[3], nodes[5], nodes[7]) )
4072 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4073 nodes[1], nodes[3], nodes[5], nodes[7]));
4077 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
4078 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4079 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4083 while ( srcElements.Length() < myLastCreatedElems.Length() )
4084 srcElements.Append( myLastCreatedElems.Last() );
4086 } // loop on swept elements
4089 //=======================================================================
4090 //function : RotationSweep
4092 //=======================================================================
4094 SMESH_MeshEditor::PGroupIDs
4095 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4096 const gp_Ax1& theAxis,
4097 const double theAngle,
4098 const int theNbSteps,
4099 const double theTol,
4100 const bool theMakeGroups,
4101 const bool theMakeWalls)
4103 myLastCreatedElems.Clear();
4104 myLastCreatedNodes.Clear();
4106 // source elements for each generated one
4107 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4109 MESSAGE( "RotationSweep()");
4111 aTrsf.SetRotation( theAxis, theAngle );
4113 aTrsf2.SetRotation( theAxis, theAngle/2. );
4115 gp_Lin aLine( theAxis );
4116 double aSqTol = theTol * theTol;
4118 SMESHDS_Mesh* aMesh = GetMeshDS();
4120 TNodeOfNodeListMap mapNewNodes;
4121 TElemOfVecOfNnlmiMap mapElemNewNodes;
4122 TElemOfElemListMap newElemsMap;
4125 TIDSortedElemSet::iterator itElem;
4126 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4127 const SMDS_MeshElement* elem = *itElem;
4128 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4130 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4131 newNodesItVec.reserve( elem->NbNodes() );
4133 // loop on elem nodes
4134 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4135 while ( itN->more() ) {
4136 // check if a node has been already sweeped
4137 const SMDS_MeshNode* node = cast2Node( itN->next() );
4139 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4141 aXYZ.Coord( coord[0], coord[1], coord[2] );
4142 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4144 TNodeOfNodeListMapItr nIt = mapNewNodes.find( node );
4145 if ( nIt == mapNewNodes.end() ) {
4146 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4147 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4150 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4152 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4153 //bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4154 const SMDS_MeshNode * newNode = node;
4155 for ( int i = 0; i < theNbSteps; i++ ) {
4157 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4159 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4160 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4161 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4162 myLastCreatedNodes.Append(newNode);
4163 srcNodes.Append( node );
4164 listNewNodes.push_back( newNode );
4165 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4166 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4169 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4171 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4172 myLastCreatedNodes.Append(newNode);
4173 srcNodes.Append( node );
4174 listNewNodes.push_back( newNode );
4177 listNewNodes.push_back( newNode );
4178 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4179 listNewNodes.push_back( newNode );
4186 // if current elem is quadratic and current node is not medium
4187 // we have to check - may be it is needed to insert additional nodes
4188 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4189 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4190 if(listNewNodes.size()==theNbSteps) {
4191 listNewNodes.clear();
4193 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4195 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4196 const SMDS_MeshNode * newNode = node;
4198 for(int i = 0; i<theNbSteps; i++) {
4199 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4200 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4201 cout<<" 3 AddNode: "<<newNode;
4202 myLastCreatedNodes.Append(newNode);
4203 listNewNodes.push_back( newNode );
4204 srcNodes.Append( node );
4205 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4206 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4207 cout<<" 4 AddNode: "<<newNode;
4208 myLastCreatedNodes.Append(newNode);
4209 srcNodes.Append( node );
4210 listNewNodes.push_back( newNode );
4214 listNewNodes.push_back( newNode );
4220 newNodesItVec.push_back( nIt );
4222 // make new elements
4223 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4227 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4229 PGroupIDs newGroupIDs;
4230 if ( theMakeGroups )
4231 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4237 //=======================================================================
4238 //function : CreateNode
4240 //=======================================================================
4241 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4244 const double tolnode,
4245 SMESH_SequenceOfNode& aNodes)
4247 myLastCreatedElems.Clear();
4248 myLastCreatedNodes.Clear();
4251 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4253 // try to search in sequence of existing nodes
4254 // if aNodes.Length()>0 we 'nave to use given sequence
4255 // else - use all nodes of mesh
4256 if(aNodes.Length()>0) {
4258 for(i=1; i<=aNodes.Length(); i++) {
4259 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4260 if(P1.Distance(P2)<tolnode)
4261 return aNodes.Value(i);
4265 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4266 while(itn->more()) {
4267 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4268 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4269 if(P1.Distance(P2)<tolnode)
4274 // create new node and return it
4275 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4276 myLastCreatedNodes.Append(NewNode);
4281 //=======================================================================
4282 //function : ExtrusionSweep
4284 //=======================================================================
4286 SMESH_MeshEditor::PGroupIDs
4287 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4288 const gp_Vec& theStep,
4289 const int theNbSteps,
4290 TElemOfElemListMap& newElemsMap,
4291 const bool theMakeGroups,
4293 const double theTolerance)
4295 ExtrusParam aParams;
4296 aParams.myDir = gp_Dir(theStep);
4297 aParams.myNodes.Clear();
4298 aParams.mySteps = new TColStd_HSequenceOfReal;
4300 for(i=1; i<=theNbSteps; i++)
4301 aParams.mySteps->Append(theStep.Magnitude());
4304 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4308 //=======================================================================
4309 //function : ExtrusionSweep
4311 //=======================================================================
4313 SMESH_MeshEditor::PGroupIDs
4314 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4315 ExtrusParam& theParams,
4316 TElemOfElemListMap& newElemsMap,
4317 const bool theMakeGroups,
4319 const double theTolerance)
4321 myLastCreatedElems.Clear();
4322 myLastCreatedNodes.Clear();
4324 // source elements for each generated one
4325 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4327 SMESHDS_Mesh* aMesh = GetMeshDS();
4329 int nbsteps = theParams.mySteps->Length();
4331 TNodeOfNodeListMap mapNewNodes;
4332 //TNodeOfNodeVecMap mapNewNodes;
4333 TElemOfVecOfNnlmiMap mapElemNewNodes;
4334 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4337 TIDSortedElemSet::iterator itElem;
4338 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4339 // check element type
4340 const SMDS_MeshElement* elem = *itElem;
4341 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4344 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4345 //vector<TNodeOfNodeVecMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4346 newNodesItVec.reserve( elem->NbNodes() );
4348 // loop on elem nodes
4349 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4350 while ( itN->more() )
4352 // check if a node has been already sweeped
4353 const SMDS_MeshNode* node = cast2Node( itN->next() );
4354 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
4355 //TNodeOfNodeVecMap::iterator nIt = mapNewNodes.find( node );
4356 if ( nIt == mapNewNodes.end() ) {
4357 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4358 //nIt = mapNewNodes.insert( make_pair( node, vector<const SMDS_MeshNode*>() )).first;
4359 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4360 //vector<const SMDS_MeshNode*>& vecNewNodes = nIt->second;
4361 //vecNewNodes.reserve(nbsteps);
4364 double coord[] = { node->X(), node->Y(), node->Z() };
4365 //int nbsteps = theParams.mySteps->Length();
4366 for ( int i = 0; i < nbsteps; i++ ) {
4367 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4368 // create additional node
4369 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4370 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4371 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4372 if( theFlags & EXTRUSION_FLAG_SEW ) {
4373 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4374 theTolerance, theParams.myNodes);
4375 listNewNodes.push_back( newNode );
4378 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4379 myLastCreatedNodes.Append(newNode);
4380 srcNodes.Append( node );
4381 listNewNodes.push_back( newNode );
4384 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4385 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4386 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4387 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4388 if( theFlags & EXTRUSION_FLAG_SEW ) {
4389 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4390 theTolerance, theParams.myNodes);
4391 listNewNodes.push_back( newNode );
4392 //vecNewNodes[i]=newNode;
4395 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4396 myLastCreatedNodes.Append(newNode);
4397 srcNodes.Append( node );
4398 listNewNodes.push_back( newNode );
4399 //vecNewNodes[i]=newNode;
4404 // if current elem is quadratic and current node is not medium
4405 // we have to check - may be it is needed to insert additional nodes
4406 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4407 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4408 if(listNewNodes.size()==nbsteps) {
4409 listNewNodes.clear();
4410 double coord[] = { node->X(), node->Y(), node->Z() };
4411 for ( int i = 0; i < nbsteps; i++ ) {
4412 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4413 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4414 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4415 if( theFlags & EXTRUSION_FLAG_SEW ) {
4416 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4417 theTolerance, theParams.myNodes);
4418 listNewNodes.push_back( newNode );
4421 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4422 myLastCreatedNodes.Append(newNode);
4423 srcNodes.Append( node );
4424 listNewNodes.push_back( newNode );
4426 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4427 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4428 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4429 if( theFlags & EXTRUSION_FLAG_SEW ) {
4430 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4431 theTolerance, theParams.myNodes);
4432 listNewNodes.push_back( newNode );
4435 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4436 myLastCreatedNodes.Append(newNode);
4437 srcNodes.Append( node );
4438 listNewNodes.push_back( newNode );
4444 newNodesItVec.push_back( nIt );
4446 // make new elements
4447 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4450 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4451 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4453 PGroupIDs newGroupIDs;
4454 if ( theMakeGroups )
4455 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4461 //=======================================================================
4462 //class : SMESH_MeshEditor_PathPoint
4463 //purpose : auxiliary class
4464 //=======================================================================
4465 class SMESH_MeshEditor_PathPoint {
4467 SMESH_MeshEditor_PathPoint() {
4468 myPnt.SetCoord(99., 99., 99.);
4469 myTgt.SetCoord(1.,0.,0.);
4473 void SetPnt(const gp_Pnt& aP3D){
4476 void SetTangent(const gp_Dir& aTgt){
4479 void SetAngle(const double& aBeta){
4482 void SetParameter(const double& aPrm){
4485 const gp_Pnt& Pnt()const{
4488 const gp_Dir& Tangent()const{
4491 double Angle()const{
4494 double Parameter()const{
4506 //=======================================================================
4507 //function : ExtrusionAlongTrack
4509 //=======================================================================
4510 SMESH_MeshEditor::Extrusion_Error
4511 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4512 SMESH_subMesh* theTrack,
4513 const SMDS_MeshNode* theN1,
4514 const bool theHasAngles,
4515 list<double>& theAngles,
4516 const bool theLinearVariation,
4517 const bool theHasRefPoint,
4518 const gp_Pnt& theRefPoint,
4519 const bool theMakeGroups)
4521 myLastCreatedElems.Clear();
4522 myLastCreatedNodes.Clear();
4525 std::list<double> aPrms;
4526 TIDSortedElemSet::iterator itElem;
4529 TopoDS_Edge aTrackEdge;
4530 TopoDS_Vertex aV1, aV2;
4532 SMDS_ElemIteratorPtr aItE;
4533 SMDS_NodeIteratorPtr aItN;
4534 SMDSAbs_ElementType aTypeE;
4536 TNodeOfNodeListMap mapNewNodes;
4539 aNbE = theElements.size();
4542 return EXTR_NO_ELEMENTS;
4544 // 1.1 Track Pattern
4547 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4549 aItE = pSubMeshDS->GetElements();
4550 while ( aItE->more() ) {
4551 const SMDS_MeshElement* pE = aItE->next();
4552 aTypeE = pE->GetType();
4553 // Pattern must contain links only
4554 if ( aTypeE != SMDSAbs_Edge )
4555 return EXTR_PATH_NOT_EDGE;
4558 list<SMESH_MeshEditor_PathPoint> fullList;
4560 const TopoDS_Shape& aS = theTrack->GetSubShape();
4561 // Sub shape for the Pattern must be an Edge or Wire
4562 if( aS.ShapeType() == TopAbs_EDGE ) {
4563 aTrackEdge = TopoDS::Edge( aS );
4564 // the Edge must not be degenerated
4565 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4566 return EXTR_BAD_PATH_SHAPE;
4567 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4568 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4569 const SMDS_MeshNode* aN1 = aItN->next();
4570 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4571 const SMDS_MeshNode* aN2 = aItN->next();
4572 // starting node must be aN1 or aN2
4573 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4574 return EXTR_BAD_STARTING_NODE;
4575 aItN = pSubMeshDS->GetNodes();
4576 while ( aItN->more() ) {
4577 const SMDS_MeshNode* pNode = aItN->next();
4578 const SMDS_EdgePosition* pEPos =
4579 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4580 double aT = pEPos->GetUParameter();
4581 aPrms.push_back( aT );
4583 //Extrusion_Error err =
4584 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4586 else if( aS.ShapeType() == TopAbs_WIRE ) {
4587 list< SMESH_subMesh* > LSM;
4588 TopTools_SequenceOfShape Edges;
4589 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4590 while(itSM->more()) {
4591 SMESH_subMesh* SM = itSM->next();
4593 const TopoDS_Shape& aS = SM->GetSubShape();
4596 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4597 int startNid = theN1->GetID();
4598 TColStd_MapOfInteger UsedNums;
4599 int NbEdges = Edges.Length();
4601 for(; i<=NbEdges; i++) {
4603 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4604 for(; itLSM!=LSM.end(); itLSM++) {
4606 if(UsedNums.Contains(k)) continue;
4607 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4608 SMESH_subMesh* locTrack = *itLSM;
4609 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4610 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4611 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4612 const SMDS_MeshNode* aN1 = aItN->next();
4613 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4614 const SMDS_MeshNode* aN2 = aItN->next();
4615 // starting node must be aN1 or aN2
4616 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4617 // 2. Collect parameters on the track edge
4619 aItN = locMeshDS->GetNodes();
4620 while ( aItN->more() ) {
4621 const SMDS_MeshNode* pNode = aItN->next();
4622 const SMDS_EdgePosition* pEPos =
4623 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4624 double aT = pEPos->GetUParameter();
4625 aPrms.push_back( aT );
4627 list<SMESH_MeshEditor_PathPoint> LPP;
4628 //Extrusion_Error err =
4629 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4630 LLPPs.push_back(LPP);
4632 // update startN for search following egde
4633 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4634 else startNid = aN1->GetID();
4638 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4639 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4640 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4641 for(; itPP!=firstList.end(); itPP++) {
4642 fullList.push_back( *itPP );
4644 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4645 fullList.pop_back();
4647 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4648 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4649 itPP = currList.begin();
4650 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4651 gp_Dir D1 = PP1.Tangent();
4652 gp_Dir D2 = PP2.Tangent();
4653 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4654 (D1.Z()+D2.Z())/2 ) );
4655 PP1.SetTangent(Dnew);
4656 fullList.push_back(PP1);
4658 for(; itPP!=firstList.end(); itPP++) {
4659 fullList.push_back( *itPP );
4661 PP1 = fullList.back();
4662 fullList.pop_back();
4664 // if wire not closed
4665 fullList.push_back(PP1);
4669 return EXTR_BAD_PATH_SHAPE;
4672 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4673 theHasRefPoint, theRefPoint, theMakeGroups);
4677 //=======================================================================
4678 //function : ExtrusionAlongTrack
4680 //=======================================================================
4681 SMESH_MeshEditor::Extrusion_Error
4682 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4683 SMESH_Mesh* theTrack,
4684 const SMDS_MeshNode* theN1,
4685 const bool theHasAngles,
4686 list<double>& theAngles,
4687 const bool theLinearVariation,
4688 const bool theHasRefPoint,
4689 const gp_Pnt& theRefPoint,
4690 const bool theMakeGroups)
4692 myLastCreatedElems.Clear();
4693 myLastCreatedNodes.Clear();
4696 std::list<double> aPrms;
4697 TIDSortedElemSet::iterator itElem;
4700 TopoDS_Edge aTrackEdge;
4701 TopoDS_Vertex aV1, aV2;
4703 SMDS_ElemIteratorPtr aItE;
4704 SMDS_NodeIteratorPtr aItN;
4705 SMDSAbs_ElementType aTypeE;
4707 TNodeOfNodeListMap mapNewNodes;
4710 aNbE = theElements.size();
4713 return EXTR_NO_ELEMENTS;
4715 // 1.1 Track Pattern
4718 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4720 aItE = pMeshDS->elementsIterator();
4721 while ( aItE->more() ) {
4722 const SMDS_MeshElement* pE = aItE->next();
4723 aTypeE = pE->GetType();
4724 // Pattern must contain links only
4725 if ( aTypeE != SMDSAbs_Edge )
4726 return EXTR_PATH_NOT_EDGE;
4729 list<SMESH_MeshEditor_PathPoint> fullList;
4731 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4732 // Sub shape for the Pattern must be an Edge or Wire
4733 if( aS.ShapeType() == TopAbs_EDGE ) {
4734 aTrackEdge = TopoDS::Edge( aS );
4735 // the Edge must not be degenerated
4736 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4737 return EXTR_BAD_PATH_SHAPE;
4738 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4739 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4740 const SMDS_MeshNode* aN1 = aItN->next();
4741 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4742 const SMDS_MeshNode* aN2 = aItN->next();
4743 // starting node must be aN1 or aN2
4744 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4745 return EXTR_BAD_STARTING_NODE;
4746 aItN = pMeshDS->nodesIterator();
4747 while ( aItN->more() ) {
4748 const SMDS_MeshNode* pNode = aItN->next();
4749 if( pNode==aN1 || pNode==aN2 ) continue;
4750 const SMDS_EdgePosition* pEPos =
4751 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4752 double aT = pEPos->GetUParameter();
4753 aPrms.push_back( aT );
4755 //Extrusion_Error err =
4756 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4758 else if( aS.ShapeType() == TopAbs_WIRE ) {
4759 list< SMESH_subMesh* > LSM;
4760 TopTools_SequenceOfShape Edges;
4761 TopExp_Explorer eExp(aS, TopAbs_EDGE);
4762 for(; eExp.More(); eExp.Next()) {
4763 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
4764 if( BRep_Tool::Degenerated(E) ) continue;
4765 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
4771 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4772 int startNid = theN1->GetID();
4773 TColStd_MapOfInteger UsedNums;
4774 int NbEdges = Edges.Length();
4776 for(; i<=NbEdges; i++) {
4778 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4779 for(; itLSM!=LSM.end(); itLSM++) {
4781 if(UsedNums.Contains(k)) continue;
4782 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4783 SMESH_subMesh* locTrack = *itLSM;
4784 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4785 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4786 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4787 const SMDS_MeshNode* aN1 = aItN->next();
4788 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4789 const SMDS_MeshNode* aN2 = aItN->next();
4790 // starting node must be aN1 or aN2
4791 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4792 // 2. Collect parameters on the track edge
4794 aItN = locMeshDS->GetNodes();
4795 while ( aItN->more() ) {
4796 const SMDS_MeshNode* pNode = aItN->next();
4797 const SMDS_EdgePosition* pEPos =
4798 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition().get() );
4799 double aT = pEPos->GetUParameter();
4800 aPrms.push_back( aT );
4802 list<SMESH_MeshEditor_PathPoint> LPP;
4803 //Extrusion_Error err =
4804 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4805 LLPPs.push_back(LPP);
4807 // update startN for search following egde
4808 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4809 else startNid = aN1->GetID();
4813 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4814 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4815 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4816 for(; itPP!=firstList.end(); itPP++) {
4817 fullList.push_back( *itPP );
4819 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4820 fullList.pop_back();
4822 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4823 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4824 itPP = currList.begin();
4825 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4826 gp_Pnt P1 = PP1.Pnt();
4827 //cout<<" PP1: Pnt("<<P1.X()<<","<<P1.Y()<<","<<P1.Z()<<")"<<endl;
4828 gp_Pnt P2 = PP2.Pnt();
4829 gp_Dir D1 = PP1.Tangent();
4830 gp_Dir D2 = PP2.Tangent();
4831 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4832 (D1.Z()+D2.Z())/2 ) );
4833 PP1.SetTangent(Dnew);
4834 fullList.push_back(PP1);
4836 for(; itPP!=currList.end(); itPP++) {
4837 fullList.push_back( *itPP );
4839 PP1 = fullList.back();
4840 fullList.pop_back();
4842 // if wire not closed
4843 fullList.push_back(PP1);
4847 return EXTR_BAD_PATH_SHAPE;
4850 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4851 theHasRefPoint, theRefPoint, theMakeGroups);
4855 //=======================================================================
4856 //function : MakeEdgePathPoints
4857 //purpose : auxilary for ExtrusionAlongTrack
4858 //=======================================================================
4859 SMESH_MeshEditor::Extrusion_Error
4860 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
4861 const TopoDS_Edge& aTrackEdge,
4863 list<SMESH_MeshEditor_PathPoint>& LPP)
4865 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
4867 aTolVec2=aTolVec*aTolVec;
4869 TopoDS_Vertex aV1, aV2;
4870 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4871 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
4872 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
4873 // 2. Collect parameters on the track edge
4874 aPrms.push_front( aT1 );
4875 aPrms.push_back( aT2 );
4878 if( FirstIsStart ) {
4889 SMESH_MeshEditor_PathPoint aPP;
4890 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
4891 std::list<double>::iterator aItD = aPrms.begin();
4892 for(; aItD != aPrms.end(); ++aItD) {
4896 aC3D->D1( aT, aP3D, aVec );
4897 aL2 = aVec.SquareMagnitude();
4898 if ( aL2 < aTolVec2 )
4899 return EXTR_CANT_GET_TANGENT;
4900 gp_Dir aTgt( aVec );
4902 aPP.SetTangent( aTgt );
4903 aPP.SetParameter( aT );
4910 //=======================================================================
4911 //function : MakeExtrElements
4912 //purpose : auxilary for ExtrusionAlongTrack
4913 //=======================================================================
4914 SMESH_MeshEditor::Extrusion_Error
4915 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
4916 list<SMESH_MeshEditor_PathPoint>& fullList,
4917 const bool theHasAngles,
4918 list<double>& theAngles,
4919 const bool theLinearVariation,
4920 const bool theHasRefPoint,
4921 const gp_Pnt& theRefPoint,
4922 const bool theMakeGroups)
4924 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
4925 int aNbTP = fullList.size();
4926 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
4928 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
4929 LinearAngleVariation(aNbTP-1, theAngles);
4931 vector<double> aAngles( aNbTP );
4933 for(; j<aNbTP; ++j) {
4936 if ( theHasAngles ) {
4938 std::list<double>::iterator aItD = theAngles.begin();
4939 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
4941 aAngles[j] = anAngle;
4944 // fill vector of path points with angles
4945 //aPPs.resize(fullList.size());
4947 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
4948 for(; itPP!=fullList.end(); itPP++) {
4950 SMESH_MeshEditor_PathPoint PP = *itPP;
4951 PP.SetAngle(aAngles[j]);
4955 TNodeOfNodeListMap mapNewNodes;
4956 TElemOfVecOfNnlmiMap mapElemNewNodes;
4957 TElemOfElemListMap newElemsMap;
4958 TIDSortedElemSet::iterator itElem;
4961 SMDSAbs_ElementType aTypeE;
4962 // source elements for each generated one
4963 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4965 // 3. Center of rotation aV0
4966 gp_Pnt aV0 = theRefPoint;
4968 if ( !theHasRefPoint ) {
4970 aGC.SetCoord( 0.,0.,0. );
4972 itElem = theElements.begin();
4973 for ( ; itElem != theElements.end(); itElem++ ) {
4974 const SMDS_MeshElement* elem = *itElem;
4976 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4977 while ( itN->more() ) {
4978 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
4983 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
4984 list<const SMDS_MeshNode*> aLNx;
4985 mapNewNodes[node] = aLNx;
4987 gp_XYZ aXYZ( aX, aY, aZ );
4995 } // if (!theHasRefPoint) {
4996 mapNewNodes.clear();
4998 // 4. Processing the elements
4999 SMESHDS_Mesh* aMesh = GetMeshDS();
5001 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5002 // check element type
5003 const SMDS_MeshElement* elem = *itElem;
5004 aTypeE = elem->GetType();
5005 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5008 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5009 newNodesItVec.reserve( elem->NbNodes() );
5011 // loop on elem nodes
5013 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5014 while ( itN->more() )
5017 // check if a node has been already processed
5018 const SMDS_MeshNode* node =
5019 static_cast<const SMDS_MeshNode*>( itN->next() );
5020 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5021 if ( nIt == mapNewNodes.end() ) {
5022 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5023 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5026 aX = node->X(); aY = node->Y(); aZ = node->Z();
5028 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5029 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5030 gp_Ax1 anAx1, anAxT1T0;
5031 gp_Dir aDT1x, aDT0x, aDT1T0;
5036 aPN0.SetCoord(aX, aY, aZ);
5038 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5040 aDT0x= aPP0.Tangent();
5041 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5043 for ( j = 1; j < aNbTP; ++j ) {
5044 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5046 aDT1x = aPP1.Tangent();
5047 aAngle1x = aPP1.Angle();
5049 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5051 gp_Vec aV01x( aP0x, aP1x );
5052 aTrsf.SetTranslation( aV01x );
5055 aV1x = aV0x.Transformed( aTrsf );
5056 aPN1 = aPN0.Transformed( aTrsf );
5058 // rotation 1 [ T1,T0 ]
5059 aAngleT1T0=-aDT1x.Angle( aDT0x );
5060 if (fabs(aAngleT1T0) > aTolAng) {
5062 anAxT1T0.SetLocation( aV1x );
5063 anAxT1T0.SetDirection( aDT1T0 );
5064 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5066 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5070 if ( theHasAngles ) {
5071 anAx1.SetLocation( aV1x );
5072 anAx1.SetDirection( aDT1x );
5073 aTrsfRot.SetRotation( anAx1, aAngle1x );
5075 aPN1 = aPN1.Transformed( aTrsfRot );
5079 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5080 // create additional node
5081 double x = ( aPN1.X() + aPN0.X() )/2.;
5082 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5083 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5084 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5085 myLastCreatedNodes.Append(newNode);
5086 srcNodes.Append( node );
5087 listNewNodes.push_back( newNode );
5092 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5093 myLastCreatedNodes.Append(newNode);
5094 srcNodes.Append( node );
5095 listNewNodes.push_back( newNode );
5105 // if current elem is quadratic and current node is not medium
5106 // we have to check - may be it is needed to insert additional nodes
5107 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5108 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5109 if(listNewNodes.size()==aNbTP-1) {
5110 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5111 gp_XYZ P(node->X(), node->Y(), node->Z());
5112 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5114 for(i=0; i<aNbTP-1; i++) {
5115 const SMDS_MeshNode* N = *it;
5116 double x = ( N->X() + P.X() )/2.;
5117 double y = ( N->Y() + P.Y() )/2.;
5118 double z = ( N->Z() + P.Z() )/2.;
5119 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5120 srcNodes.Append( node );
5121 myLastCreatedNodes.Append(newN);
5124 P = gp_XYZ(N->X(),N->Y(),N->Z());
5126 listNewNodes.clear();
5127 for(i=0; i<2*(aNbTP-1); i++) {
5128 listNewNodes.push_back(aNodes[i]);
5134 newNodesItVec.push_back( nIt );
5136 // make new elements
5137 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5138 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5139 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5142 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5144 if ( theMakeGroups )
5145 generateGroups( srcNodes, srcElems, "extruded");
5151 //=======================================================================
5152 //function : LinearAngleVariation
5153 //purpose : auxilary for ExtrusionAlongTrack
5154 //=======================================================================
5155 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5156 list<double>& Angles)
5158 int nbAngles = Angles.size();
5159 if( nbSteps > nbAngles ) {
5160 vector<double> theAngles(nbAngles);
5161 list<double>::iterator it = Angles.begin();
5163 for(; it!=Angles.end(); it++) {
5165 theAngles[i] = (*it);
5168 double rAn2St = double( nbAngles ) / double( nbSteps );
5169 double angPrev = 0, angle;
5170 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5171 double angCur = rAn2St * ( iSt+1 );
5172 double angCurFloor = floor( angCur );
5173 double angPrevFloor = floor( angPrev );
5174 if ( angPrevFloor == angCurFloor )
5175 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5177 int iP = int( angPrevFloor );
5178 double angPrevCeil = ceil(angPrev);
5179 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5181 int iC = int( angCurFloor );
5182 if ( iC < nbAngles )
5183 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5185 iP = int( angPrevCeil );
5187 angle += theAngles[ iC ];
5189 res.push_back(angle);
5194 for(; it!=res.end(); it++)
5195 Angles.push_back( *it );
5200 //================================================================================
5202 * \brief Move or copy theElements applying theTrsf to their nodes
5203 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5204 * \param theTrsf - transformation to apply
5205 * \param theCopy - if true, create translated copies of theElems
5206 * \param theMakeGroups - if true and theCopy, create translated groups
5207 * \param theTargetMesh - mesh to copy translated elements into
5208 * \retval SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5210 //================================================================================
5212 SMESH_MeshEditor::PGroupIDs
5213 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5214 const gp_Trsf& theTrsf,
5216 const bool theMakeGroups,
5217 SMESH_Mesh* theTargetMesh)
5219 myLastCreatedElems.Clear();
5220 myLastCreatedNodes.Clear();
5222 bool needReverse = false;
5223 string groupPostfix;
5224 switch ( theTrsf.Form() ) {
5229 groupPostfix = "mirrored";
5232 groupPostfix = "rotated";
5234 case gp_Translation:
5235 groupPostfix = "translated";
5238 case gp_CompoundTrsf: // different scale by axis
5239 groupPostfix = "scaled";
5242 needReverse = false;
5243 groupPostfix = "transformed";
5246 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5247 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5248 SMESHDS_Mesh* aMesh = GetMeshDS();
5251 // map old node to new one
5252 TNodeNodeMap nodeMap;
5254 // elements sharing moved nodes; those of them which have all
5255 // nodes mirrored but are not in theElems are to be reversed
5256 TIDSortedElemSet inverseElemSet;
5258 // source elements for each generated one
5259 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5261 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5262 list<SMDS_MeshNode> orphanCopy; // copies of orphan nodes
5263 vector<const SMDS_MeshNode*> orphanNode; // original orphan nodes
5265 if ( theElems.empty() ) // transform the whole mesh
5268 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5269 while ( eIt->more() ) theElems.insert( eIt->next() );
5271 SMDS_MeshElementIDFactory idFactory;
5272 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5273 while ( nIt->more() )
5275 const SMDS_MeshNode* node = nIt->next();
5276 if ( node->NbInverseElements() == 0 && !theElems.insert( node ).second )
5278 // node was not inserted into theElems because an element with the same ID
5279 // is already there. As a work around we insert a copy of node with
5280 // an ID = -<index in orphanNode>
5281 orphanCopy.push_back( *node ); // copy node
5282 SMDS_MeshNode* nodeCopy = &orphanCopy.back();
5283 int uniqueID = -orphanNode.size();
5284 orphanNode.push_back( node );
5285 idFactory.BindID( uniqueID, nodeCopy );
5286 theElems.insert( nodeCopy );
5290 // loop on theElems to transorm nodes
5291 TIDSortedElemSet::iterator itElem;
5292 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5293 const SMDS_MeshElement* elem = *itElem;
5297 // loop on elem nodes
5298 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5299 while ( itN->more() ) {
5301 const SMDS_MeshNode* node = cast2Node( itN->next() );
5302 if ( node->GetID() < 0 )
5303 node = orphanNode[ -node->GetID() ];
5304 // check if a node has been already transformed
5305 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5306 nodeMap.insert( make_pair ( node, node ));
5307 if ( !n2n_isnew.second )
5311 coord[0] = node->X();
5312 coord[1] = node->Y();
5313 coord[2] = node->Z();
5314 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5315 if ( theTargetMesh ) {
5316 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5317 n2n_isnew.first->second = newNode;
5318 myLastCreatedNodes.Append(newNode);
5319 srcNodes.Append( node );
5321 else if ( theCopy ) {
5322 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5323 n2n_isnew.first->second = newNode;
5324 myLastCreatedNodes.Append(newNode);
5325 srcNodes.Append( node );
5328 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5329 // node position on shape becomes invalid
5330 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5331 ( SMDS_SpacePosition::originSpacePosition() );
5334 // keep inverse elements
5335 if ( !theCopy && !theTargetMesh && needReverse ) {
5336 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5337 while ( invElemIt->more() ) {
5338 const SMDS_MeshElement* iel = invElemIt->next();
5339 inverseElemSet.insert( iel );
5345 // either create new elements or reverse mirrored ones
5346 if ( !theCopy && !needReverse && !theTargetMesh )
5349 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5350 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5351 theElems.insert( *invElemIt );
5353 // replicate or reverse elements
5356 REV_TETRA = 0, // = nbNodes - 4
5357 REV_PYRAMID = 1, // = nbNodes - 4
5358 REV_PENTA = 2, // = nbNodes - 4
5360 REV_HEXA = 4, // = nbNodes - 4
5364 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA
5365 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID
5366 { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA
5367 { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE
5368 { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA
5369 { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD
5372 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5374 const SMDS_MeshElement* elem = *itElem;
5375 if ( !elem || elem->GetType() == SMDSAbs_Node )
5378 int nbNodes = elem->NbNodes();
5379 int elemType = elem->GetType();
5381 if (elem->IsPoly()) {
5382 // Polygon or Polyhedral Volume
5383 switch ( elemType ) {
5386 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5388 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5389 while (itN->more()) {
5390 const SMDS_MeshNode* node =
5391 static_cast<const SMDS_MeshNode*>(itN->next());
5392 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5393 if (nodeMapIt == nodeMap.end())
5394 break; // not all nodes transformed
5396 // reverse mirrored faces and volumes
5397 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5399 poly_nodes[iNode] = (*nodeMapIt).second;
5403 if ( iNode != nbNodes )
5404 continue; // not all nodes transformed
5406 if ( theTargetMesh ) {
5407 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5408 srcElems.Append( elem );
5410 else if ( theCopy ) {
5411 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5412 srcElems.Append( elem );
5415 aMesh->ChangePolygonNodes(elem, poly_nodes);
5419 case SMDSAbs_Volume:
5421 // ATTENTION: Reversing is not yet done!!!
5422 const SMDS_PolyhedralVolumeOfNodes* aPolyedre =
5423 dynamic_cast<const SMDS_PolyhedralVolumeOfNodes*>( elem );
5425 MESSAGE("Warning: bad volumic element");
5429 vector<const SMDS_MeshNode*> poly_nodes;
5430 vector<int> quantities;
5432 bool allTransformed = true;
5433 int nbFaces = aPolyedre->NbFaces();
5434 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5435 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5436 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5437 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5438 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5439 if (nodeMapIt == nodeMap.end()) {
5440 allTransformed = false; // not all nodes transformed
5442 poly_nodes.push_back((*nodeMapIt).second);
5445 quantities.push_back(nbFaceNodes);
5447 if ( !allTransformed )
5448 continue; // not all nodes transformed
5450 if ( theTargetMesh ) {
5451 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5452 srcElems.Append( elem );
5454 else if ( theCopy ) {
5455 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5456 srcElems.Append( elem );
5459 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5469 int* i = index[ FORWARD ];
5470 if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes
5471 if ( elemType == SMDSAbs_Face )
5472 i = index[ REV_FACE ];
5474 i = index[ nbNodes - 4 ];
5476 if(elem->IsQuadratic()) {
5477 static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
5480 if(nbNodes==3) { // quadratic edge
5481 static int anIds[] = {1,0,2};
5484 else if(nbNodes==6) { // quadratic triangle
5485 static int anIds[] = {0,2,1,5,4,3};
5488 else if(nbNodes==8) { // quadratic quadrangle
5489 static int anIds[] = {0,3,2,1,7,6,5,4};
5492 else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes
5493 static int anIds[] = {0,2,1,3,6,5,4,7,9,8};
5496 else if(nbNodes==13) { // quadratic pyramid of 13 nodes
5497 static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10};
5500 else if(nbNodes==15) { // quadratic pentahedron with 15 nodes
5501 static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13};
5504 else { // nbNodes==20 - quadratic hexahedron with 20 nodes
5505 static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17};
5511 // find transformed nodes
5512 vector<const SMDS_MeshNode*> nodes(nbNodes);
5514 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5515 while ( itN->more() ) {
5516 const SMDS_MeshNode* node =
5517 static_cast<const SMDS_MeshNode*>( itN->next() );
5518 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5519 if ( nodeMapIt == nodeMap.end() )
5520 break; // not all nodes transformed
5521 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5523 if ( iNode != nbNodes )
5524 continue; // not all nodes transformed
5526 if ( theTargetMesh ) {
5527 if ( SMDS_MeshElement* copy =
5528 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5529 myLastCreatedElems.Append( copy );
5530 srcElems.Append( elem );
5533 else if ( theCopy ) {
5534 if ( SMDS_MeshElement* copy = AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5535 myLastCreatedElems.Append( copy );
5536 srcElems.Append( elem );
5540 // reverse element as it was reversed by transformation
5542 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5546 PGroupIDs newGroupIDs;
5548 if ( theMakeGroups && theCopy ||
5549 theMakeGroups && theTargetMesh )
5550 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5555 //=======================================================================
5557 * \brief Create groups of elements made during transformation
5558 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5559 * \param elemGens - elements making corresponding myLastCreatedElems
5560 * \param postfix - to append to names of new groups
5562 //=======================================================================
5564 SMESH_MeshEditor::PGroupIDs
5565 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5566 const SMESH_SequenceOfElemPtr& elemGens,
5567 const std::string& postfix,
5568 SMESH_Mesh* targetMesh)
5570 PGroupIDs newGroupIDs( new list<int> );
5571 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5573 // Sort existing groups by types and collect their names
5575 // to store an old group and a generated new one
5576 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5577 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5579 set< string > groupNames;
5581 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5582 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5583 while ( groupIt->more() ) {
5584 SMESH_Group * group = groupIt->next();
5585 if ( !group ) continue;
5586 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5587 if ( !groupDS || groupDS->IsEmpty() ) continue;
5588 groupNames.insert( group->GetName() );
5589 groupDS->SetStoreName( group->GetName() );
5590 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5595 // loop on nodes and elements
5596 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5598 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5599 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5600 if ( gens.Length() != elems.Length() )
5601 throw SALOME_Exception(LOCALIZED("invalid args"));
5603 // loop on created elements
5604 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5606 const SMDS_MeshElement* sourceElem = gens( iElem );
5607 if ( !sourceElem ) {
5608 MESSAGE("generateGroups(): NULL source element");
5611 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5612 if ( groupsOldNew.empty() ) {
5613 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5614 ++iElem; // skip all elements made by sourceElem
5617 // collect all elements made by sourceElem
5618 list< const SMDS_MeshElement* > resultElems;
5619 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5620 if ( resElem != sourceElem )
5621 resultElems.push_back( resElem );
5622 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5623 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5624 if ( resElem != sourceElem )
5625 resultElems.push_back( resElem );
5626 // do not generate element groups from node ones
5627 if ( sourceElem->GetType() == SMDSAbs_Node &&
5628 elems( iElem )->GetType() != SMDSAbs_Node )
5631 // add resultElems to groups made by ones the sourceElem belongs to
5632 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5633 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5635 SMESHDS_GroupBase* oldGroup = gOldNew->first;
5636 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5638 SMDS_MeshGroup* & newGroup = gOldNew->second;
5639 if ( !newGroup )// create a new group
5642 string name = oldGroup->GetStoreName();
5643 if ( !targetMesh ) {
5647 while ( !groupNames.insert( name ).second ) // name exists
5653 TCollection_AsciiString nbStr(nb+1);
5654 name.resize( name.rfind('_')+1 );
5655 name += nbStr.ToCString();
5662 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5664 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5665 newGroup = & groupDS->SMDSGroup();
5666 newGroupIDs->push_back( id );
5669 // fill in a new group
5670 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5671 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5672 newGroup->Add( *resElemIt );
5675 } // loop on created elements
5676 }// loop on nodes and elements
5681 //================================================================================
5683 * \brief Return list of group of nodes close to each other within theTolerance
5684 * Search among theNodes or in the whole mesh if theNodes is empty using
5685 * an Octree algorithm
5687 //================================================================================
5689 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
5690 const double theTolerance,
5691 TListOfListOfNodes & theGroupsOfNodes)
5693 myLastCreatedElems.Clear();
5694 myLastCreatedNodes.Clear();
5696 if ( theNodes.empty() )
5697 { // get all nodes in the mesh
5698 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
5699 while ( nIt->more() )
5700 theNodes.insert( theNodes.end(),nIt->next());
5703 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
5707 //=======================================================================
5709 * \brief Implementation of search for the node closest to point
5711 //=======================================================================
5713 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
5715 //---------------------------------------------------------------------
5717 * \brief Constructor
5719 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
5721 myMesh = ( SMESHDS_Mesh* ) theMesh;
5723 TIDSortedNodeSet nodes;
5725 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
5726 while ( nIt->more() )
5727 nodes.insert( nodes.end(), nIt->next() );
5729 myOctreeNode = new SMESH_OctreeNode(nodes) ;
5731 // get max size of a leaf box
5732 SMESH_OctreeNode* tree = myOctreeNode;
5733 while ( !tree->isLeaf() )
5735 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5739 myHalfLeafSize = tree->maxSize() / 2.;
5742 //---------------------------------------------------------------------
5744 * \brief Move node and update myOctreeNode accordingly
5746 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
5748 myOctreeNode->UpdateByMoveNode( node, toPnt );
5749 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
5752 //---------------------------------------------------------------------
5754 * \brief Do it's job
5756 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
5758 SMDS_MeshNode tgtNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5759 map<double, const SMDS_MeshNode*> dist2Nodes;
5760 myOctreeNode->NodesAround( &tgtNode, dist2Nodes, myHalfLeafSize );
5761 if ( !dist2Nodes.empty() )
5762 return dist2Nodes.begin()->second;
5763 list<const SMDS_MeshNode*> nodes;
5764 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
5766 double minSqDist = DBL_MAX;
5767 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
5769 // sort leafs by their distance from thePnt
5770 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
5771 TDistTreeMap treeMap;
5772 list< SMESH_OctreeNode* > treeList;
5773 list< SMESH_OctreeNode* >::iterator trIt;
5774 treeList.push_back( myOctreeNode );
5776 SMDS_MeshNode pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5777 bool pointInside = myOctreeNode->isInside( &pointNode, myHalfLeafSize );
5778 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
5780 SMESH_OctreeNode* tree = *trIt;
5781 if ( !tree->isLeaf() ) // put children to the queue
5783 if ( pointInside && !tree->isInside( &pointNode, myHalfLeafSize )) continue;
5784 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5785 while ( cIt->more() )
5786 treeList.push_back( cIt->next() );
5788 else if ( tree->NbNodes() ) // put a tree to the treeMap
5790 const Bnd_B3d& box = tree->getBox();
5791 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
5792 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
5793 if ( !it_in.second ) // not unique distance to box center
5794 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
5797 // find distance after which there is no sense to check tree's
5798 double sqLimit = DBL_MAX;
5799 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
5800 if ( treeMap.size() > 5 ) {
5801 SMESH_OctreeNode* closestTree = sqDist_tree->second;
5802 const Bnd_B3d& box = closestTree->getBox();
5803 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
5804 sqLimit = limit * limit;
5806 // get all nodes from trees
5807 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
5808 if ( sqDist_tree->first > sqLimit )
5810 SMESH_OctreeNode* tree = sqDist_tree->second;
5811 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
5814 // find closest among nodes
5815 minSqDist = DBL_MAX;
5816 const SMDS_MeshNode* closestNode = 0;
5817 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
5818 for ( ; nIt != nodes.end(); ++nIt ) {
5819 double sqDist = thePnt.SquareDistance( SMESH_MeshEditor::TNodeXYZ( *nIt ) );
5820 if ( minSqDist > sqDist ) {
5828 //---------------------------------------------------------------------
5832 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
5834 //---------------------------------------------------------------------
5836 * \brief Return the node tree
5838 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
5841 SMESH_OctreeNode* myOctreeNode;
5842 SMESHDS_Mesh* myMesh;
5843 double myHalfLeafSize; // max size of a leaf box
5846 //=======================================================================
5848 * \brief Return SMESH_NodeSearcher
5850 //=======================================================================
5852 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
5854 return new SMESH_NodeSearcherImpl( GetMeshDS() );
5857 // ========================================================================
5858 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
5860 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
5861 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
5862 const double NodeRadius = 1e-9; // to enlarge bnd box of element
5864 //=======================================================================
5866 * \brief Octal tree of bounding boxes of elements
5868 //=======================================================================
5870 class ElementBndBoxTree : public SMESH_Octree
5874 ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType);
5875 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
5876 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
5877 ~ElementBndBoxTree();
5880 ElementBndBoxTree() {}
5881 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
5882 void buildChildrenData();
5883 Bnd_B3d* buildRootBox();
5885 //!< Bounding box of element
5886 struct ElementBox : public Bnd_B3d
5888 const SMDS_MeshElement* _element;
5889 int _refCount; // an ElementBox can be included in several tree branches
5890 ElementBox(const SMDS_MeshElement* elem);
5892 vector< ElementBox* > _elements;
5895 //================================================================================
5897 * \brief ElementBndBoxTree creation
5899 //================================================================================
5901 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType)
5902 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
5904 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
5905 _elements.reserve( nbElems );
5907 SMDS_ElemIteratorPtr elemIt = mesh.elementsIterator( elemType );
5908 while ( elemIt->more() )
5909 _elements.push_back( new ElementBox( elemIt->next() ));
5911 if ( _elements.size() > MaxNbElemsInLeaf )
5917 //================================================================================
5921 //================================================================================
5923 ElementBndBoxTree::~ElementBndBoxTree()
5925 for ( int i = 0; i < _elements.size(); ++i )
5926 if ( --_elements[i]->_refCount <= 0 )
5927 delete _elements[i];
5930 //================================================================================
5932 * \brief Return the maximal box
5934 //================================================================================
5936 Bnd_B3d* ElementBndBoxTree::buildRootBox()
5938 Bnd_B3d* box = new Bnd_B3d;
5939 for ( int i = 0; i < _elements.size(); ++i )
5940 box->Add( *_elements[i] );
5944 //================================================================================
5946 * \brief Redistrubute element boxes among children
5948 //================================================================================
5950 void ElementBndBoxTree::buildChildrenData()
5952 for ( int i = 0; i < _elements.size(); ++i )
5954 for (int j = 0; j < 8; j++)
5956 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
5958 _elements[i]->_refCount++;
5959 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
5962 _elements[i]->_refCount--;
5966 for (int j = 0; j < 8; j++)
5968 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
5969 if ( child->_elements.size() <= MaxNbElemsInLeaf )
5970 child->myIsLeaf = true;
5972 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
5973 child->_elements.resize( child->_elements.size() ); // compact
5977 //================================================================================
5979 * \brief Return elements which can include the point
5981 //================================================================================
5983 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
5984 TIDSortedElemSet& foundElems)
5986 if ( level() && getBox().IsOut( point.XYZ() ))
5991 for ( int i = 0; i < _elements.size(); ++i )
5992 if ( !_elements[i]->IsOut( point.XYZ() ))
5993 foundElems.insert( _elements[i]->_element );
5997 for (int i = 0; i < 8; i++)
5998 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6002 //================================================================================
6004 * \brief Return elements which can be intersected by the line
6006 //================================================================================
6008 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6009 TIDSortedElemSet& foundElems)
6011 if ( level() && getBox().IsOut( line ))
6016 for ( int i = 0; i < _elements.size(); ++i )
6017 if ( !_elements[i]->IsOut( line ))
6018 foundElems.insert( _elements[i]->_element );
6022 for (int i = 0; i < 8; i++)
6023 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6027 //================================================================================
6029 * \brief Construct the element box
6031 //================================================================================
6033 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem)
6037 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6038 while ( nIt->more() )
6039 Add( SMESH_MeshEditor::TNodeXYZ( cast2Node( nIt->next() )));
6040 Enlarge( NodeRadius );
6045 //=======================================================================
6047 * \brief Implementation of search for the elements by point and
6048 * of classification of point in 2D mesh
6050 //=======================================================================
6052 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6054 SMESHDS_Mesh* _mesh;
6055 ElementBndBoxTree* _ebbTree;
6056 SMESH_NodeSearcherImpl* _nodeSearcher;
6057 SMDSAbs_ElementType _elementType;
6059 bool _outerFacesFound;
6060 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6062 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh )
6063 : _mesh(&mesh),_ebbTree(0),_nodeSearcher(0), _tolerance(-1), _outerFacesFound(false) {}
6064 ~SMESH_ElementSearcherImpl()
6066 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6067 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6069 virtual int FindElementsByPoint(const gp_Pnt& point,
6070 SMDSAbs_ElementType type,
6071 vector< const SMDS_MeshElement* >& foundElements);
6072 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6074 void GetElementsNearLine( const gp_Ax1& line,
6075 SMDSAbs_ElementType type,
6076 vector< const SMDS_MeshElement* >& foundElems);
6077 double getTolerance();
6078 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6079 const double tolerance, double & param);
6080 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6081 bool isOuterBoundary(const SMDS_MeshElement* face) const
6083 return _outerFaces.empty() || _outerFaces.count(face);
6085 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6087 const SMDS_MeshElement* _face;
6089 bool _coincides; //!< the line lays in face plane
6090 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6091 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6093 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6096 TIDSortedElemSet _faces;
6097 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6098 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6102 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6104 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6105 << ", _coincides="<<i._coincides << ")";
6108 //=======================================================================
6110 * \brief define tolerance for search
6112 //=======================================================================
6114 double SMESH_ElementSearcherImpl::getTolerance()
6116 if ( _tolerance < 0 )
6118 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6121 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6123 double boxSize = _nodeSearcher->getTree()->maxSize();
6124 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6126 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6128 double boxSize = _ebbTree->maxSize();
6129 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6131 if ( _tolerance == 0 )
6133 // define tolerance by size of a most complex element
6134 int complexType = SMDSAbs_Volume;
6135 while ( complexType > SMDSAbs_All &&
6136 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6138 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6141 if ( complexType == int( SMDSAbs_Node ))
6143 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6145 if ( meshInfo.NbNodes() > 2 )
6146 elemSize = SMESH_MeshEditor::TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6150 const SMDS_MeshElement* elem =
6151 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ))->next();
6152 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6153 SMESH_MeshEditor::TNodeXYZ n1( cast2Node( nodeIt->next() ));
6154 while ( nodeIt->more() )
6156 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6157 elemSize = max( dist, elemSize );
6160 _tolerance = 1e-6 * elemSize;
6166 //================================================================================
6168 * \brief Find intersection of the line and an edge of face and return parameter on line
6170 //================================================================================
6172 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6173 const SMDS_MeshElement* face,
6180 GeomAPI_ExtremaCurveCurve anExtCC;
6181 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6183 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6184 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6186 GC_MakeSegment edge( SMESH_MeshEditor::TNodeXYZ( face->GetNode( i )),
6187 SMESH_MeshEditor::TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6188 anExtCC.Init( lineCurve, edge);
6189 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6191 Quantity_Parameter pl, pe;
6192 anExtCC.LowerDistanceParameters( pl, pe );
6194 if ( ++nbInts == 2 )
6198 if ( nbInts > 0 ) param /= nbInts;
6201 //================================================================================
6203 * \brief Find all faces belonging to the outer boundary of mesh
6205 //================================================================================
6207 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6209 if ( _outerFacesFound ) return;
6211 // Collect all outer faces by passing from one outer face to another via their links
6212 // and BTW find out if there are internal faces at all.
6214 // checked links and links where outer boundary meets internal one
6215 set< SMESH_TLink > visitedLinks, seamLinks;
6217 // links to treat with already visited faces sharing them
6218 list < TFaceLink > startLinks;
6220 // load startLinks with the first outerFace
6221 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6222 _outerFaces.insert( outerFace );
6224 TIDSortedElemSet emptySet;
6225 while ( !startLinks.empty() )
6227 const SMESH_TLink& link = startLinks.front()._link;
6228 TIDSortedElemSet& faces = startLinks.front()._faces;
6230 outerFace = *faces.begin();
6231 // find other faces sharing the link
6232 const SMDS_MeshElement* f;
6233 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6236 // select another outer face among the found
6237 const SMDS_MeshElement* outerFace2 = 0;
6238 if ( faces.size() == 2 )
6240 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6242 else if ( faces.size() > 2 )
6244 seamLinks.insert( link );
6246 // link direction within the outerFace
6247 gp_Vec n1n2( SMESH_MeshEditor::TNodeXYZ( link.node1()),
6248 SMESH_MeshEditor::TNodeXYZ( link.node2()));
6249 int i1 = outerFace->GetNodeIndex( link.node1() );
6250 int i2 = outerFace->GetNodeIndex( link.node2() );
6251 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6252 if ( rev ) n1n2.Reverse();
6254 gp_XYZ ofNorm, fNorm;
6255 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6257 // direction from the link inside outerFace
6258 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6259 // sort all other faces by angle with the dirInOF
6260 map< double, const SMDS_MeshElement* > angle2Face;
6261 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6262 for ( ; face != faces.end(); ++face )
6264 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6266 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6267 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6268 if ( angle < 0 ) angle += 2*PI;
6269 angle2Face.insert( make_pair( angle, *face ));
6271 if ( !angle2Face.empty() )
6272 outerFace2 = angle2Face.begin()->second;
6275 // store the found outer face and add its links to continue seaching from
6278 _outerFaces.insert( outerFace );
6279 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6280 for ( int i = 0; i < nbNodes; ++i )
6282 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6283 if ( visitedLinks.insert( link2 ).second )
6284 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6287 startLinks.pop_front();
6289 _outerFacesFound = true;
6291 if ( !seamLinks.empty() )
6293 // There are internal boundaries touching the outher one,
6294 // find all faces of internal boundaries in order to find
6295 // faces of boundaries of holes, if any.
6300 _outerFaces.clear();
6304 //=======================================================================
6306 * \brief Find elements of given type where the given point is IN or ON.
6307 * Returns nb of found elements and elements them-selves.
6309 * 'ALL' type means elements of any type excluding nodes and 0D elements
6311 //=======================================================================
6313 int SMESH_ElementSearcherImpl::
6314 FindElementsByPoint(const gp_Pnt& point,
6315 SMDSAbs_ElementType type,
6316 vector< const SMDS_MeshElement* >& foundElements)
6318 foundElements.clear();
6320 double tolerance = getTolerance();
6322 // =================================================================================
6323 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6325 if ( !_nodeSearcher )
6326 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6328 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6329 if ( !closeNode ) return foundElements.size();
6331 if ( point.Distance( SMESH_MeshEditor::TNodeXYZ( closeNode )) > tolerance )
6332 return foundElements.size(); // to far from any node
6334 if ( type == SMDSAbs_Node )
6336 foundElements.push_back( closeNode );
6340 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6341 while ( elemIt->more() )
6342 foundElements.push_back( elemIt->next() );
6345 // =================================================================================
6346 else // elements more complex than 0D
6348 if ( !_ebbTree || _elementType != type )
6350 if ( _ebbTree ) delete _ebbTree;
6351 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type );
6353 TIDSortedElemSet suspectElems;
6354 _ebbTree->getElementsNearPoint( point, suspectElems );
6355 TIDSortedElemSet::iterator elem = suspectElems.begin();
6356 for ( ; elem != suspectElems.end(); ++elem )
6357 if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6358 foundElements.push_back( *elem );
6360 return foundElements.size();
6363 //================================================================================
6365 * \brief Classify the given point in the closed 2D mesh
6367 //================================================================================
6369 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6371 double tolerance = getTolerance();
6372 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6374 if ( _ebbTree ) delete _ebbTree;
6375 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face );
6377 // Algo: analyse transition of a line starting at the point through mesh boundary;
6378 // try three lines parallel to axis of the coordinate system and perform rough
6379 // analysis. If solution is not clear perform thorough analysis.
6381 const int nbAxes = 3;
6382 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6383 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6384 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6385 multimap< int, int > nbInt2Axis; // to find the simplest case
6386 for ( int axis = 0; axis < nbAxes; ++axis )
6388 gp_Ax1 lineAxis( point, axisDir[axis]);
6389 gp_Lin line ( lineAxis );
6391 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6392 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6394 // Intersect faces with the line
6396 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6397 TIDSortedElemSet::iterator face = suspectFaces.begin();
6398 for ( ; face != suspectFaces.end(); ++face )
6402 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6403 gp_Pln facePlane( SMESH_MeshEditor::TNodeXYZ( (*face)->GetNode(0)), fNorm );
6405 // perform intersection
6406 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6407 if ( !intersection.IsDone() )
6409 if ( intersection.IsInQuadric() )
6411 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6413 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6415 gp_Pnt intersectionPoint = intersection.Point(1);
6416 if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6417 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6420 // Analyse intersections roughly
6422 int nbInter = u2inters.size();
6426 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6427 if ( nbInter == 1 ) // not closed mesh
6428 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6430 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6433 if ( (f<0) == (l<0) )
6436 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6437 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6438 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6441 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6443 if ( _outerFacesFound ) break; // pass to thorough analysis
6445 } // three attempts - loop on CS axes
6447 // Analyse intersections thoroughly.
6448 // We make two loops maximum, on the first one we only exclude touching intersections,
6449 // on the second, if situation is still unclear, we gather and use information on
6450 // position of faces (internal or outer). If faces position is already gathered,
6451 // we make the second loop right away.
6453 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6455 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6456 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6458 int axis = nb_axis->second;
6459 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6461 gp_Ax1 lineAxis( point, axisDir[axis]);
6462 gp_Lin line ( lineAxis );
6464 // add tangent intersections to u2inters
6466 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6467 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6468 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6469 u2inters.insert(make_pair( param, *tgtInt ));
6470 tangentInters[ axis ].clear();
6472 // Count intersections before and after the point excluding touching ones.
6473 // If hasPositionInfo we count intersections of outer boundary only
6475 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6476 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6477 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6478 bool ok = ! u_int1->second._coincides;
6479 while ( ok && u_int1 != u2inters.end() )
6481 double u = u_int1->first;
6482 bool touchingInt = false;
6483 if ( ++u_int2 != u2inters.end() )
6485 // skip intersections at the same point (if the line passes through edge or node)
6487 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6493 // skip tangent intersections
6495 const SMDS_MeshElement* prevFace = u_int1->second._face;
6496 while ( ok && u_int2->second._coincides )
6498 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6504 ok = ( u_int2 != u2inters.end() );
6509 // skip intersections at the same point after tangent intersections
6512 double u2 = u_int2->first;
6514 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6520 // decide if we skipped a touching intersection
6521 if ( nbSamePnt + nbTgt > 0 )
6523 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6524 map< double, TInters >::iterator u_int = u_int1;
6525 for ( ; u_int != u_int2; ++u_int )
6527 if ( u_int->second._coincides ) continue;
6528 double dot = u_int->second._faceNorm * line.Direction();
6529 if ( dot > maxDot ) maxDot = dot;
6530 if ( dot < minDot ) minDot = dot;
6532 touchingInt = ( minDot*maxDot < 0 );
6537 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6548 u_int1 = u_int2; // to next intersection
6550 } // loop on intersections with one line
6554 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6557 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6560 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6561 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6563 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6566 if ( (f<0) == (l<0) )
6569 if ( hasPositionInfo )
6570 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
6572 } // loop on intersections of the tree lines - thorough analysis
6574 if ( !hasPositionInfo )
6576 // gather info on faces position - is face in the outer boundary or not
6577 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
6578 findOuterBoundary( u2inters.begin()->second._face );
6581 } // two attempts - with and w/o faces position info in the mesh
6583 return TopAbs_UNKNOWN;
6586 //=======================================================================
6588 * \brief Return elements possibly intersecting the line
6590 //=======================================================================
6592 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
6593 SMDSAbs_ElementType type,
6594 vector< const SMDS_MeshElement* >& foundElems)
6596 if ( !_ebbTree || _elementType != type )
6598 if ( _ebbTree ) delete _ebbTree;
6599 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type );
6601 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
6602 _ebbTree->getElementsNearLine( line, suspectFaces );
6603 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
6606 //=======================================================================
6608 * \brief Return SMESH_ElementSearcher
6610 //=======================================================================
6612 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
6614 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
6617 //=======================================================================
6619 * \brief Return true if the point is IN or ON of the element
6621 //=======================================================================
6623 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
6625 if ( element->GetType() == SMDSAbs_Volume)
6627 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
6630 // get ordered nodes
6632 vector< gp_XYZ > xyz;
6634 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
6635 if ( element->IsQuadratic() )
6636 if (const SMDS_QuadraticFaceOfNodes* f=dynamic_cast<const SMDS_QuadraticFaceOfNodes*>(element))
6637 nodeIt = f->interlacedNodesElemIterator();
6638 else if (const SMDS_QuadraticEdge* e =dynamic_cast<const SMDS_QuadraticEdge*>(element))
6639 nodeIt = e->interlacedNodesElemIterator();
6641 while ( nodeIt->more() )
6642 xyz.push_back( TNodeXYZ( cast2Node( nodeIt->next() )));
6644 int i, nbNodes = element->NbNodes();
6646 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
6648 // compute face normal
6649 gp_Vec faceNorm(0,0,0);
6650 xyz.push_back( xyz.front() );
6651 for ( i = 0; i < nbNodes; ++i )
6653 gp_Vec edge1( xyz[i+1], xyz[i]);
6654 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
6655 faceNorm += edge1 ^ edge2;
6657 double normSize = faceNorm.Magnitude();
6658 if ( normSize <= tol )
6660 // degenerated face: point is out if it is out of all face edges
6661 for ( i = 0; i < nbNodes; ++i )
6663 SMDS_MeshNode n1( xyz[i].X(), xyz[i].Y(), xyz[i].Z() );
6664 SMDS_MeshNode n2( xyz[i+1].X(), xyz[i+1].Y(), xyz[i+1].Z() );
6665 SMDS_MeshEdge edge( &n1, &n2 );
6666 if ( !isOut( &edge, point, tol ))
6671 faceNorm /= normSize;
6673 // check if the point lays on face plane
6674 gp_Vec n2p( xyz[0], point );
6675 if ( fabs( n2p * faceNorm ) > tol )
6676 return true; // not on face plane
6678 // check if point is out of face boundary:
6679 // define it by closest transition of a ray point->infinity through face boundary
6680 // on the face plane.
6681 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
6682 // to find intersections of the ray with the boundary.
6684 gp_Vec plnNorm = ray ^ faceNorm;
6685 normSize = plnNorm.Magnitude();
6686 if ( normSize <= tol ) return false; // point coincides with the first node
6687 plnNorm /= normSize;
6688 // for each node of the face, compute its signed distance to the plane
6689 vector<double> dist( nbNodes + 1);
6690 for ( i = 0; i < nbNodes; ++i )
6692 gp_Vec n2p( xyz[i], point );
6693 dist[i] = n2p * plnNorm;
6695 dist.back() = dist.front();
6696 // find the closest intersection
6698 double rClosest, distClosest = 1e100;;
6700 for ( i = 0; i < nbNodes; ++i )
6703 if ( fabs( dist[i]) < tol )
6705 else if ( fabs( dist[i+1]) < tol )
6707 else if ( dist[i] * dist[i+1] < 0 )
6708 r = dist[i] / ( dist[i] - dist[i+1] );
6710 continue; // no intersection
6711 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
6712 gp_Vec p2int ( point, pInt);
6713 if ( p2int * ray > -tol ) // right half-space
6715 double intDist = p2int.SquareMagnitude();
6716 if ( intDist < distClosest )
6721 distClosest = intDist;
6726 return true; // no intesections - out
6728 // analyse transition
6729 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
6730 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
6731 gp_Vec p2int ( point, pClosest );
6732 bool out = (edgeNorm * p2int) < -tol;
6733 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
6736 // ray pass through a face node; analyze transition through an adjacent edge
6737 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
6738 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
6739 gp_Vec edgeAdjacent( p1, p2 );
6740 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
6741 bool out2 = (edgeNorm2 * p2int) < -tol;
6743 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
6744 return covexCorner ? (out || out2) : (out && out2);
6746 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
6748 // point is out of edge if it is NOT ON any straight part of edge
6749 // (we consider quadratic edge as being composed of two straight parts)
6750 for ( i = 1; i < nbNodes; ++i )
6752 gp_Vec edge( xyz[i-1], xyz[i]);
6753 gp_Vec n1p ( xyz[i-1], point);
6754 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
6757 gp_Vec n2p( xyz[i], point );
6758 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
6760 return false; // point is ON this part
6764 // Node or 0D element -------------------------------------------------------------------------
6766 gp_Vec n2p ( xyz[0], point );
6767 return n2p.Magnitude() <= tol;
6772 //=======================================================================
6773 //function : SimplifyFace
6775 //=======================================================================
6776 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
6777 vector<const SMDS_MeshNode *>& poly_nodes,
6778 vector<int>& quantities) const
6780 int nbNodes = faceNodes.size();
6785 set<const SMDS_MeshNode*> nodeSet;
6787 // get simple seq of nodes
6788 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6789 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6790 int iSimple = 0, nbUnique = 0;
6792 simpleNodes[iSimple++] = faceNodes[0];
6794 for (int iCur = 1; iCur < nbNodes; iCur++) {
6795 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6796 simpleNodes[iSimple++] = faceNodes[iCur];
6797 if (nodeSet.insert( faceNodes[iCur] ).second)
6801 int nbSimple = iSimple;
6802 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6812 bool foundLoop = (nbSimple > nbUnique);
6815 set<const SMDS_MeshNode*> loopSet;
6816 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6817 const SMDS_MeshNode* n = simpleNodes[iSimple];
6818 if (!loopSet.insert( n ).second) {
6822 int iC = 0, curLast = iSimple;
6823 for (; iC < curLast; iC++) {
6824 if (simpleNodes[iC] == n) break;
6826 int loopLen = curLast - iC;
6828 // create sub-element
6830 quantities.push_back(loopLen);
6831 for (; iC < curLast; iC++) {
6832 poly_nodes.push_back(simpleNodes[iC]);
6835 // shift the rest nodes (place from the first loop position)
6836 for (iC = curLast + 1; iC < nbSimple; iC++) {
6837 simpleNodes[iC - loopLen] = simpleNodes[iC];
6839 nbSimple -= loopLen;
6842 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6843 } // while (foundLoop)
6847 quantities.push_back(iSimple);
6848 for (int i = 0; i < iSimple; i++)
6849 poly_nodes.push_back(simpleNodes[i]);
6855 //=======================================================================
6856 //function : MergeNodes
6857 //purpose : In each group, the cdr of nodes are substituted by the first one
6859 //=======================================================================
6861 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6863 myLastCreatedElems.Clear();
6864 myLastCreatedNodes.Clear();
6866 SMESHDS_Mesh* aMesh = GetMeshDS();
6868 TNodeNodeMap nodeNodeMap; // node to replace - new node
6869 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6870 list< int > rmElemIds, rmNodeIds;
6872 // Fill nodeNodeMap and elems
6874 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6875 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6876 list<const SMDS_MeshNode*>& nodes = *grIt;
6877 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6878 const SMDS_MeshNode* nToKeep = *nIt;
6879 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6880 const SMDS_MeshNode* nToRemove = *nIt;
6881 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6882 if ( nToRemove != nToKeep ) {
6883 rmNodeIds.push_back( nToRemove->GetID() );
6884 AddToSameGroups( nToKeep, nToRemove, aMesh );
6887 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6888 while ( invElemIt->more() ) {
6889 const SMDS_MeshElement* elem = invElemIt->next();
6894 // Change element nodes or remove an element
6896 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6897 for ( ; eIt != elems.end(); eIt++ ) {
6898 const SMDS_MeshElement* elem = *eIt;
6899 int nbNodes = elem->NbNodes();
6900 int aShapeId = FindShape( elem );
6902 set<const SMDS_MeshNode*> nodeSet;
6903 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6904 int iUnique = 0, iCur = 0, nbRepl = 0;
6905 vector<int> iRepl( nbNodes );
6907 // get new seq of nodes
6908 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6909 while ( itN->more() ) {
6910 const SMDS_MeshNode* n =
6911 static_cast<const SMDS_MeshNode*>( itN->next() );
6913 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6914 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6916 // BUG 0020185: begin
6918 bool stopRecur = false;
6919 set<const SMDS_MeshNode*> nodesRecur;
6920 nodesRecur.insert(n);
6921 while (!stopRecur) {
6922 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6923 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6924 n = (*nnIt_i).second;
6925 if (!nodesRecur.insert(n).second) {
6926 // error: recursive dependancy
6935 iRepl[ nbRepl++ ] = iCur;
6937 curNodes[ iCur ] = n;
6938 bool isUnique = nodeSet.insert( n ).second;
6940 uniqueNodes[ iUnique++ ] = n;
6944 // Analyse element topology after replacement
6947 int nbUniqueNodes = nodeSet.size();
6948 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6949 // Polygons and Polyhedral volumes
6950 if (elem->IsPoly()) {
6952 if (elem->GetType() == SMDSAbs_Face) {
6954 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6956 for (; inode < nbNodes; inode++) {
6957 face_nodes[inode] = curNodes[inode];
6960 vector<const SMDS_MeshNode *> polygons_nodes;
6961 vector<int> quantities;
6962 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6966 for (int iface = 0; iface < nbNew - 1; iface++) {
6967 int nbNodes = quantities[iface];
6968 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6969 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6970 poly_nodes[ii] = polygons_nodes[inode];
6972 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6973 myLastCreatedElems.Append(newElem);
6975 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6977 aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6980 rmElemIds.push_back(elem->GetID());
6984 else if (elem->GetType() == SMDSAbs_Volume) {
6985 // Polyhedral volume
6986 if (nbUniqueNodes < 4) {
6987 rmElemIds.push_back(elem->GetID());
6990 // each face has to be analized in order to check volume validity
6991 const SMDS_PolyhedralVolumeOfNodes* aPolyedre =
6992 static_cast<const SMDS_PolyhedralVolumeOfNodes*>( elem );
6994 int nbFaces = aPolyedre->NbFaces();
6996 vector<const SMDS_MeshNode *> poly_nodes;
6997 vector<int> quantities;
6999 for (int iface = 1; iface <= nbFaces; iface++) {
7000 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7001 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7003 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7004 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7005 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7006 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7007 faceNode = (*nnIt).second;
7009 faceNodes[inode - 1] = faceNode;
7012 SimplifyFace(faceNodes, poly_nodes, quantities);
7015 if (quantities.size() > 3) {
7016 // to be done: remove coincident faces
7019 if (quantities.size() > 3)
7020 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7022 rmElemIds.push_back(elem->GetID());
7026 rmElemIds.push_back(elem->GetID());
7037 switch ( nbNodes ) {
7038 case 2: ///////////////////////////////////// EDGE
7039 isOk = false; break;
7040 case 3: ///////////////////////////////////// TRIANGLE
7041 isOk = false; break;
7043 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7045 else { //////////////////////////////////// QUADRANGLE
7046 if ( nbUniqueNodes < 3 )
7048 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7049 isOk = false; // opposite nodes stick
7052 case 6: ///////////////////////////////////// PENTAHEDRON
7053 if ( nbUniqueNodes == 4 ) {
7054 // ---------------------------------> tetrahedron
7056 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7057 // all top nodes stick: reverse a bottom
7058 uniqueNodes[ 0 ] = curNodes [ 1 ];
7059 uniqueNodes[ 1 ] = curNodes [ 0 ];
7061 else if (nbRepl == 3 &&
7062 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7063 // all bottom nodes stick: set a top before
7064 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7065 uniqueNodes[ 0 ] = curNodes [ 3 ];
7066 uniqueNodes[ 1 ] = curNodes [ 4 ];
7067 uniqueNodes[ 2 ] = curNodes [ 5 ];
7069 else if (nbRepl == 4 &&
7070 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7071 // a lateral face turns into a line: reverse a bottom
7072 uniqueNodes[ 0 ] = curNodes [ 1 ];
7073 uniqueNodes[ 1 ] = curNodes [ 0 ];
7078 else if ( nbUniqueNodes == 5 ) {
7079 // PENTAHEDRON --------------------> 2 tetrahedrons
7080 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7081 // a bottom node sticks with a linked top one
7083 SMDS_MeshElement* newElem =
7084 aMesh->AddVolume(curNodes[ 3 ],
7087 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7088 myLastCreatedElems.Append(newElem);
7090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7091 // 2. : reverse a bottom
7092 uniqueNodes[ 0 ] = curNodes [ 1 ];
7093 uniqueNodes[ 1 ] = curNodes [ 0 ];
7103 if(elem->IsQuadratic()) { // Quadratic quadrangle
7116 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7117 uniqueNodes[0] = curNodes[0];
7118 uniqueNodes[1] = curNodes[2];
7119 uniqueNodes[2] = curNodes[3];
7120 uniqueNodes[3] = curNodes[5];
7121 uniqueNodes[4] = curNodes[6];
7122 uniqueNodes[5] = curNodes[7];
7125 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7126 uniqueNodes[0] = curNodes[0];
7127 uniqueNodes[1] = curNodes[1];
7128 uniqueNodes[2] = curNodes[2];
7129 uniqueNodes[3] = curNodes[4];
7130 uniqueNodes[4] = curNodes[5];
7131 uniqueNodes[5] = curNodes[6];
7134 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7135 uniqueNodes[0] = curNodes[1];
7136 uniqueNodes[1] = curNodes[2];
7137 uniqueNodes[2] = curNodes[3];
7138 uniqueNodes[3] = curNodes[5];
7139 uniqueNodes[4] = curNodes[6];
7140 uniqueNodes[5] = curNodes[0];
7143 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7144 uniqueNodes[0] = curNodes[0];
7145 uniqueNodes[1] = curNodes[1];
7146 uniqueNodes[2] = curNodes[3];
7147 uniqueNodes[3] = curNodes[4];
7148 uniqueNodes[4] = curNodes[6];
7149 uniqueNodes[5] = curNodes[7];
7152 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7153 uniqueNodes[0] = curNodes[0];
7154 uniqueNodes[1] = curNodes[2];
7155 uniqueNodes[2] = curNodes[3];
7156 uniqueNodes[3] = curNodes[1];
7157 uniqueNodes[4] = curNodes[6];
7158 uniqueNodes[5] = curNodes[7];
7161 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7162 uniqueNodes[0] = curNodes[0];
7163 uniqueNodes[1] = curNodes[1];
7164 uniqueNodes[2] = curNodes[2];
7165 uniqueNodes[3] = curNodes[4];
7166 uniqueNodes[4] = curNodes[5];
7167 uniqueNodes[5] = curNodes[7];
7170 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7171 uniqueNodes[0] = curNodes[0];
7172 uniqueNodes[1] = curNodes[1];
7173 uniqueNodes[2] = curNodes[3];
7174 uniqueNodes[3] = curNodes[4];
7175 uniqueNodes[4] = curNodes[2];
7176 uniqueNodes[5] = curNodes[7];
7179 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7180 uniqueNodes[0] = curNodes[0];
7181 uniqueNodes[1] = curNodes[1];
7182 uniqueNodes[2] = curNodes[2];
7183 uniqueNodes[3] = curNodes[4];
7184 uniqueNodes[4] = curNodes[5];
7185 uniqueNodes[5] = curNodes[3];
7191 //////////////////////////////////// HEXAHEDRON
7193 SMDS_VolumeTool hexa (elem);
7194 hexa.SetExternalNormal();
7195 if ( nbUniqueNodes == 4 && nbRepl == 6 ) {
7196 //////////////////////// ---> tetrahedron
7197 for ( int iFace = 0; iFace < 6; iFace++ ) {
7198 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7199 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7200 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7201 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7202 // one face turns into a point ...
7203 int iOppFace = hexa.GetOppFaceIndex( iFace );
7204 ind = hexa.GetFaceNodesIndices( iOppFace );
7206 iUnique = 2; // reverse a tetrahedron bottom
7207 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7208 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7210 else if ( iUnique >= 0 )
7211 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7213 if ( nbStick == 1 ) {
7214 // ... and the opposite one - into a triangle.
7216 ind = hexa.GetFaceNodesIndices( iFace );
7217 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7224 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7225 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7226 for ( int iFace = 0; iFace < 6; iFace++ ) {
7227 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7228 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7229 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7230 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7231 // one face turns into a point ...
7232 int iOppFace = hexa.GetOppFaceIndex( iFace );
7233 ind = hexa.GetFaceNodesIndices( iOppFace );
7235 iUnique = 2; // reverse a tetrahedron 1 bottom
7236 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7237 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7239 else if ( iUnique >= 0 )
7240 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7242 if ( nbStick == 0 ) {
7243 // ... and the opposite one is a quadrangle
7245 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7246 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7249 SMDS_MeshElement* newElem =
7250 aMesh->AddVolume(curNodes[ind[ 0 ]],
7253 curNodes[indTop[ 0 ]]);
7254 myLastCreatedElems.Append(newElem);
7256 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7263 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7264 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7265 // find indices of quad and tri faces
7266 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7267 for ( iFace = 0; iFace < 6; iFace++ ) {
7268 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7270 for ( iCur = 0; iCur < 4; iCur++ )
7271 nodeSet.insert( curNodes[ind[ iCur ]] );
7272 nbUniqueNodes = nodeSet.size();
7273 if ( nbUniqueNodes == 3 )
7274 iTriFace[ nbTri++ ] = iFace;
7275 else if ( nbUniqueNodes == 4 )
7276 iQuadFace[ nbQuad++ ] = iFace;
7278 if (nbQuad == 2 && nbTri == 4 &&
7279 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7280 // 2 opposite quadrangles stuck with a diagonal;
7281 // sample groups of merged indices: (0-4)(2-6)
7282 // --------------------------------------------> 2 tetrahedrons
7283 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7284 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7285 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7286 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7287 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7288 // stuck with 0-2 diagonal
7296 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7297 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7298 // stuck with 1-3 diagonal
7310 uniqueNodes[ 0 ] = curNodes [ i0 ];
7311 uniqueNodes[ 1 ] = curNodes [ i1d ];
7312 uniqueNodes[ 2 ] = curNodes [ i3d ];
7313 uniqueNodes[ 3 ] = curNodes [ i0t ];
7316 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7320 myLastCreatedElems.Append(newElem);
7322 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7325 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7326 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7327 // --------------------------------------------> prism
7328 // find 2 opposite triangles
7330 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7331 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7332 // find indices of kept and replaced nodes
7333 // and fill unique nodes of 2 opposite triangles
7334 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7335 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7336 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7337 // fill unique nodes
7340 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7341 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7342 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7344 // iCur of a linked node of the opposite face (make normals co-directed):
7345 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7346 // check that correspondent corners of triangles are linked
7347 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7350 uniqueNodes[ iUnique ] = n;
7351 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7360 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7366 } // switch ( nbNodes )
7368 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7371 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume) {
7372 // Change nodes of polyedre
7373 const SMDS_PolyhedralVolumeOfNodes* aPolyedre =
7374 static_cast<const SMDS_PolyhedralVolumeOfNodes*>( elem );
7376 int nbFaces = aPolyedre->NbFaces();
7378 vector<const SMDS_MeshNode *> poly_nodes;
7379 vector<int> quantities (nbFaces);
7381 for (int iface = 1; iface <= nbFaces; iface++) {
7382 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7383 quantities[iface - 1] = nbFaceNodes;
7385 for (inode = 1; inode <= nbFaceNodes; inode++) {
7386 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7388 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7389 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7390 curNode = (*nnIt).second;
7392 poly_nodes.push_back(curNode);
7395 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7399 // Change regular element or polygon
7400 aMesh->ChangeElementNodes( elem, & uniqueNodes[0], nbUniqueNodes );
7404 // Remove invalid regular element or invalid polygon
7405 rmElemIds.push_back( elem->GetID() );
7408 } // loop on elements
7410 // Remove equal nodes and bad elements
7412 Remove( rmNodeIds, true );
7413 Remove( rmElemIds, false );
7418 // ========================================================
7419 // class : SortableElement
7420 // purpose : allow sorting elements basing on their nodes
7421 // ========================================================
7422 class SortableElement : public set <const SMDS_MeshElement*>
7426 SortableElement( const SMDS_MeshElement* theElem )
7429 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7430 while ( nodeIt->more() )
7431 this->insert( nodeIt->next() );
7434 const SMDS_MeshElement* Get() const
7437 void Set(const SMDS_MeshElement* e) const
7442 mutable const SMDS_MeshElement* myElem;
7445 //=======================================================================
7446 //function : FindEqualElements
7447 //purpose : Return list of group of elements built on the same nodes.
7448 // Search among theElements or in the whole mesh if theElements is empty
7449 //=======================================================================
7450 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7451 TListOfListOfElementsID & theGroupsOfElementsID)
7453 myLastCreatedElems.Clear();
7454 myLastCreatedNodes.Clear();
7456 typedef set<const SMDS_MeshElement*> TElemsSet;
7457 typedef map< SortableElement, int > TMapOfNodeSet;
7458 typedef list<int> TGroupOfElems;
7461 if ( theElements.empty() )
7462 { // get all elements in the mesh
7463 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7464 while ( eIt->more() )
7465 elems.insert( elems.end(), eIt->next());
7468 elems = theElements;
7470 vector< TGroupOfElems > arrayOfGroups;
7471 TGroupOfElems groupOfElems;
7472 TMapOfNodeSet mapOfNodeSet;
7474 TElemsSet::iterator elemIt = elems.begin();
7475 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
7476 const SMDS_MeshElement* curElem = *elemIt;
7477 SortableElement SE(curElem);
7480 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7481 if( !(pp.second) ) {
7482 TMapOfNodeSet::iterator& itSE = pp.first;
7483 ind = (*itSE).second;
7484 arrayOfGroups[ind].push_back(curElem->GetID());
7487 groupOfElems.clear();
7488 groupOfElems.push_back(curElem->GetID());
7489 arrayOfGroups.push_back(groupOfElems);
7494 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7495 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7496 groupOfElems = *groupIt;
7497 if ( groupOfElems.size() > 1 ) {
7498 groupOfElems.sort();
7499 theGroupsOfElementsID.push_back(groupOfElems);
7504 //=======================================================================
7505 //function : MergeElements
7506 //purpose : In each given group, substitute all elements by the first one.
7507 //=======================================================================
7509 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7511 myLastCreatedElems.Clear();
7512 myLastCreatedNodes.Clear();
7514 typedef list<int> TListOfIDs;
7515 TListOfIDs rmElemIds; // IDs of elems to remove
7517 SMESHDS_Mesh* aMesh = GetMeshDS();
7519 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7520 while ( groupsIt != theGroupsOfElementsID.end() ) {
7521 TListOfIDs& aGroupOfElemID = *groupsIt;
7522 aGroupOfElemID.sort();
7523 int elemIDToKeep = aGroupOfElemID.front();
7524 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7525 aGroupOfElemID.pop_front();
7526 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7527 while ( idIt != aGroupOfElemID.end() ) {
7528 int elemIDToRemove = *idIt;
7529 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7530 // add the kept element in groups of removed one (PAL15188)
7531 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7532 rmElemIds.push_back( elemIDToRemove );
7538 Remove( rmElemIds, false );
7541 //=======================================================================
7542 //function : MergeEqualElements
7543 //purpose : Remove all but one of elements built on the same nodes.
7544 //=======================================================================
7546 void SMESH_MeshEditor::MergeEqualElements()
7548 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
7549 to merge equal elements in the whole mesh */
7550 TListOfListOfElementsID aGroupsOfElementsID;
7551 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7552 MergeElements(aGroupsOfElementsID);
7555 //=======================================================================
7556 //function : FindFaceInSet
7557 //purpose : Return a face having linked nodes n1 and n2 and which is
7558 // - not in avoidSet,
7559 // - in elemSet provided that !elemSet.empty()
7560 // i1 and i2 optionally returns indices of n1 and n2
7561 //=======================================================================
7563 const SMDS_MeshElement*
7564 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
7565 const SMDS_MeshNode* n2,
7566 const TIDSortedElemSet& elemSet,
7567 const TIDSortedElemSet& avoidSet,
7573 const SMDS_MeshElement* face = 0;
7575 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
7576 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
7578 const SMDS_MeshElement* elem = invElemIt->next();
7579 if (avoidSet.count( elem ))
7581 if ( !elemSet.empty() && !elemSet.count( elem ))
7584 i1 = elem->GetNodeIndex( n1 );
7585 // find a n2 linked to n1
7586 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
7587 for ( int di = -1; di < 2 && !face; di += 2 )
7589 i2 = (i1+di+nbN) % nbN;
7590 if ( elem->GetNode( i2 ) == n2 )
7593 if ( !face && elem->IsQuadratic())
7595 // analysis for quadratic elements using all nodes
7596 const SMDS_QuadraticFaceOfNodes* F =
7597 static_cast<const SMDS_QuadraticFaceOfNodes*>(elem);
7598 // use special nodes iterator
7599 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
7600 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
7601 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
7603 const SMDS_MeshNode* n = cast2Node( anIter->next() );
7604 if ( n1 == prevN && n2 == n )
7608 else if ( n2 == prevN && n1 == n )
7610 face = elem; swap( i1, i2 );
7616 if ( n1ind ) *n1ind = i1;
7617 if ( n2ind ) *n2ind = i2;
7621 //=======================================================================
7622 //function : findAdjacentFace
7624 //=======================================================================
7626 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7627 const SMDS_MeshNode* n2,
7628 const SMDS_MeshElement* elem)
7630 TIDSortedElemSet elemSet, avoidSet;
7632 avoidSet.insert ( elem );
7633 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
7636 //=======================================================================
7637 //function : FindFreeBorder
7639 //=======================================================================
7641 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7643 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7644 const SMDS_MeshNode* theSecondNode,
7645 const SMDS_MeshNode* theLastNode,
7646 list< const SMDS_MeshNode* > & theNodes,
7647 list< const SMDS_MeshElement* >& theFaces)
7649 if ( !theFirstNode || !theSecondNode )
7651 // find border face between theFirstNode and theSecondNode
7652 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7656 theFaces.push_back( curElem );
7657 theNodes.push_back( theFirstNode );
7658 theNodes.push_back( theSecondNode );
7660 //vector<const SMDS_MeshNode*> nodes;
7661 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7662 TIDSortedElemSet foundElems;
7663 bool needTheLast = ( theLastNode != 0 );
7665 while ( nStart != theLastNode ) {
7666 if ( nStart == theFirstNode )
7667 return !needTheLast;
7669 // find all free border faces sharing form nStart
7671 list< const SMDS_MeshElement* > curElemList;
7672 list< const SMDS_MeshNode* > nStartList;
7673 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7674 while ( invElemIt->more() ) {
7675 const SMDS_MeshElement* e = invElemIt->next();
7676 if ( e == curElem || foundElems.insert( e ).second ) {
7678 int iNode = 0, nbNodes = e->NbNodes();
7679 //const SMDS_MeshNode* nodes[nbNodes+1];
7680 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7682 if(e->IsQuadratic()) {
7683 const SMDS_QuadraticFaceOfNodes* F =
7684 static_cast<const SMDS_QuadraticFaceOfNodes*>(e);
7685 // use special nodes iterator
7686 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
7687 while( anIter->more() ) {
7688 nodes[ iNode++ ] = anIter->next();
7692 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7693 while ( nIt->more() )
7694 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7696 nodes[ iNode ] = nodes[ 0 ];
7698 for ( iNode = 0; iNode < nbNodes; iNode++ )
7699 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7700 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7701 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7703 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7704 curElemList.push_back( e );
7708 // analyse the found
7710 int nbNewBorders = curElemList.size();
7711 if ( nbNewBorders == 0 ) {
7712 // no free border furthermore
7713 return !needTheLast;
7715 else if ( nbNewBorders == 1 ) {
7716 // one more element found
7718 nStart = nStartList.front();
7719 curElem = curElemList.front();
7720 theFaces.push_back( curElem );
7721 theNodes.push_back( nStart );
7724 // several continuations found
7725 list< const SMDS_MeshElement* >::iterator curElemIt;
7726 list< const SMDS_MeshNode* >::iterator nStartIt;
7727 // check if one of them reached the last node
7728 if ( needTheLast ) {
7729 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7730 curElemIt!= curElemList.end();
7731 curElemIt++, nStartIt++ )
7732 if ( *nStartIt == theLastNode ) {
7733 theFaces.push_back( *curElemIt );
7734 theNodes.push_back( *nStartIt );
7738 // find the best free border by the continuations
7739 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7740 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7741 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7742 curElemIt!= curElemList.end();
7743 curElemIt++, nStartIt++ )
7745 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7746 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7747 // find one more free border
7748 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7752 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7753 // choice: clear a worse one
7754 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7755 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7756 contNodes[ iWorse ].clear();
7757 contFaces[ iWorse ].clear();
7760 if ( contNodes[0].empty() && contNodes[1].empty() )
7763 // append the best free border
7764 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7765 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7766 theNodes.pop_back(); // remove nIgnore
7767 theNodes.pop_back(); // remove nStart
7768 theFaces.pop_back(); // remove curElem
7769 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7770 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7771 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7772 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7775 } // several continuations found
7776 } // while ( nStart != theLastNode )
7781 //=======================================================================
7782 //function : CheckFreeBorderNodes
7783 //purpose : Return true if the tree nodes are on a free border
7784 //=======================================================================
7786 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7787 const SMDS_MeshNode* theNode2,
7788 const SMDS_MeshNode* theNode3)
7790 list< const SMDS_MeshNode* > nodes;
7791 list< const SMDS_MeshElement* > faces;
7792 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7795 //=======================================================================
7796 //function : SewFreeBorder
7798 //=======================================================================
7800 SMESH_MeshEditor::Sew_Error
7801 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7802 const SMDS_MeshNode* theBordSecondNode,
7803 const SMDS_MeshNode* theBordLastNode,
7804 const SMDS_MeshNode* theSideFirstNode,
7805 const SMDS_MeshNode* theSideSecondNode,
7806 const SMDS_MeshNode* theSideThirdNode,
7807 const bool theSideIsFreeBorder,
7808 const bool toCreatePolygons,
7809 const bool toCreatePolyedrs)
7811 myLastCreatedElems.Clear();
7812 myLastCreatedNodes.Clear();
7814 MESSAGE("::SewFreeBorder()");
7815 Sew_Error aResult = SEW_OK;
7817 // ====================================
7818 // find side nodes and elements
7819 // ====================================
7821 list< const SMDS_MeshNode* > nSide[ 2 ];
7822 list< const SMDS_MeshElement* > eSide[ 2 ];
7823 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7824 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7828 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7829 nSide[0], eSide[0])) {
7830 MESSAGE(" Free Border 1 not found " );
7831 aResult = SEW_BORDER1_NOT_FOUND;
7833 if (theSideIsFreeBorder) {
7836 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7837 nSide[1], eSide[1])) {
7838 MESSAGE(" Free Border 2 not found " );
7839 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7842 if ( aResult != SEW_OK )
7845 if (!theSideIsFreeBorder) {
7849 // -------------------------------------------------------------------------
7851 // 1. If nodes to merge are not coincident, move nodes of the free border
7852 // from the coord sys defined by the direction from the first to last
7853 // nodes of the border to the correspondent sys of the side 2
7854 // 2. On the side 2, find the links most co-directed with the correspondent
7855 // links of the free border
7856 // -------------------------------------------------------------------------
7858 // 1. Since sewing may brake if there are volumes to split on the side 2,
7859 // we wont move nodes but just compute new coordinates for them
7860 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7861 TNodeXYZMap nBordXYZ;
7862 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7863 list< const SMDS_MeshNode* >::iterator nBordIt;
7865 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7866 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7867 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7868 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7869 double tol2 = 1.e-8;
7870 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7871 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7872 // Need node movement.
7874 // find X and Z axes to create trsf
7875 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7877 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7879 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7882 gp_Ax3 toBordAx( Pb1, Zb, X );
7883 gp_Ax3 fromSideAx( Ps1, Zs, X );
7884 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7886 gp_Trsf toBordSys, fromSide2Sys;
7887 toBordSys.SetTransformation( toBordAx );
7888 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7889 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7892 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7893 const SMDS_MeshNode* n = *nBordIt;
7894 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7895 toBordSys.Transforms( xyz );
7896 fromSide2Sys.Transforms( xyz );
7897 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7901 // just insert nodes XYZ in the nBordXYZ map
7902 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7903 const SMDS_MeshNode* n = *nBordIt;
7904 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7908 // 2. On the side 2, find the links most co-directed with the correspondent
7909 // links of the free border
7911 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7912 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7913 sideNodes.push_back( theSideFirstNode );
7915 bool hasVolumes = false;
7916 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7917 set<long> foundSideLinkIDs, checkedLinkIDs;
7918 SMDS_VolumeTool volume;
7919 //const SMDS_MeshNode* faceNodes[ 4 ];
7921 const SMDS_MeshNode* sideNode;
7922 const SMDS_MeshElement* sideElem;
7923 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7924 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7925 nBordIt = bordNodes.begin();
7927 // border node position and border link direction to compare with
7928 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7929 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7930 // choose next side node by link direction or by closeness to
7931 // the current border node:
7932 bool searchByDir = ( *nBordIt != theBordLastNode );
7934 // find the next node on the Side 2
7936 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7938 checkedLinkIDs.clear();
7939 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7941 // loop on inverse elements of current node (prevSideNode) on the Side 2
7942 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7943 while ( invElemIt->more() )
7945 const SMDS_MeshElement* elem = invElemIt->next();
7946 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7947 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7948 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7949 bool isVolume = volume.Set( elem );
7950 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7951 if ( isVolume ) // --volume
7953 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7954 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7955 if(elem->IsQuadratic()) {
7956 const SMDS_QuadraticFaceOfNodes* F =
7957 static_cast<const SMDS_QuadraticFaceOfNodes*>(elem);
7958 // use special nodes iterator
7959 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
7960 while( anIter->more() ) {
7961 nodes[ iNode ] = anIter->next();
7962 if ( nodes[ iNode++ ] == prevSideNode )
7963 iPrevNode = iNode - 1;
7967 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7968 while ( nIt->more() ) {
7969 nodes[ iNode ] = cast2Node( nIt->next() );
7970 if ( nodes[ iNode++ ] == prevSideNode )
7971 iPrevNode = iNode - 1;
7974 // there are 2 links to check
7979 // loop on links, to be precise, on the second node of links
7980 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7981 const SMDS_MeshNode* n = nodes[ iNode ];
7983 if ( !volume.IsLinked( n, prevSideNode ))
7987 if ( iNode ) // a node before prevSideNode
7988 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7989 else // a node after prevSideNode
7990 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7992 // check if this link was already used
7993 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7994 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7995 if (!isJustChecked &&
7996 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7998 // test a link geometrically
7999 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8000 bool linkIsBetter = false;
8001 double dot = 0.0, dist = 0.0;
8002 if ( searchByDir ) { // choose most co-directed link
8003 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8004 linkIsBetter = ( dot > maxDot );
8006 else { // choose link with the node closest to bordPos
8007 dist = ( nextXYZ - bordPos ).SquareModulus();
8008 linkIsBetter = ( dist < minDist );
8010 if ( linkIsBetter ) {
8019 } // loop on inverse elements of prevSideNode
8022 MESSAGE(" Cant find path by links of the Side 2 ");
8023 return SEW_BAD_SIDE_NODES;
8025 sideNodes.push_back( sideNode );
8026 sideElems.push_back( sideElem );
8027 foundSideLinkIDs.insert ( linkID );
8028 prevSideNode = sideNode;
8030 if ( *nBordIt == theBordLastNode )
8031 searchByDir = false;
8033 // find the next border link to compare with
8034 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8035 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8036 // move to next border node if sideNode is before forward border node (bordPos)
8037 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8038 prevBordNode = *nBordIt;
8040 bordPos = nBordXYZ[ *nBordIt ];
8041 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8042 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8046 while ( sideNode != theSideSecondNode );
8048 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8049 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8050 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8052 } // end nodes search on the side 2
8054 // ============================
8055 // sew the border to the side 2
8056 // ============================
8058 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8059 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8061 TListOfListOfNodes nodeGroupsToMerge;
8062 if ( nbNodes[0] == nbNodes[1] ||
8063 ( theSideIsFreeBorder && !theSideThirdNode)) {
8065 // all nodes are to be merged
8067 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8068 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8069 nIt[0]++, nIt[1]++ )
8071 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8072 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8073 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8078 // insert new nodes into the border and the side to get equal nb of segments
8080 // get normalized parameters of nodes on the borders
8081 //double param[ 2 ][ maxNbNodes ];
8083 param[0] = new double [ maxNbNodes ];
8084 param[1] = new double [ maxNbNodes ];
8086 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8087 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8088 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8089 const SMDS_MeshNode* nPrev = *nIt;
8090 double bordLength = 0;
8091 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8092 const SMDS_MeshNode* nCur = *nIt;
8093 gp_XYZ segment (nCur->X() - nPrev->X(),
8094 nCur->Y() - nPrev->Y(),
8095 nCur->Z() - nPrev->Z());
8096 double segmentLen = segment.Modulus();
8097 bordLength += segmentLen;
8098 param[ iBord ][ iNode ] = bordLength;
8101 // normalize within [0,1]
8102 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8103 param[ iBord ][ iNode ] /= bordLength;
8107 // loop on border segments
8108 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8109 int i[ 2 ] = { 0, 0 };
8110 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8111 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8113 TElemOfNodeListMap insertMap;
8114 TElemOfNodeListMap::iterator insertMapIt;
8116 // key: elem to insert nodes into
8117 // value: 2 nodes to insert between + nodes to be inserted
8119 bool next[ 2 ] = { false, false };
8121 // find min adjacent segment length after sewing
8122 double nextParam = 10., prevParam = 0;
8123 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8124 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8125 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8126 if ( i[ iBord ] > 0 )
8127 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8129 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8130 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8131 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8133 // choose to insert or to merge nodes
8134 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8135 if ( Abs( du ) <= minSegLen * 0.2 ) {
8138 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8139 const SMDS_MeshNode* n0 = *nIt[0];
8140 const SMDS_MeshNode* n1 = *nIt[1];
8141 nodeGroupsToMerge.back().push_back( n1 );
8142 nodeGroupsToMerge.back().push_back( n0 );
8143 // position of node of the border changes due to merge
8144 param[ 0 ][ i[0] ] += du;
8145 // move n1 for the sake of elem shape evaluation during insertion.
8146 // n1 will be removed by MergeNodes() anyway
8147 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8148 next[0] = next[1] = true;
8153 int intoBord = ( du < 0 ) ? 0 : 1;
8154 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8155 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8156 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8157 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8158 if ( intoBord == 1 ) {
8159 // move node of the border to be on a link of elem of the side
8160 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8161 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8162 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8163 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8164 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8166 insertMapIt = insertMap.find( elem );
8167 bool notFound = ( insertMapIt == insertMap.end() );
8168 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8170 // insert into another link of the same element:
8171 // 1. perform insertion into the other link of the elem
8172 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8173 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8174 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8175 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8176 // 2. perform insertion into the link of adjacent faces
8178 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8180 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8184 if (toCreatePolyedrs) {
8185 // perform insertion into the links of adjacent volumes
8186 UpdateVolumes(n12, n22, nodeList);
8188 // 3. find an element appeared on n1 and n2 after the insertion
8189 insertMap.erase( elem );
8190 elem = findAdjacentFace( n1, n2, 0 );
8192 if ( notFound || otherLink ) {
8193 // add element and nodes of the side into the insertMap
8194 insertMapIt = insertMap.insert
8195 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8196 (*insertMapIt).second.push_back( n1 );
8197 (*insertMapIt).second.push_back( n2 );
8199 // add node to be inserted into elem
8200 (*insertMapIt).second.push_back( nIns );
8201 next[ 1 - intoBord ] = true;
8204 // go to the next segment
8205 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8206 if ( next[ iBord ] ) {
8207 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8209 nPrev[ iBord ] = *nIt[ iBord ];
8210 nIt[ iBord ]++; i[ iBord ]++;
8214 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8216 // perform insertion of nodes into elements
8218 for (insertMapIt = insertMap.begin();
8219 insertMapIt != insertMap.end();
8222 const SMDS_MeshElement* elem = (*insertMapIt).first;
8223 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8224 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8225 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8227 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8229 if ( !theSideIsFreeBorder ) {
8230 // look for and insert nodes into the faces adjacent to elem
8232 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8234 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8239 if (toCreatePolyedrs) {
8240 // perform insertion into the links of adjacent volumes
8241 UpdateVolumes(n1, n2, nodeList);
8247 } // end: insert new nodes
8249 MergeNodes ( nodeGroupsToMerge );
8254 //=======================================================================
8255 //function : InsertNodesIntoLink
8256 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8257 // and theBetweenNode2 and split theElement
8258 //=======================================================================
8260 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8261 const SMDS_MeshNode* theBetweenNode1,
8262 const SMDS_MeshNode* theBetweenNode2,
8263 list<const SMDS_MeshNode*>& theNodesToInsert,
8264 const bool toCreatePoly)
8266 if ( theFace->GetType() != SMDSAbs_Face ) return;
8268 // find indices of 2 link nodes and of the rest nodes
8269 int iNode = 0, il1, il2, i3, i4;
8270 il1 = il2 = i3 = i4 = -1;
8271 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8272 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8274 if(theFace->IsQuadratic()) {
8275 const SMDS_QuadraticFaceOfNodes* F =
8276 static_cast<const SMDS_QuadraticFaceOfNodes*>(theFace);
8277 // use special nodes iterator
8278 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
8279 while( anIter->more() ) {
8280 const SMDS_MeshNode* n = anIter->next();
8281 if ( n == theBetweenNode1 )
8283 else if ( n == theBetweenNode2 )
8289 nodes[ iNode++ ] = n;
8293 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8294 while ( nodeIt->more() ) {
8295 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8296 if ( n == theBetweenNode1 )
8298 else if ( n == theBetweenNode2 )
8304 nodes[ iNode++ ] = n;
8307 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8310 // arrange link nodes to go one after another regarding the face orientation
8311 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8312 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8317 aNodesToInsert.reverse();
8319 // check that not link nodes of a quadrangles are in good order
8320 int nbFaceNodes = theFace->NbNodes();
8321 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8327 if (toCreatePoly || theFace->IsPoly()) {
8330 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8332 // add nodes of face up to first node of link
8335 if(theFace->IsQuadratic()) {
8336 const SMDS_QuadraticFaceOfNodes* F =
8337 static_cast<const SMDS_QuadraticFaceOfNodes*>(theFace);
8338 // use special nodes iterator
8339 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
8340 while( anIter->more() && !isFLN ) {
8341 const SMDS_MeshNode* n = anIter->next();
8342 poly_nodes[iNode++] = n;
8343 if (n == nodes[il1]) {
8347 // add nodes to insert
8348 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8349 for (; nIt != aNodesToInsert.end(); nIt++) {
8350 poly_nodes[iNode++] = *nIt;
8352 // add nodes of face starting from last node of link
8353 while ( anIter->more() ) {
8354 poly_nodes[iNode++] = anIter->next();
8358 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8359 while ( nodeIt->more() && !isFLN ) {
8360 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8361 poly_nodes[iNode++] = n;
8362 if (n == nodes[il1]) {
8366 // add nodes to insert
8367 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8368 for (; nIt != aNodesToInsert.end(); nIt++) {
8369 poly_nodes[iNode++] = *nIt;
8371 // add nodes of face starting from last node of link
8372 while ( nodeIt->more() ) {
8373 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8374 poly_nodes[iNode++] = n;
8378 // edit or replace the face
8379 SMESHDS_Mesh *aMesh = GetMeshDS();
8381 if (theFace->IsPoly()) {
8382 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8385 int aShapeId = FindShape( theFace );
8387 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8388 myLastCreatedElems.Append(newElem);
8389 if ( aShapeId && newElem )
8390 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8392 aMesh->RemoveElement(theFace);
8397 if( !theFace->IsQuadratic() ) {
8399 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8400 int nbLinkNodes = 2 + aNodesToInsert.size();
8401 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8402 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8403 linkNodes[ 0 ] = nodes[ il1 ];
8404 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8405 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8406 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8407 linkNodes[ iNode++ ] = *nIt;
8409 // decide how to split a quadrangle: compare possible variants
8410 // and choose which of splits to be a quadrangle
8411 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8412 if ( nbFaceNodes == 3 ) {
8413 iBestQuad = nbSplits;
8416 else if ( nbFaceNodes == 4 ) {
8417 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8418 double aBestRate = DBL_MAX;
8419 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8421 double aBadRate = 0;
8422 // evaluate elements quality
8423 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8424 if ( iSplit == iQuad ) {
8425 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8429 aBadRate += getBadRate( &quad, aCrit );
8432 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8434 nodes[ iSplit < iQuad ? i4 : i3 ]);
8435 aBadRate += getBadRate( &tria, aCrit );
8439 if ( aBadRate < aBestRate ) {
8441 aBestRate = aBadRate;
8446 // create new elements
8447 SMESHDS_Mesh *aMesh = GetMeshDS();
8448 int aShapeId = FindShape( theFace );
8451 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8452 SMDS_MeshElement* newElem = 0;
8453 if ( iSplit == iBestQuad )
8454 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8459 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8461 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8462 myLastCreatedElems.Append(newElem);
8463 if ( aShapeId && newElem )
8464 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8467 // change nodes of theFace
8468 const SMDS_MeshNode* newNodes[ 4 ];
8469 newNodes[ 0 ] = linkNodes[ i1 ];
8470 newNodes[ 1 ] = linkNodes[ i2 ];
8471 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8472 newNodes[ 3 ] = nodes[ i4 ];
8473 aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8474 } // end if(!theFace->IsQuadratic())
8475 else { // theFace is quadratic
8476 // we have to split theFace on simple triangles and one simple quadrangle
8478 int nbshift = tmp*2;
8479 // shift nodes in nodes[] by nbshift
8481 for(i=0; i<nbshift; i++) {
8482 const SMDS_MeshNode* n = nodes[0];
8483 for(j=0; j<nbFaceNodes-1; j++) {
8484 nodes[j] = nodes[j+1];
8486 nodes[nbFaceNodes-1] = n;
8488 il1 = il1 - nbshift;
8489 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8490 // n0 n1 n2 n0 n1 n2
8491 // +-----+-----+ +-----+-----+
8500 // create new elements
8501 SMESHDS_Mesh *aMesh = GetMeshDS();
8502 int aShapeId = FindShape( theFace );
8505 if(nbFaceNodes==6) { // quadratic triangle
8506 SMDS_MeshElement* newElem =
8507 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8508 myLastCreatedElems.Append(newElem);
8509 if ( aShapeId && newElem )
8510 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8511 if(theFace->IsMediumNode(nodes[il1])) {
8512 // create quadrangle
8513 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8514 myLastCreatedElems.Append(newElem);
8515 if ( aShapeId && newElem )
8516 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8522 // create quadrangle
8523 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8524 myLastCreatedElems.Append(newElem);
8525 if ( aShapeId && newElem )
8526 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8532 else { // nbFaceNodes==8 - quadratic quadrangle
8533 SMDS_MeshElement* newElem =
8534 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8535 myLastCreatedElems.Append(newElem);
8536 if ( aShapeId && newElem )
8537 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8538 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8539 myLastCreatedElems.Append(newElem);
8540 if ( aShapeId && newElem )
8541 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8542 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8543 myLastCreatedElems.Append(newElem);
8544 if ( aShapeId && newElem )
8545 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8546 if(theFace->IsMediumNode(nodes[il1])) {
8547 // create quadrangle
8548 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8549 myLastCreatedElems.Append(newElem);
8550 if ( aShapeId && newElem )
8551 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8557 // create quadrangle
8558 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8559 myLastCreatedElems.Append(newElem);
8560 if ( aShapeId && newElem )
8561 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8567 // create needed triangles using n1,n2,n3 and inserted nodes
8568 int nbn = 2 + aNodesToInsert.size();
8569 //const SMDS_MeshNode* aNodes[nbn];
8570 vector<const SMDS_MeshNode*> aNodes(nbn);
8571 aNodes[0] = nodes[n1];
8572 aNodes[nbn-1] = nodes[n2];
8573 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8574 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8575 aNodes[iNode++] = *nIt;
8577 for(i=1; i<nbn; i++) {
8578 SMDS_MeshElement* newElem =
8579 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8580 myLastCreatedElems.Append(newElem);
8581 if ( aShapeId && newElem )
8582 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8584 // remove old quadratic face
8585 aMesh->RemoveElement(theFace);
8589 //=======================================================================
8590 //function : UpdateVolumes
8592 //=======================================================================
8593 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8594 const SMDS_MeshNode* theBetweenNode2,
8595 list<const SMDS_MeshNode*>& theNodesToInsert)
8597 myLastCreatedElems.Clear();
8598 myLastCreatedNodes.Clear();
8600 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8601 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8602 const SMDS_MeshElement* elem = invElemIt->next();
8604 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8605 SMDS_VolumeTool aVolume (elem);
8606 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8609 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8610 int iface, nbFaces = aVolume.NbFaces();
8611 vector<const SMDS_MeshNode *> poly_nodes;
8612 vector<int> quantities (nbFaces);
8614 for (iface = 0; iface < nbFaces; iface++) {
8615 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8616 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8617 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8619 for (int inode = 0; inode < nbFaceNodes; inode++) {
8620 poly_nodes.push_back(faceNodes[inode]);
8622 if (nbInserted == 0) {
8623 if (faceNodes[inode] == theBetweenNode1) {
8624 if (faceNodes[inode + 1] == theBetweenNode2) {
8625 nbInserted = theNodesToInsert.size();
8627 // add nodes to insert
8628 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8629 for (; nIt != theNodesToInsert.end(); nIt++) {
8630 poly_nodes.push_back(*nIt);
8634 else if (faceNodes[inode] == theBetweenNode2) {
8635 if (faceNodes[inode + 1] == theBetweenNode1) {
8636 nbInserted = theNodesToInsert.size();
8638 // add nodes to insert in reversed order
8639 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8641 for (; nIt != theNodesToInsert.begin(); nIt--) {
8642 poly_nodes.push_back(*nIt);
8644 poly_nodes.push_back(*nIt);
8651 quantities[iface] = nbFaceNodes + nbInserted;
8654 // Replace or update the volume
8655 SMESHDS_Mesh *aMesh = GetMeshDS();
8657 if (elem->IsPoly()) {
8658 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8662 int aShapeId = FindShape( elem );
8664 SMDS_MeshElement* newElem =
8665 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8666 myLastCreatedElems.Append(newElem);
8667 if (aShapeId && newElem)
8668 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8670 aMesh->RemoveElement(elem);
8675 //=======================================================================
8677 * \brief Convert elements contained in a submesh to quadratic
8678 * \retval int - nb of checked elements
8680 //=======================================================================
8682 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8683 SMESH_MesherHelper& theHelper,
8684 const bool theForce3d)
8687 if( !theSm ) return nbElem;
8689 vector<int> nbNodeInFaces;
8690 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8691 while(ElemItr->more())
8694 const SMDS_MeshElement* elem = ElemItr->next();
8695 if( !elem || elem->IsQuadratic() ) continue;
8697 int id = elem->GetID();
8698 int nbNodes = elem->NbNodes();
8699 SMDSAbs_ElementType aType = elem->GetType();
8701 vector<const SMDS_MeshNode *> nodes (elem->begin_nodes(), elem->end_nodes());
8702 if ( elem->GetEntityType() == SMDSEntity_Polyhedra )
8703 nbNodeInFaces = static_cast<const SMDS_PolyhedralVolumeOfNodes* >( elem )->GetQuanities();
8705 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8707 const SMDS_MeshElement* NewElem = 0;
8713 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8721 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8724 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8727 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8732 case SMDSAbs_Volume :
8737 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8740 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8743 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8746 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8747 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8750 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8757 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8759 theSm->AddElement( NewElem );
8764 //=======================================================================
8765 //function : ConvertToQuadratic
8767 //=======================================================================
8768 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
8770 SMESHDS_Mesh* meshDS = GetMeshDS();
8772 SMESH_MesherHelper aHelper(*myMesh);
8773 aHelper.SetIsQuadratic( true );
8775 int nbCheckedElems = 0;
8776 if ( myMesh->HasShapeToMesh() )
8778 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8780 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8781 while ( smIt->more() ) {
8782 SMESH_subMesh* sm = smIt->next();
8783 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8784 aHelper.SetSubShape( sm->GetSubShape() );
8785 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8790 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8791 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8793 SMESHDS_SubMesh *smDS = 0;
8794 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8795 while(aEdgeItr->more())
8797 const SMDS_MeshEdge* edge = aEdgeItr->next();
8798 if(edge && !edge->IsQuadratic())
8800 int id = edge->GetID();
8801 const SMDS_MeshNode* n1 = edge->GetNode(0);
8802 const SMDS_MeshNode* n2 = edge->GetNode(1);
8804 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8806 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8807 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8810 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8811 while(aFaceItr->more())
8813 const SMDS_MeshFace* face = aFaceItr->next();
8814 if(!face || face->IsQuadratic() ) continue;
8816 int id = face->GetID();
8817 int nbNodes = face->NbNodes();
8818 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8820 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8822 SMDS_MeshFace * NewFace = 0;
8826 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8829 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8832 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8834 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8836 vector<int> nbNodeInFaces;
8837 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8838 while(aVolumeItr->more())
8840 const SMDS_MeshVolume* volume = aVolumeItr->next();
8841 if(!volume || volume->IsQuadratic() ) continue;
8843 int id = volume->GetID();
8844 int nbNodes = volume->NbNodes();
8845 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8846 if ( volume->GetEntityType() == SMDSEntity_Polyhedra )
8847 nbNodeInFaces = static_cast<const SMDS_PolyhedralVolumeOfNodes* >(volume)->GetQuanities();
8849 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8851 SMDS_MeshVolume * NewVolume = 0;
8855 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8856 nodes[3], id, theForce3d );
8859 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8860 nodes[3], nodes[4], id, theForce3d);
8863 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8864 nodes[3], nodes[4], nodes[5], id, theForce3d);
8867 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8868 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8871 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8873 ReplaceElemInGroups(volume, NewVolume, meshDS);
8877 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
8878 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8879 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8880 aHelper.FixQuadraticElements();
8884 //=======================================================================
8886 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
8887 * \retval int - nb of checked elements
8889 //=======================================================================
8891 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
8892 SMDS_ElemIteratorPtr theItr,
8893 const int theShapeID)
8896 SMESHDS_Mesh* meshDS = GetMeshDS();
8897 const bool notFromGroups = false;
8899 while( theItr->more() )
8901 const SMDS_MeshElement* elem = theItr->next();
8903 if( elem && elem->IsQuadratic())
8905 int id = elem->GetID();
8906 int nbNodes = elem->NbNodes();
8907 vector<const SMDS_MeshNode *> nodes, mediumNodes;
8908 nodes.reserve( nbNodes );
8909 mediumNodes.reserve( nbNodes );
8911 for(int i = 0; i < nbNodes; i++)
8913 const SMDS_MeshNode* n = elem->GetNode(i);
8915 if( elem->IsMediumNode( n ) )
8916 mediumNodes.push_back( n );
8918 nodes.push_back( n );
8920 if( nodes.empty() ) continue;
8921 SMDSAbs_ElementType aType = elem->GetType();
8923 //remove old quadratic element
8924 meshDS->RemoveFreeElement( elem, theSm, notFromGroups );
8926 SMDS_MeshElement * NewElem = AddElement( nodes, aType, false, id );
8927 ReplaceElemInGroups(elem, NewElem, meshDS);
8928 if( theSm && NewElem )
8929 theSm->AddElement( NewElem );
8931 // remove medium nodes
8932 vector<const SMDS_MeshNode*>::iterator nIt = mediumNodes.begin();
8933 for ( ; nIt != mediumNodes.end(); ++nIt ) {
8934 const SMDS_MeshNode* n = *nIt;
8935 if ( n->NbInverseElements() == 0 ) {
8936 if ( n->GetPosition()->GetShapeId() != theShapeID )
8937 meshDS->RemoveFreeNode( n, meshDS->MeshElements
8938 ( n->GetPosition()->GetShapeId() ));
8940 meshDS->RemoveFreeNode( n, theSm );
8948 //=======================================================================
8949 //function : ConvertFromQuadratic
8951 //=======================================================================
8952 bool SMESH_MeshEditor::ConvertFromQuadratic()
8954 int nbCheckedElems = 0;
8955 if ( myMesh->HasShapeToMesh() )
8957 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8959 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8960 while ( smIt->more() ) {
8961 SMESH_subMesh* sm = smIt->next();
8962 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
8963 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
8969 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
8970 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8972 SMESHDS_SubMesh *aSM = 0;
8973 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
8979 //=======================================================================
8980 //function : SewSideElements
8982 //=======================================================================
8984 SMESH_MeshEditor::Sew_Error
8985 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
8986 TIDSortedElemSet& theSide2,
8987 const SMDS_MeshNode* theFirstNode1,
8988 const SMDS_MeshNode* theFirstNode2,
8989 const SMDS_MeshNode* theSecondNode1,
8990 const SMDS_MeshNode* theSecondNode2)
8992 myLastCreatedElems.Clear();
8993 myLastCreatedNodes.Clear();
8995 MESSAGE ("::::SewSideElements()");
8996 if ( theSide1.size() != theSide2.size() )
8997 return SEW_DIFF_NB_OF_ELEMENTS;
8999 Sew_Error aResult = SEW_OK;
9001 // 1. Build set of faces representing each side
9002 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9003 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9005 // =======================================================================
9006 // 1. Build set of faces representing each side:
9007 // =======================================================================
9008 // a. build set of nodes belonging to faces
9009 // b. complete set of faces: find missing fices whose nodes are in set of nodes
9010 // c. create temporary faces representing side of volumes if correspondent
9011 // face does not exist
9013 SMESHDS_Mesh* aMesh = GetMeshDS();
9014 SMDS_Mesh aTmpFacesMesh;
9015 set<const SMDS_MeshElement*> faceSet1, faceSet2;
9016 set<const SMDS_MeshElement*> volSet1, volSet2;
9017 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9018 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9019 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9020 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9021 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9022 int iSide, iFace, iNode;
9024 for ( iSide = 0; iSide < 2; iSide++ ) {
9025 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9026 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9027 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9028 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9029 set<const SMDS_MeshElement*>::iterator vIt;
9030 TIDSortedElemSet::iterator eIt;
9031 set<const SMDS_MeshNode*>::iterator nIt;
9033 // check that given nodes belong to given elements
9034 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9035 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9036 int firstIndex = -1, secondIndex = -1;
9037 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9038 const SMDS_MeshElement* elem = *eIt;
9039 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9040 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9041 if ( firstIndex > -1 && secondIndex > -1 ) break;
9043 if ( firstIndex < 0 || secondIndex < 0 ) {
9044 // we can simply return until temporary faces created
9045 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9048 // -----------------------------------------------------------
9049 // 1a. Collect nodes of existing faces
9050 // and build set of face nodes in order to detect missing
9051 // faces corresponing to sides of volumes
9052 // -----------------------------------------------------------
9054 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9056 // loop on the given element of a side
9057 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9058 //const SMDS_MeshElement* elem = *eIt;
9059 const SMDS_MeshElement* elem = *eIt;
9060 if ( elem->GetType() == SMDSAbs_Face ) {
9061 faceSet->insert( elem );
9062 set <const SMDS_MeshNode*> faceNodeSet;
9063 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9064 while ( nodeIt->more() ) {
9065 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9066 nodeSet->insert( n );
9067 faceNodeSet.insert( n );
9069 setOfFaceNodeSet.insert( faceNodeSet );
9071 else if ( elem->GetType() == SMDSAbs_Volume )
9072 volSet->insert( elem );
9074 // ------------------------------------------------------------------------------
9075 // 1b. Complete set of faces: find missing fices whose nodes are in set of nodes
9076 // ------------------------------------------------------------------------------
9078 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9079 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9080 while ( fIt->more() ) { // loop on faces sharing a node
9081 const SMDS_MeshElement* f = fIt->next();
9082 if ( faceSet->find( f ) == faceSet->end() ) {
9083 // check if all nodes are in nodeSet and
9084 // complete setOfFaceNodeSet if they are
9085 set <const SMDS_MeshNode*> faceNodeSet;
9086 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9087 bool allInSet = true;
9088 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9089 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9090 if ( nodeSet->find( n ) == nodeSet->end() )
9093 faceNodeSet.insert( n );
9096 faceSet->insert( f );
9097 setOfFaceNodeSet.insert( faceNodeSet );
9103 // -------------------------------------------------------------------------
9104 // 1c. Create temporary faces representing sides of volumes if correspondent
9105 // face does not exist
9106 // -------------------------------------------------------------------------
9108 if ( !volSet->empty() ) {
9109 //int nodeSetSize = nodeSet->size();
9111 // loop on given volumes
9112 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9113 SMDS_VolumeTool vol (*vIt);
9114 // loop on volume faces: find free faces
9115 // --------------------------------------
9116 list<const SMDS_MeshElement* > freeFaceList;
9117 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9118 if ( !vol.IsFreeFace( iFace ))
9120 // check if there is already a face with same nodes in a face set
9121 const SMDS_MeshElement* aFreeFace = 0;
9122 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9123 int nbNodes = vol.NbFaceNodes( iFace );
9124 set <const SMDS_MeshNode*> faceNodeSet;
9125 vol.GetFaceNodes( iFace, faceNodeSet );
9126 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9128 // no such a face is given but it still can exist, check it
9129 if ( nbNodes == 3 ) {
9130 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9132 else if ( nbNodes == 4 ) {
9133 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9136 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9137 aFreeFace = aMesh->FindFace(poly_nodes);
9141 // create a temporary face
9142 if ( nbNodes == 3 ) {
9143 aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9145 else if ( nbNodes == 4 ) {
9146 aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9149 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9150 aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9154 freeFaceList.push_back( aFreeFace );
9156 } // loop on faces of a volume
9158 // choose one of several free faces
9159 // --------------------------------------
9160 if ( freeFaceList.size() > 1 ) {
9161 // choose a face having max nb of nodes shared by other elems of a side
9162 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9163 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9164 while ( fIt != freeFaceList.end() ) { // loop on free faces
9165 int nbSharedNodes = 0;
9166 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9167 while ( nodeIt->more() ) { // loop on free face nodes
9168 const SMDS_MeshNode* n =
9169 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9170 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9171 while ( invElemIt->more() ) {
9172 const SMDS_MeshElement* e = invElemIt->next();
9173 if ( faceSet->find( e ) != faceSet->end() )
9175 if ( elemSet->find( e ) != elemSet->end() )
9179 if ( nbSharedNodes >= maxNbNodes ) {
9180 maxNbNodes = nbSharedNodes;
9184 freeFaceList.erase( fIt++ ); // here fIt++ occures before erase
9186 if ( freeFaceList.size() > 1 )
9188 // could not choose one face, use another way
9189 // choose a face most close to the bary center of the opposite side
9190 gp_XYZ aBC( 0., 0., 0. );
9191 set <const SMDS_MeshNode*> addedNodes;
9192 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9193 eIt = elemSet2->begin();
9194 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9195 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9196 while ( nodeIt->more() ) { // loop on free face nodes
9197 const SMDS_MeshNode* n =
9198 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9199 if ( addedNodes.insert( n ).second )
9200 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9203 aBC /= addedNodes.size();
9204 double minDist = DBL_MAX;
9205 fIt = freeFaceList.begin();
9206 while ( fIt != freeFaceList.end() ) { // loop on free faces
9208 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9209 while ( nodeIt->more() ) { // loop on free face nodes
9210 const SMDS_MeshNode* n =
9211 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9212 gp_XYZ p( n->X(),n->Y(),n->Z() );
9213 dist += ( aBC - p ).SquareModulus();
9215 if ( dist < minDist ) {
9217 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9220 fIt = freeFaceList.erase( fIt++ );
9223 } // choose one of several free faces of a volume
9225 if ( freeFaceList.size() == 1 ) {
9226 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9227 faceSet->insert( aFreeFace );
9228 // complete a node set with nodes of a found free face
9229 // for ( iNode = 0; iNode < ; iNode++ )
9230 // nodeSet->insert( fNodes[ iNode ] );
9233 } // loop on volumes of a side
9235 // // complete a set of faces if new nodes in a nodeSet appeared
9236 // // ----------------------------------------------------------
9237 // if ( nodeSetSize != nodeSet->size() ) {
9238 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9239 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9240 // while ( fIt->more() ) { // loop on faces sharing a node
9241 // const SMDS_MeshElement* f = fIt->next();
9242 // if ( faceSet->find( f ) == faceSet->end() ) {
9243 // // check if all nodes are in nodeSet and
9244 // // complete setOfFaceNodeSet if they are
9245 // set <const SMDS_MeshNode*> faceNodeSet;
9246 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9247 // bool allInSet = true;
9248 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9249 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9250 // if ( nodeSet->find( n ) == nodeSet->end() )
9251 // allInSet = false;
9253 // faceNodeSet.insert( n );
9255 // if ( allInSet ) {
9256 // faceSet->insert( f );
9257 // setOfFaceNodeSet.insert( faceNodeSet );
9263 } // Create temporary faces, if there are volumes given
9266 if ( faceSet1.size() != faceSet2.size() ) {
9267 // delete temporary faces: they are in reverseElements of actual nodes
9268 SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9269 while ( tmpFaceIt->more() )
9270 aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9271 MESSAGE("Diff nb of faces");
9272 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9275 // ============================================================
9276 // 2. Find nodes to merge:
9277 // bind a node to remove to a node to put instead
9278 // ============================================================
9280 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9281 if ( theFirstNode1 != theFirstNode2 )
9282 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
9283 if ( theSecondNode1 != theSecondNode2 )
9284 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
9286 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9287 set< long > linkIdSet; // links to process
9288 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9290 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9291 list< NLink > linkList[2];
9292 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9293 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9294 // loop on links in linkList; find faces by links and append links
9295 // of the found faces to linkList
9296 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9297 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9298 NLink link[] = { *linkIt[0], *linkIt[1] };
9299 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9300 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
9303 // by links, find faces in the face sets,
9304 // and find indices of link nodes in the found faces;
9305 // in a face set, there is only one or no face sharing a link
9306 // ---------------------------------------------------------------
9308 const SMDS_MeshElement* face[] = { 0, 0 };
9309 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
9310 vector<const SMDS_MeshNode*> fnodes1(9);
9311 vector<const SMDS_MeshNode*> fnodes2(9);
9312 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
9313 vector<const SMDS_MeshNode*> notLinkNodes1(6);
9314 vector<const SMDS_MeshNode*> notLinkNodes2(6);
9315 int iLinkNode[2][2];
9316 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9317 const SMDS_MeshNode* n1 = link[iSide].first;
9318 const SMDS_MeshNode* n2 = link[iSide].second;
9319 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9320 set< const SMDS_MeshElement* > fMap;
9321 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
9322 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
9323 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9324 while ( fIt->more() ) { // loop on faces sharing a node
9325 const SMDS_MeshElement* f = fIt->next();
9326 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9327 ! fMap.insert( f ).second ) // f encounters twice
9329 if ( face[ iSide ] ) {
9330 MESSAGE( "2 faces per link " );
9331 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
9335 faceSet->erase( f );
9336 // get face nodes and find ones of a link
9341 fnodes1.resize(f->NbNodes()+1);
9342 notLinkNodes1.resize(f->NbNodes()-2);
9345 fnodes2.resize(f->NbNodes()+1);
9346 notLinkNodes2.resize(f->NbNodes()-2);
9349 if(!f->IsQuadratic()) {
9350 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
9351 while ( nIt->more() ) {
9352 const SMDS_MeshNode* n =
9353 static_cast<const SMDS_MeshNode*>( nIt->next() );
9355 iLinkNode[ iSide ][ 0 ] = iNode;
9357 else if ( n == n2 ) {
9358 iLinkNode[ iSide ][ 1 ] = iNode;
9360 //else if ( notLinkNodes[ iSide ][ 0 ] )
9361 // notLinkNodes[ iSide ][ 1 ] = n;
9363 // notLinkNodes[ iSide ][ 0 ] = n;
9367 notLinkNodes1[nbl] = n;
9368 //notLinkNodes1.push_back(n);
9370 notLinkNodes2[nbl] = n;
9371 //notLinkNodes2.push_back(n);
9373 //faceNodes[ iSide ][ iNode++ ] = n;
9375 fnodes1[iNode++] = n;
9378 fnodes2[iNode++] = n;
9382 else { // f->IsQuadratic()
9383 const SMDS_QuadraticFaceOfNodes* F =
9384 static_cast<const SMDS_QuadraticFaceOfNodes*>(f);
9385 // use special nodes iterator
9386 SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator();
9387 while ( anIter->more() ) {
9388 const SMDS_MeshNode* n =
9389 static_cast<const SMDS_MeshNode*>( anIter->next() );
9391 iLinkNode[ iSide ][ 0 ] = iNode;
9393 else if ( n == n2 ) {
9394 iLinkNode[ iSide ][ 1 ] = iNode;
9399 notLinkNodes1[nbl] = n;
9402 notLinkNodes2[nbl] = n;
9406 fnodes1[iNode++] = n;
9409 fnodes2[iNode++] = n;
9413 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
9415 fnodes1[iNode] = fnodes1[0];
9418 fnodes2[iNode] = fnodes1[0];
9425 // check similarity of elements of the sides
9426 if (aResult == SEW_OK && ( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9427 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9428 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9429 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9432 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9434 break; // do not return because it s necessary to remove tmp faces
9437 // set nodes to merge
9438 // -------------------
9440 if ( face[0] && face[1] ) {
9441 int nbNodes = face[0]->NbNodes();
9442 if ( nbNodes != face[1]->NbNodes() ) {
9443 MESSAGE("Diff nb of face nodes");
9444 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9445 break; // do not return because it s necessary to remove tmp faces
9447 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
9448 if ( nbNodes == 3 ) {
9449 //nReplaceMap.insert( TNodeNodeMap::value_type
9450 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
9451 nReplaceMap.insert( TNodeNodeMap::value_type
9452 ( notLinkNodes1[0], notLinkNodes2[0] ));
9455 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9456 // analyse link orientation in faces
9457 int i1 = iLinkNode[ iSide ][ 0 ];
9458 int i2 = iLinkNode[ iSide ][ 1 ];
9459 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9460 // if notLinkNodes are the first and the last ones, then
9461 // their order does not correspond to the link orientation
9462 if (( i1 == 1 && i2 == 2 ) ||
9463 ( i1 == 2 && i2 == 1 ))
9464 reverse[ iSide ] = !reverse[ iSide ];
9466 if ( reverse[0] == reverse[1] ) {
9467 //nReplaceMap.insert( TNodeNodeMap::value_type
9468 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
9469 //nReplaceMap.insert( TNodeNodeMap::value_type
9470 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
9471 for(int nn=0; nn<nbNodes-2; nn++) {
9472 nReplaceMap.insert( TNodeNodeMap::value_type
9473 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
9477 //nReplaceMap.insert( TNodeNodeMap::value_type
9478 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
9479 //nReplaceMap.insert( TNodeNodeMap::value_type
9480 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
9481 for(int nn=0; nn<nbNodes-2; nn++) {
9482 nReplaceMap.insert( TNodeNodeMap::value_type
9483 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
9488 // add other links of the faces to linkList
9489 // -----------------------------------------
9491 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
9492 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9493 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
9494 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
9495 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9496 if ( !iter_isnew.second ) { // already in a set: no need to process
9497 linkIdSet.erase( iter_isnew.first );
9499 else // new in set == encountered for the first time: add
9501 //const SMDS_MeshNode* n1 = nodes[ iNode ];
9502 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
9503 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
9504 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
9505 linkList[0].push_back ( NLink( n1, n2 ));
9506 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9510 } // loop on link lists
9512 if ( aResult == SEW_OK &&
9513 ( linkIt[0] != linkList[0].end() ||
9514 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9515 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9516 " " << (faceSetPtr[1]->empty()));
9517 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9520 // ====================================================================
9521 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9522 // ====================================================================
9524 // delete temporary faces: they are in reverseElements of actual nodes
9525 SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9526 while ( tmpFaceIt->more() )
9527 aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9529 if ( aResult != SEW_OK)
9532 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9533 // loop on nodes replacement map
9534 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9535 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9536 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9537 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9538 nodeIDsToRemove.push_back( nToRemove->GetID() );
9539 // loop on elements sharing nToRemove
9540 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9541 while ( invElemIt->more() ) {
9542 const SMDS_MeshElement* e = invElemIt->next();
9543 // get a new suite of nodes: make replacement
9544 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9545 vector< const SMDS_MeshNode*> nodes( nbNodes );
9546 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9547 while ( nIt->more() ) {
9548 const SMDS_MeshNode* n =
9549 static_cast<const SMDS_MeshNode*>( nIt->next() );
9550 nnIt = nReplaceMap.find( n );
9551 if ( nnIt != nReplaceMap.end() ) {
9557 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9558 // elemIDsToRemove.push_back( e->GetID() );
9561 aMesh->ChangeElementNodes( e, & nodes[0], nbNodes );
9565 Remove( nodeIDsToRemove, true );
9570 //================================================================================
9572 * \brief Find corresponding nodes in two sets of faces
9573 * \param theSide1 - first face set
9574 * \param theSide2 - second first face
9575 * \param theFirstNode1 - a boundary node of set 1
9576 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9577 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9578 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9579 * \param nReplaceMap - output map of corresponding nodes
9580 * \retval bool - is a success or not
9582 //================================================================================
9585 //#define DEBUG_MATCHING_NODES
9588 SMESH_MeshEditor::Sew_Error
9589 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9590 set<const SMDS_MeshElement*>& theSide2,
9591 const SMDS_MeshNode* theFirstNode1,
9592 const SMDS_MeshNode* theFirstNode2,
9593 const SMDS_MeshNode* theSecondNode1,
9594 const SMDS_MeshNode* theSecondNode2,
9595 TNodeNodeMap & nReplaceMap)
9597 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9599 nReplaceMap.clear();
9600 if ( theFirstNode1 != theFirstNode2 )
9601 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9602 if ( theSecondNode1 != theSecondNode2 )
9603 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9605 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9606 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9608 list< NLink > linkList[2];
9609 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9610 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9612 // loop on links in linkList; find faces by links and append links
9613 // of the found faces to linkList
9614 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9615 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9616 NLink link[] = { *linkIt[0], *linkIt[1] };
9617 if ( linkSet.find( link[0] ) == linkSet.end() )
9620 // by links, find faces in the face sets,
9621 // and find indices of link nodes in the found faces;
9622 // in a face set, there is only one or no face sharing a link
9623 // ---------------------------------------------------------------
9625 const SMDS_MeshElement* face[] = { 0, 0 };
9626 list<const SMDS_MeshNode*> notLinkNodes[2];
9627 //bool reverse[] = { false, false }; // order of notLinkNodes
9629 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9631 const SMDS_MeshNode* n1 = link[iSide].first;
9632 const SMDS_MeshNode* n2 = link[iSide].second;
9633 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9634 set< const SMDS_MeshElement* > facesOfNode1;
9635 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9637 // during a loop of the first node, we find all faces around n1,
9638 // during a loop of the second node, we find one face sharing both n1 and n2
9639 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9640 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9641 while ( fIt->more() ) { // loop on faces sharing a node
9642 const SMDS_MeshElement* f = fIt->next();
9643 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9644 ! facesOfNode1.insert( f ).second ) // f encounters twice
9646 if ( face[ iSide ] ) {
9647 MESSAGE( "2 faces per link " );
9648 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9651 faceSet->erase( f );
9653 // get not link nodes
9654 int nbN = f->NbNodes();
9655 if ( f->IsQuadratic() )
9657 nbNodes[ iSide ] = nbN;
9658 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9659 int i1 = f->GetNodeIndex( n1 );
9660 int i2 = f->GetNodeIndex( n2 );
9661 int iEnd = nbN, iBeg = -1, iDelta = 1;
9662 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9664 std::swap( iEnd, iBeg ); iDelta = -1;
9669 if ( i == iEnd ) i = iBeg + iDelta;
9670 if ( i == i1 ) break;
9671 nodes.push_back ( f->GetNode( i ) );
9677 // check similarity of elements of the sides
9678 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9679 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9680 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9681 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9684 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9688 // set nodes to merge
9689 // -------------------
9691 if ( face[0] && face[1] ) {
9692 if ( nbNodes[0] != nbNodes[1] ) {
9693 MESSAGE("Diff nb of face nodes");
9694 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9696 #ifdef DEBUG_MATCHING_NODES
9697 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9698 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9699 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9701 int nbN = nbNodes[0];
9703 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9704 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9705 for ( int i = 0 ; i < nbN - 2; ++i ) {
9706 #ifdef DEBUG_MATCHING_NODES
9707 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9709 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9713 // add other links of the face 1 to linkList
9714 // -----------------------------------------
9716 const SMDS_MeshElement* f0 = face[0];
9717 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9718 for ( int i = 0; i < nbN; i++ )
9720 const SMDS_MeshNode* n2 = f0->GetNode( i );
9721 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9722 linkSet.insert( SMESH_TLink( n1, n2 ));
9723 if ( !iter_isnew.second ) { // already in a set: no need to process
9724 linkSet.erase( iter_isnew.first );
9726 else // new in set == encountered for the first time: add
9728 #ifdef DEBUG_MATCHING_NODES
9729 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9730 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9732 linkList[0].push_back ( NLink( n1, n2 ));
9733 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9738 } // loop on link lists
9743 //================================================================================
9745 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9746 \param theElems - the list of elements (edges or faces) to be replicated
9747 The nodes for duplication could be found from these elements
9748 \param theNodesNot - list of nodes to NOT replicate
9749 \param theAffectedElems - the list of elements (cells and edges) to which the
9750 replicated nodes should be associated to.
9751 \return TRUE if operation has been completed successfully, FALSE otherwise
9753 //================================================================================
9755 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
9756 const TIDSortedElemSet& theNodesNot,
9757 const TIDSortedElemSet& theAffectedElems )
9759 myLastCreatedElems.Clear();
9760 myLastCreatedNodes.Clear();
9762 if ( theElems.size() == 0 )
9765 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9770 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9771 // duplicate elements and nodes
9772 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
9773 // replce nodes by duplications
9774 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
9778 //================================================================================
9780 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9781 \param theMeshDS - mesh instance
9782 \param theElems - the elements replicated or modified (nodes should be changed)
9783 \param theNodesNot - nodes to NOT replicate
9784 \param theNodeNodeMap - relation of old node to new created node
9785 \param theIsDoubleElem - flag os to replicate element or modify
9786 \return TRUE if operation has been completed successfully, FALSE otherwise
9788 //================================================================================
9790 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
9791 const TIDSortedElemSet& theElems,
9792 const TIDSortedElemSet& theNodesNot,
9793 std::map< const SMDS_MeshNode*,
9794 const SMDS_MeshNode* >& theNodeNodeMap,
9795 const bool theIsDoubleElem )
9797 // iterate on through element and duplicate them (by nodes duplication)
9799 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9800 for ( ; elemItr != theElems.end(); ++elemItr )
9802 const SMDS_MeshElement* anElem = *elemItr;
9806 bool isDuplicate = false;
9807 // duplicate nodes to duplicate element
9808 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
9809 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9811 while ( anIter->more() )
9814 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9815 SMDS_MeshNode* aNewNode = aCurrNode;
9816 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
9817 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
9818 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
9821 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
9822 theNodeNodeMap[ aCurrNode ] = aNewNode;
9823 myLastCreatedNodes.Append( aNewNode );
9825 isDuplicate |= (aCurrNode != aNewNode);
9826 newNodes[ ind++ ] = aNewNode;
9831 if ( theIsDoubleElem )
9832 myLastCreatedElems.Append( AddElement(newNodes, anElem->GetType(), anElem->IsPoly()) );
9834 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
9841 //================================================================================
9843 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9844 \param theNodes - identifiers of nodes to be doubled
9845 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
9846 nodes. If list of element identifiers is empty then nodes are doubled but
9847 they not assigned to elements
9848 \return TRUE if operation has been completed successfully, FALSE otherwise
9850 //================================================================================
9852 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
9853 const std::list< int >& theListOfModifiedElems )
9855 myLastCreatedElems.Clear();
9856 myLastCreatedNodes.Clear();
9858 if ( theListOfNodes.size() == 0 )
9861 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9865 // iterate through nodes and duplicate them
9867 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9869 std::list< int >::const_iterator aNodeIter;
9870 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
9872 int aCurr = *aNodeIter;
9873 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
9879 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
9882 anOldNodeToNewNode[ aNode ] = aNewNode;
9883 myLastCreatedNodes.Append( aNewNode );
9887 // Create map of new nodes for modified elements
9889 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
9891 std::list< int >::const_iterator anElemIter;
9892 for ( anElemIter = theListOfModifiedElems.begin();
9893 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
9895 int aCurr = *anElemIter;
9896 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
9900 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
9902 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9904 while ( anIter->more() )
9906 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9907 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
9909 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
9910 aNodeArr[ ind++ ] = aNewNode;
9913 aNodeArr[ ind++ ] = aCurrNode;
9915 anElemToNodes[ anElem ] = aNodeArr;
9918 // Change nodes of elements
9920 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
9921 anElemToNodesIter = anElemToNodes.begin();
9922 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
9924 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
9925 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
9927 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
9935 //================================================================================
9937 \brief Check if element located inside shape
9938 \return TRUE if IN or ON shape, FALSE otherwise
9940 //================================================================================
9942 template<class Classifier>
9943 bool isInside(const SMDS_MeshElement* theElem,
9944 Classifier& theClassifier,
9945 const double theTol)
9947 gp_XYZ centerXYZ (0, 0, 0);
9948 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
9949 while (aNodeItr->more())
9950 centerXYZ += SMESH_MeshEditor::TNodeXYZ(cast2Node( aNodeItr->next()));
9952 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
9953 theClassifier.Perform(aPnt, theTol);
9954 TopAbs_State aState = theClassifier.State();
9955 return (aState == TopAbs_IN || aState == TopAbs_ON );
9958 //================================================================================
9960 * \brief Classifier of the 3D point on the TopoDS_Face
9961 * with interaface suitable for isInside()
9963 //================================================================================
9965 struct _FaceClassifier
9967 Extrema_ExtPS _extremum;
9968 BRepAdaptor_Surface _surface;
9969 TopAbs_State _state;
9971 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
9973 _extremum.Initialize( _surface,
9974 _surface.FirstUParameter(), _surface.LastUParameter(),
9975 _surface.FirstVParameter(), _surface.LastVParameter(),
9976 _surface.Tolerance(), _surface.Tolerance() );
9978 void Perform(const gp_Pnt& aPnt, double theTol)
9980 _state = TopAbs_OUT;
9981 _extremum.Perform(aPnt);
9982 if ( _extremum.IsDone() )
9983 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
9984 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9986 TopAbs_State State() const
9993 //================================================================================
9995 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9996 \param theElems - group of of elements (edges or faces) to be replicated
9997 \param theNodesNot - group of nodes not to replicate
9998 \param theShape - shape to detect affected elements (element which geometric center
9999 located on or inside shape).
10000 The replicated nodes should be associated to affected elements.
10001 \return TRUE if operation has been completed successfully, FALSE otherwise
10003 //================================================================================
10005 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10006 const TIDSortedElemSet& theNodesNot,
10007 const TopoDS_Shape& theShape )
10009 if ( theShape.IsNull() )
10012 const double aTol = Precision::Confusion();
10013 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10014 auto_ptr<_FaceClassifier> aFaceClassifier;
10015 if ( theShape.ShapeType() == TopAbs_SOLID )
10017 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10018 bsc3d->PerformInfinitePoint(aTol);
10020 else if (theShape.ShapeType() == TopAbs_FACE )
10022 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10025 // iterates on indicated elements and get elements by back references from their nodes
10026 TIDSortedElemSet anAffected;
10027 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10028 for ( ; elemItr != theElems.end(); ++elemItr )
10030 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10034 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10035 while ( nodeItr->more() )
10037 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10038 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10040 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10041 while ( backElemItr->more() )
10043 const SMDS_MeshElement* curElem = backElemItr->next();
10044 if ( curElem && theElems.find(curElem) == theElems.end() &&
10046 isInside( curElem, *bsc3d, aTol ) :
10047 isInside( curElem, *aFaceClassifier, aTol )))
10048 anAffected.insert( curElem );
10052 return DoubleNodes( theElems, theNodesNot, anAffected );
10055 //================================================================================
10057 * \brief Generated skin mesh (containing 2D cells) from 3D mesh
10058 * The created 2D mesh elements based on nodes of free faces of boundary volumes
10059 * \return TRUE if operation has been completed successfully, FALSE otherwise
10061 //================================================================================
10063 bool SMESH_MeshEditor::Make2DMeshFrom3D()
10065 // iterates on volume elements and detect all free faces on them
10066 SMESHDS_Mesh* aMesh = GetMeshDS();
10069 //bool res = false;
10070 int nbFree = 0, nbExisted = 0, nbCreated = 0;
10071 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
10074 const SMDS_MeshVolume* volume = vIt->next();
10075 SMDS_VolumeTool vTool( volume );
10076 vTool.SetExternalNormal();
10077 const bool isPoly = volume->IsPoly();
10078 const bool isQuad = volume->IsQuadratic();
10079 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
10081 if (!vTool.IsFreeFace(iface))
10084 vector<const SMDS_MeshNode *> nodes;
10085 int nbFaceNodes = vTool.NbFaceNodes(iface);
10086 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
10088 for ( ; inode < nbFaceNodes; inode += isQuad ? 2 : 1)
10089 nodes.push_back(faceNodes[inode]);
10091 for ( inode = 1; inode < nbFaceNodes; inode += 2)
10092 nodes.push_back(faceNodes[inode]);
10094 // add new face based on volume nodes
10095 if (aMesh->FindFace( nodes ) ) {
10097 continue; // face already exsist
10099 myLastCreatedElems.Append( AddElement(nodes, SMDSAbs_Face, isPoly && iface == 1) );
10103 return ( nbFree==(nbExisted+nbCreated) );