1 // Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // SMESH SMESH : idl implementation based on 'SMESH' unit's classes
24 // File : SMESH_MeshEditor.cxx
25 // Created : Mon Apr 12 16:10:22 2004
26 // Author : Edward AGAPOV (eap)
29 #include "SMESH_MeshEditor.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_VolumeTool.hxx"
33 #include "SMDS_EdgePosition.hxx"
34 #include "SMDS_PolyhedralVolumeOfNodes.hxx"
35 #include "SMDS_FacePosition.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 //#include "SMDS_QuadraticFaceOfNodes.hxx"
38 #include "SMDS_MeshGroup.hxx"
39 #include "SMDS_LinearEdge.hxx"
40 #include "SMDS_Downward.hxx"
41 #include "SMDS_SetIterator.hxx"
43 #include "SMESHDS_Group.hxx"
44 #include "SMESHDS_Mesh.hxx"
46 #include "SMESH_Algo.hxx"
47 #include "SMESH_ControlsDef.hxx"
48 #include "SMESH_Group.hxx"
49 #include "SMESH_MesherHelper.hxx"
50 #include "SMESH_OctreeNode.hxx"
51 #include "SMESH_subMesh.hxx"
53 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <GC_MakeSegment.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAPI_ExtremaCurveCurve.hxx>
65 #include <GeomAdaptor_Surface.hxx>
66 #include <Geom_Curve.hxx>
67 #include <Geom_Line.hxx>
68 #include <Geom_Surface.hxx>
69 #include <IntAna_IntConicQuad.hxx>
70 #include <IntAna_Quadric.hxx>
71 #include <Precision.hxx>
72 #include <TColStd_ListOfInteger.hxx>
73 #include <TopAbs_State.hxx>
75 #include <TopExp_Explorer.hxx>
76 #include <TopTools_ListIteratorOfListOfShape.hxx>
77 #include <TopTools_ListOfShape.hxx>
78 #include <TopTools_SequenceOfShape.hxx>
80 #include <TopoDS_Face.hxx>
86 #include <gp_Trsf.hxx>
100 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103 using namespace SMESH::Controls;
105 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
106 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
108 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
110 //=======================================================================
111 //function : SMESH_MeshEditor
113 //=======================================================================
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116 :myMesh( theMesh ) // theMesh may be NULL
120 //=======================================================================
124 //=======================================================================
127 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
128 const SMDSAbs_ElementType type,
132 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
133 SMDS_MeshElement* e = 0;
134 int nbnode = node.size();
135 SMESHDS_Mesh* mesh = GetMeshDS();
140 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
141 else e = mesh->AddFace (node[0], node[1], node[2] );
143 else if (nbnode == 4) {
144 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
145 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
147 else if (nbnode == 6) {
148 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
149 node[4], node[5], ID);
150 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
153 else if (nbnode == 8) {
154 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
155 node[4], node[5], node[6], node[7], ID);
156 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
157 node[4], node[5], node[6], node[7] );
160 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
161 else e = mesh->AddPolygonalFace (node );
168 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
169 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
171 else if (nbnode == 5) {
172 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
174 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
177 else if (nbnode == 6) {
178 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
179 node[4], node[5], ID);
180 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
183 else if (nbnode == 8) {
184 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
185 node[4], node[5], node[6], node[7], ID);
186 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
187 node[4], node[5], node[6], node[7] );
189 else if (nbnode == 10) {
190 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
191 node[4], node[5], node[6], node[7],
192 node[8], node[9], ID);
193 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
194 node[4], node[5], node[6], node[7],
197 else if (nbnode == 13) {
198 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
199 node[4], node[5], node[6], node[7],
200 node[8], node[9], node[10],node[11],
202 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
203 node[4], node[5], node[6], node[7],
204 node[8], node[9], node[10],node[11],
207 else if (nbnode == 15) {
208 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], node[7],
210 node[8], node[9], node[10],node[11],
211 node[12],node[13],node[14],ID);
212 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6], node[7],
214 node[8], node[9], node[10],node[11],
215 node[12],node[13],node[14] );
217 else if (nbnode == 20) {
218 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7],
220 node[8], node[9], node[10],node[11],
221 node[12],node[13],node[14],node[15],
222 node[16],node[17],node[18],node[19],ID);
223 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7],
225 node[8], node[9], node[10],node[11],
226 node[12],node[13],node[14],node[15],
227 node[16],node[17],node[18],node[19] );
234 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
235 else e = mesh->AddEdge (node[0], node[1] );
237 else if ( nbnode == 3 ) {
238 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
239 else e = mesh->AddEdge (node[0], node[1], node[2] );
243 case SMDSAbs_0DElement:
245 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
246 else e = mesh->Add0DElement (node[0] );
251 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
252 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
257 if ( e ) myLastCreatedElems.Append( e );
261 //=======================================================================
265 //=======================================================================
267 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
268 const SMDSAbs_ElementType type,
272 vector<const SMDS_MeshNode*> nodes;
273 nodes.reserve( nodeIDs.size() );
274 vector<int>::const_iterator id = nodeIDs.begin();
275 while ( id != nodeIDs.end() ) {
276 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
277 nodes.push_back( node );
281 return AddElement( nodes, type, isPoly, ID );
284 //=======================================================================
286 //purpose : Remove a node or an element.
287 // Modify a compute state of sub-meshes which become empty
288 //=======================================================================
290 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
293 myLastCreatedElems.Clear();
294 myLastCreatedNodes.Clear();
296 SMESHDS_Mesh* aMesh = GetMeshDS();
297 set< SMESH_subMesh *> smmap;
300 list<int>::const_iterator it = theIDs.begin();
301 for ( ; it != theIDs.end(); it++ ) {
302 const SMDS_MeshElement * elem;
304 elem = aMesh->FindNode( *it );
306 elem = aMesh->FindElement( *it );
310 // Notify VERTEX sub-meshes about modification
312 const SMDS_MeshNode* node = cast2Node( elem );
313 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
314 if ( int aShapeID = node->getshapeId() )
315 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
318 // Find sub-meshes to notify about modification
319 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
320 // while ( nodeIt->more() ) {
321 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
322 // const SMDS_PositionPtr& aPosition = node->GetPosition();
323 // if ( aPosition.get() ) {
324 // if ( int aShapeID = aPosition->GetShapeId() ) {
325 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
326 // smmap.insert( sm );
333 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
335 aMesh->RemoveElement( elem );
339 // Notify sub-meshes about modification
340 if ( !smmap.empty() ) {
341 set< SMESH_subMesh *>::iterator smIt;
342 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
343 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
346 // // Check if the whole mesh becomes empty
347 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
348 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
353 //=======================================================================
354 //function : FindShape
355 //purpose : Return an index of the shape theElem is on
356 // or zero if a shape not found
357 //=======================================================================
359 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
361 myLastCreatedElems.Clear();
362 myLastCreatedNodes.Clear();
364 SMESHDS_Mesh * aMesh = GetMeshDS();
365 if ( aMesh->ShapeToMesh().IsNull() )
368 int aShapeID = theElem->getshapeId();
372 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
373 if ( sm->Contains( theElem ))
376 if ( theElem->GetType() == SMDSAbs_Node ) {
377 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
380 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
383 TopoDS_Shape aShape; // the shape a node of theElem is on
384 if ( theElem->GetType() != SMDSAbs_Node )
386 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
387 while ( nodeIt->more() ) {
388 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
389 if ((aShapeID = node->getshapeId()) > 0) {
390 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
391 if ( sm->Contains( theElem ))
393 if ( aShape.IsNull() )
394 aShape = aMesh->IndexToShape( aShapeID );
400 // None of nodes is on a proper shape,
401 // find the shape among ancestors of aShape on which a node is
402 if ( !aShape.IsNull() ) {
403 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
404 for ( ; ancIt.More(); ancIt.Next() ) {
405 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
406 if ( sm && sm->Contains( theElem ))
407 return aMesh->ShapeToIndex( ancIt.Value() );
412 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
413 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
414 for ( ; id_sm != id2sm.end(); ++id_sm )
415 if ( id_sm->second->Contains( theElem ))
419 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
423 //=======================================================================
424 //function : IsMedium
426 //=======================================================================
428 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
429 const SMDSAbs_ElementType typeToCheck)
431 bool isMedium = false;
432 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
433 while (it->more() && !isMedium ) {
434 const SMDS_MeshElement* elem = it->next();
435 isMedium = elem->IsMediumNode(node);
440 //=======================================================================
441 //function : ShiftNodesQuadTria
443 // Shift nodes in the array corresponded to quadratic triangle
444 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
445 //=======================================================================
446 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
448 const SMDS_MeshNode* nd1 = aNodes[0];
449 aNodes[0] = aNodes[1];
450 aNodes[1] = aNodes[2];
452 const SMDS_MeshNode* nd2 = aNodes[3];
453 aNodes[3] = aNodes[4];
454 aNodes[4] = aNodes[5];
458 //=======================================================================
459 //function : GetNodesFromTwoTria
461 // Shift nodes in the array corresponded to quadratic triangle
462 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
463 //=======================================================================
464 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
465 const SMDS_MeshElement * theTria2,
466 const SMDS_MeshNode* N1[],
467 const SMDS_MeshNode* N2[])
469 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
472 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
475 if(it->more()) return false;
476 it = theTria2->nodesIterator();
479 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
482 if(it->more()) return false;
484 int sames[3] = {-1,-1,-1};
496 if(nbsames!=2) return false;
498 ShiftNodesQuadTria(N1);
500 ShiftNodesQuadTria(N1);
503 i = sames[0] + sames[1] + sames[2];
505 ShiftNodesQuadTria(N2);
507 // now we receive following N1 and N2 (using numeration as above image)
508 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
509 // i.e. first nodes from both arrays determ new diagonal
513 //=======================================================================
514 //function : InverseDiag
515 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
516 // but having other common link.
517 // Return False if args are improper
518 //=======================================================================
520 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
521 const SMDS_MeshElement * theTria2 )
523 MESSAGE("InverseDiag");
524 myLastCreatedElems.Clear();
525 myLastCreatedNodes.Clear();
527 if (!theTria1 || !theTria2)
530 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
531 if (!F1) return false;
532 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
533 if (!F2) return false;
534 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
535 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
537 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
538 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
542 // put nodes in array and find out indices of the same ones
543 const SMDS_MeshNode* aNodes [6];
544 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
546 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
547 while ( it->more() ) {
548 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
550 if ( i > 2 ) // theTria2
551 // find same node of theTria1
552 for ( int j = 0; j < 3; j++ )
553 if ( aNodes[ i ] == aNodes[ j ]) {
562 return false; // theTria1 is not a triangle
563 it = theTria2->nodesIterator();
565 if ( i == 6 && it->more() )
566 return false; // theTria2 is not a triangle
569 // find indices of 1,2 and of A,B in theTria1
570 int iA = 0, iB = 0, i1 = 0, i2 = 0;
571 for ( i = 0; i < 6; i++ ) {
572 if ( sameInd [ i ] == 0 ) {
581 // nodes 1 and 2 should not be the same
582 if ( aNodes[ i1 ] == aNodes[ i2 ] )
586 aNodes[ iA ] = aNodes[ i2 ];
588 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
590 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
591 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
595 } // end if(F1 && F2)
597 // check case of quadratic faces
598 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
600 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
604 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
605 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
613 const SMDS_MeshNode* N1 [6];
614 const SMDS_MeshNode* N2 [6];
615 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
617 // now we receive following N1 and N2 (using numeration as above image)
618 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
619 // i.e. first nodes from both arrays determ new diagonal
621 const SMDS_MeshNode* N1new [6];
622 const SMDS_MeshNode* N2new [6];
635 // replaces nodes in faces
636 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
637 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
642 //=======================================================================
643 //function : findTriangles
644 //purpose : find triangles sharing theNode1-theNode2 link
645 //=======================================================================
647 static bool findTriangles(const SMDS_MeshNode * theNode1,
648 const SMDS_MeshNode * theNode2,
649 const SMDS_MeshElement*& theTria1,
650 const SMDS_MeshElement*& theTria2)
652 if ( !theNode1 || !theNode2 ) return false;
654 theTria1 = theTria2 = 0;
656 set< const SMDS_MeshElement* > emap;
657 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
659 const SMDS_MeshElement* elem = it->next();
660 if ( elem->NbNodes() == 3 )
663 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
665 const SMDS_MeshElement* elem = it->next();
666 if ( emap.find( elem ) != emap.end() ) {
668 // theTria1 must be element with minimum ID
669 if( theTria1->GetID() < elem->GetID() ) {
683 return ( theTria1 && theTria2 );
686 //=======================================================================
687 //function : InverseDiag
688 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
689 // with ones built on the same 4 nodes but having other common link.
690 // Return false if proper faces not found
691 //=======================================================================
693 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
694 const SMDS_MeshNode * theNode2)
696 myLastCreatedElems.Clear();
697 myLastCreatedNodes.Clear();
699 MESSAGE( "::InverseDiag()" );
701 const SMDS_MeshElement *tr1, *tr2;
702 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
705 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
706 if (!F1) return false;
707 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
708 if (!F2) return false;
709 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
710 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
712 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
713 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
717 // put nodes in array
718 // and find indices of 1,2 and of A in tr1 and of B in tr2
719 int i, iA1 = 0, i1 = 0;
720 const SMDS_MeshNode* aNodes1 [3];
721 SMDS_ElemIteratorPtr it;
722 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
723 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
724 if ( aNodes1[ i ] == theNode1 )
725 iA1 = i; // node A in tr1
726 else if ( aNodes1[ i ] != theNode2 )
730 const SMDS_MeshNode* aNodes2 [3];
731 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
732 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
733 if ( aNodes2[ i ] == theNode2 )
734 iB2 = i; // node B in tr2
735 else if ( aNodes2[ i ] != theNode1 )
739 // nodes 1 and 2 should not be the same
740 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
744 aNodes1[ iA1 ] = aNodes2[ i2 ];
746 aNodes2[ iB2 ] = aNodes1[ i1 ];
748 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
749 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
754 // check case of quadratic faces
755 return InverseDiag(tr1,tr2);
758 //=======================================================================
759 //function : getQuadrangleNodes
760 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
761 // fusion of triangles tr1 and tr2 having shared link on
762 // theNode1 and theNode2
763 //=======================================================================
765 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
766 const SMDS_MeshNode * theNode1,
767 const SMDS_MeshNode * theNode2,
768 const SMDS_MeshElement * tr1,
769 const SMDS_MeshElement * tr2 )
771 if( tr1->NbNodes() != tr2->NbNodes() )
773 // find the 4-th node to insert into tr1
774 const SMDS_MeshNode* n4 = 0;
775 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
777 while ( !n4 && i<3 ) {
778 const SMDS_MeshNode * n = cast2Node( it->next() );
780 bool isDiag = ( n == theNode1 || n == theNode2 );
784 // Make an array of nodes to be in a quadrangle
785 int iNode = 0, iFirstDiag = -1;
786 it = tr1->nodesIterator();
789 const SMDS_MeshNode * n = cast2Node( it->next() );
791 bool isDiag = ( n == theNode1 || n == theNode2 );
793 if ( iFirstDiag < 0 )
795 else if ( iNode - iFirstDiag == 1 )
796 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
798 else if ( n == n4 ) {
799 return false; // tr1 and tr2 should not have all the same nodes
801 theQuadNodes[ iNode++ ] = n;
803 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
804 theQuadNodes[ iNode ] = n4;
809 //=======================================================================
810 //function : DeleteDiag
811 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
812 // with a quadrangle built on the same 4 nodes.
813 // Return false if proper faces not found
814 //=======================================================================
816 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
817 const SMDS_MeshNode * theNode2)
819 myLastCreatedElems.Clear();
820 myLastCreatedNodes.Clear();
822 MESSAGE( "::DeleteDiag()" );
824 const SMDS_MeshElement *tr1, *tr2;
825 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
828 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
829 if (!F1) return false;
830 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
831 if (!F2) return false;
832 SMESHDS_Mesh * aMesh = GetMeshDS();
834 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
835 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
837 const SMDS_MeshNode* aNodes [ 4 ];
838 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
841 const SMDS_MeshElement* newElem = 0;
842 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
843 myLastCreatedElems.Append(newElem);
844 AddToSameGroups( newElem, tr1, aMesh );
845 int aShapeId = tr1->getshapeId();
848 aMesh->SetMeshElementOnShape( newElem, aShapeId );
850 aMesh->RemoveElement( tr1 );
851 aMesh->RemoveElement( tr2 );
856 // check case of quadratic faces
857 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
859 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
863 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
864 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
872 const SMDS_MeshNode* N1 [6];
873 const SMDS_MeshNode* N2 [6];
874 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
876 // now we receive following N1 and N2 (using numeration as above image)
877 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
878 // i.e. first nodes from both arrays determ new diagonal
880 const SMDS_MeshNode* aNodes[8];
890 const SMDS_MeshElement* newElem = 0;
891 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
892 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
893 myLastCreatedElems.Append(newElem);
894 AddToSameGroups( newElem, tr1, aMesh );
895 int aShapeId = tr1->getshapeId();
898 aMesh->SetMeshElementOnShape( newElem, aShapeId );
900 aMesh->RemoveElement( tr1 );
901 aMesh->RemoveElement( tr2 );
903 // remove middle node (9)
904 GetMeshDS()->RemoveNode( N1[4] );
909 //=======================================================================
910 //function : Reorient
911 //purpose : Reverse theElement orientation
912 //=======================================================================
914 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
917 myLastCreatedElems.Clear();
918 myLastCreatedNodes.Clear();
922 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
923 if ( !it || !it->more() )
926 switch ( theElem->GetType() ) {
930 if(!theElem->IsQuadratic()) {
931 int i = theElem->NbNodes();
932 vector<const SMDS_MeshNode*> aNodes( i );
934 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
935 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
938 // quadratic elements
939 if(theElem->GetType()==SMDSAbs_Edge) {
940 vector<const SMDS_MeshNode*> aNodes(3);
941 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
942 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
943 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
944 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
947 int nbn = theElem->NbNodes();
948 vector<const SMDS_MeshNode*> aNodes(nbn);
949 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
951 for(; i<nbn/2; i++) {
952 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
954 for(i=0; i<nbn/2; i++) {
955 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
957 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
961 case SMDSAbs_Volume: {
962 if (theElem->IsPoly()) {
963 // TODO reorient vtk polyhedron
964 MESSAGE("reorient vtk polyhedron ?");
965 const SMDS_VtkVolume* aPolyedre =
966 dynamic_cast<const SMDS_VtkVolume*>( theElem );
968 MESSAGE("Warning: bad volumic element");
972 int nbFaces = aPolyedre->NbFaces();
973 vector<const SMDS_MeshNode *> poly_nodes;
974 vector<int> quantities (nbFaces);
976 // reverse each face of the polyedre
977 for (int iface = 1; iface <= nbFaces; iface++) {
978 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
979 quantities[iface - 1] = nbFaceNodes;
981 for (inode = nbFaceNodes; inode >= 1; inode--) {
982 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
983 poly_nodes.push_back(curNode);
987 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
991 SMDS_VolumeTool vTool;
992 if ( !vTool.Set( theElem ))
995 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
996 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1005 //=======================================================================
1006 //function : getBadRate
1008 //=======================================================================
1010 static double getBadRate (const SMDS_MeshElement* theElem,
1011 SMESH::Controls::NumericalFunctorPtr& theCrit)
1013 SMESH::Controls::TSequenceOfXYZ P;
1014 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1016 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1017 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1020 //=======================================================================
1021 //function : QuadToTri
1022 //purpose : Cut quadrangles into triangles.
1023 // theCrit is used to select a diagonal to cut
1024 //=======================================================================
1026 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1027 SMESH::Controls::NumericalFunctorPtr theCrit)
1029 myLastCreatedElems.Clear();
1030 myLastCreatedNodes.Clear();
1032 MESSAGE( "::QuadToTri()" );
1034 if ( !theCrit.get() )
1037 SMESHDS_Mesh * aMesh = GetMeshDS();
1039 Handle(Geom_Surface) surface;
1040 SMESH_MesherHelper helper( *GetMesh() );
1042 TIDSortedElemSet::iterator itElem;
1043 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1044 const SMDS_MeshElement* elem = *itElem;
1045 if ( !elem || elem->GetType() != SMDSAbs_Face )
1047 if ( elem->NbNodes() != ( elem->IsQuadratic() ? 8 : 4 ))
1050 // retrieve element nodes
1051 const SMDS_MeshNode* aNodes [8];
1052 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1054 while ( itN->more() )
1055 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1057 // compare two sets of possible triangles
1058 double aBadRate1, aBadRate2; // to what extent a set is bad
1059 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1060 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1061 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1063 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1064 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1065 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1067 int aShapeId = FindShape( elem );
1068 const SMDS_MeshElement* newElem1 = 0;
1069 const SMDS_MeshElement* newElem2 = 0;
1071 if( !elem->IsQuadratic() ) {
1073 // split liner quadrangle
1074 if ( aBadRate1 <= aBadRate2 ) {
1075 // tr1 + tr2 is better
1076 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1077 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1080 // tr3 + tr4 is better
1081 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1082 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1087 // split quadratic quadrangle
1089 // get surface elem is on
1090 if ( aShapeId != helper.GetSubShapeID() ) {
1094 shape = aMesh->IndexToShape( aShapeId );
1095 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1096 TopoDS_Face face = TopoDS::Face( shape );
1097 surface = BRep_Tool::Surface( face );
1098 if ( !surface.IsNull() )
1099 helper.SetSubShape( shape );
1103 const SMDS_MeshNode* aNodes [8];
1104 const SMDS_MeshNode* inFaceNode = 0;
1105 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1107 while ( itN->more() ) {
1108 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1109 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
1110 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1112 inFaceNode = aNodes[ i-1 ];
1115 // find middle point for (0,1,2,3)
1116 // and create a node in this point;
1118 if ( surface.IsNull() ) {
1120 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1124 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1127 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1129 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1131 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1132 myLastCreatedNodes.Append(newN);
1134 // create a new element
1135 if ( aBadRate1 <= aBadRate2 ) {
1136 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1137 aNodes[6], aNodes[7], newN );
1138 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1139 newN, aNodes[4], aNodes[5] );
1142 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1143 aNodes[7], aNodes[4], newN );
1144 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1145 newN, aNodes[5], aNodes[6] );
1149 // care of a new element
1151 myLastCreatedElems.Append(newElem1);
1152 myLastCreatedElems.Append(newElem2);
1153 AddToSameGroups( newElem1, elem, aMesh );
1154 AddToSameGroups( newElem2, elem, aMesh );
1156 // put a new triangle on the same shape
1159 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1160 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1162 aMesh->RemoveElement( elem );
1167 //=======================================================================
1168 //function : BestSplit
1169 //purpose : Find better diagonal for cutting.
1170 //=======================================================================
1172 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1173 SMESH::Controls::NumericalFunctorPtr theCrit)
1175 myLastCreatedElems.Clear();
1176 myLastCreatedNodes.Clear();
1181 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1184 if( theQuad->NbNodes()==4 ||
1185 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1187 // retrieve element nodes
1188 const SMDS_MeshNode* aNodes [4];
1189 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1191 //while (itN->more())
1193 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1195 // compare two sets of possible triangles
1196 double aBadRate1, aBadRate2; // to what extent a set is bad
1197 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1198 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1199 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1201 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1202 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1203 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1205 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1206 return 1; // diagonal 1-3
1208 return 2; // diagonal 2-4
1215 // Methods of splitting volumes into tetra
1217 const int theHexTo5_1[5*4+1] =
1219 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1221 const int theHexTo5_2[5*4+1] =
1223 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1225 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1227 const int theHexTo6_1[6*4+1] =
1229 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1231 const int theHexTo6_2[6*4+1] =
1233 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1235 const int theHexTo6_3[6*4+1] =
1237 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1239 const int theHexTo6_4[6*4+1] =
1241 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1243 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1245 const int thePyraTo2_1[2*4+1] =
1247 0, 1, 2, 4, 0, 2, 3, 4, -1
1249 const int thePyraTo2_2[2*4+1] =
1251 1, 2, 3, 4, 1, 3, 0, 4, -1
1253 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1255 const int thePentaTo3_1[3*4+1] =
1257 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1259 const int thePentaTo3_2[3*4+1] =
1261 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1263 const int thePentaTo3_3[3*4+1] =
1265 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1267 const int thePentaTo3_4[3*4+1] =
1269 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1271 const int thePentaTo3_5[3*4+1] =
1273 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1275 const int thePentaTo3_6[3*4+1] =
1277 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1279 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1280 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1282 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1285 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1286 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1287 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1292 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1293 bool _baryNode; //!< additional node is to be created at cell barycenter
1294 bool _ownConn; //!< to delete _connectivity in destructor
1295 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1297 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1298 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1299 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1300 bool hasFacet( const TTriangleFacet& facet ) const
1302 const int* tetConn = _connectivity;
1303 for ( ; tetConn[0] >= 0; tetConn += 4 )
1304 if (( facet.contains( tetConn[0] ) +
1305 facet.contains( tetConn[1] ) +
1306 facet.contains( tetConn[2] ) +
1307 facet.contains( tetConn[3] )) == 3 )
1313 //=======================================================================
1315 * \brief return TSplitMethod for the given element
1317 //=======================================================================
1319 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1321 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1323 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1324 // an edge and a face barycenter; tertaherdons are based on triangles and
1325 // a volume barycenter
1326 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1328 // Find out how adjacent volumes are split
1330 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1331 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1332 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1334 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1335 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1336 if ( nbNodes < 4 ) continue;
1338 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1339 const int* nInd = vol.GetFaceNodesIndices( iF );
1342 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1343 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1344 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1345 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1349 int iCom = 0; // common node of triangle faces to split into
1350 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1352 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1353 nInd[ iQ * ( (iCom+1)%nbNodes )],
1354 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1355 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1356 nInd[ iQ * ( (iCom+2)%nbNodes )],
1357 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1358 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1360 triaSplits.push_back( t012 );
1361 triaSplits.push_back( t023 );
1366 if ( !triaSplits.empty() )
1367 hasAdjacentSplits = true;
1370 // Among variants of split method select one compliant with adjacent volumes
1372 TSplitMethod method;
1373 if ( !vol.Element()->IsPoly() && !is24TetMode )
1375 int nbVariants = 2, nbTet = 0;
1376 const int** connVariants = 0;
1377 switch ( vol.Element()->GetEntityType() )
1379 case SMDSEntity_Hexa:
1380 case SMDSEntity_Quad_Hexa:
1381 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1382 connVariants = theHexTo5, nbTet = 5;
1384 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1386 case SMDSEntity_Pyramid:
1387 case SMDSEntity_Quad_Pyramid:
1388 connVariants = thePyraTo2; nbTet = 2;
1390 case SMDSEntity_Penta:
1391 case SMDSEntity_Quad_Penta:
1392 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1397 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1399 // check method compliancy with adjacent tetras,
1400 // all found splits must be among facets of tetras described by this method
1401 method = TSplitMethod( nbTet, connVariants[variant] );
1402 if ( hasAdjacentSplits && method._nbTetra > 0 )
1404 bool facetCreated = true;
1405 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1407 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1408 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1409 facetCreated = method.hasFacet( *facet );
1411 if ( !facetCreated )
1412 method = TSplitMethod(0); // incompatible method
1416 if ( method._nbTetra < 1 )
1418 // No standard method is applicable, use a generic solution:
1419 // each facet of a volume is split into triangles and
1420 // each of triangles and a volume barycenter form a tetrahedron.
1422 int* connectivity = new int[ maxTetConnSize + 1 ];
1423 method._connectivity = connectivity;
1424 method._ownConn = true;
1425 method._baryNode = true;
1428 int baryCenInd = vol.NbNodes();
1429 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1431 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1432 const int* nInd = vol.GetFaceNodesIndices( iF );
1433 // find common node of triangle facets of tetra to create
1434 int iCommon = 0; // index in linear numeration
1435 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1436 if ( !triaSplits.empty() )
1439 const TTriangleFacet* facet = &triaSplits.front();
1440 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1441 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1442 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1445 else if ( nbNodes > 3 && !is24TetMode )
1447 // find the best method of splitting into triangles by aspect ratio
1448 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1449 map< double, int > badness2iCommon;
1450 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1451 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1452 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1453 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1455 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1456 nodes[ iQ*((iLast-1)%nbNodes)],
1457 nodes[ iQ*((iLast )%nbNodes)]);
1458 double badness = getBadRate( &tria, aspectRatio );
1459 badness2iCommon.insert( make_pair( badness, iCommon ));
1461 // use iCommon with lowest badness
1462 iCommon = badness2iCommon.begin()->second;
1464 if ( iCommon >= nbNodes )
1465 iCommon = 0; // something wrong
1467 // fill connectivity of tetrahedra based on a current face
1468 int nbTet = nbNodes - 2;
1469 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1471 method._faceBaryNode.insert( make_pair( iF, (const SMDS_MeshNode*)0 ));
1472 int faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1474 for ( int i = 0; i < nbTet; ++i )
1476 int i1 = i, i2 = (i+1) % nbNodes;
1477 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1478 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1479 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1480 connectivity[ connSize++ ] = faceBaryCenInd;
1481 connectivity[ connSize++ ] = baryCenInd;
1486 for ( int i = 0; i < nbTet; ++i )
1488 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1489 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1490 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1491 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1492 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1493 connectivity[ connSize++ ] = baryCenInd;
1496 method._nbTetra += nbTet;
1498 connectivity[ connSize++ ] = -1;
1502 //================================================================================
1504 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1506 //================================================================================
1508 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1510 // find the tetrahedron including the three nodes of facet
1511 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1512 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1513 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1514 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1515 while ( volIt1->more() )
1517 const SMDS_MeshElement* v = volIt1->next();
1518 if ( v->GetEntityType() != ( v->IsQuadratic() ? SMDSEntity_Quad_Tetra : SMDSEntity_Tetra ))
1520 SMDS_ElemIteratorPtr volIt2 = n2->GetInverseElementIterator(SMDSAbs_Volume);
1521 while ( volIt2->more() )
1522 if ( v != volIt2->next() )
1524 SMDS_ElemIteratorPtr volIt3 = n3->GetInverseElementIterator(SMDSAbs_Volume);
1525 while ( volIt3->more() )
1526 if ( v == volIt3->next() )
1532 //=======================================================================
1534 * \brief A key of a face of volume
1536 //=======================================================================
1538 struct TVolumeFaceKey: pair< int, pair< int, int> >
1540 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1542 TIDSortedNodeSet sortedNodes;
1543 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1544 int nbNodes = vol.NbFaceNodes( iF );
1545 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1546 for ( int i = 0; i < nbNodes; i += iQ )
1547 sortedNodes.insert( fNodes[i] );
1548 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1549 first = (*(n++))->GetID();
1550 second.first = (*(n++))->GetID();
1551 second.second = (*(n++))->GetID();
1556 //=======================================================================
1557 //function : SplitVolumesIntoTetra
1558 //purpose : Split volumic elements into tetrahedra.
1559 //=======================================================================
1561 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1562 const int theMethodFlags)
1564 // std-like iterator on coordinates of nodes of mesh element
1565 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1566 NXyzIterator xyzEnd;
1568 SMDS_VolumeTool volTool;
1569 SMESH_MesherHelper helper( *GetMesh());
1571 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1572 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1574 SMESH_SequenceOfElemPtr newNodes, newElems;
1576 // map face of volume to it's baricenrtic node
1577 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1580 TIDSortedElemSet::const_iterator elem = theElems.begin();
1581 for ( ; elem != theElems.end(); ++elem )
1583 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1584 if ( geomType <= SMDSEntity_Quad_Tetra )
1585 continue; // tetra or face or ...
1587 if ( !volTool.Set( *elem )) continue; // not volume? strange...
1589 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1590 if ( splitMethod._nbTetra < 1 ) continue;
1592 // find submesh to add new tetras to
1593 if ( !subMesh || !subMesh->Contains( *elem ))
1595 int shapeID = FindShape( *elem );
1596 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1597 subMesh = GetMeshDS()->MeshElements( shapeID );
1600 if ( (*elem)->IsQuadratic() )
1603 // add quadratic links to the helper
1604 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1606 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1607 for ( int iN = 0; iN < volTool.NbFaceNodes( iF ); iN += iQ )
1608 helper.AddTLinkNode( fNodes[iF], fNodes[iF+2], fNodes[iF+1] );
1610 helper.SetIsQuadratic( true );
1615 helper.SetIsQuadratic( false );
1617 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1618 if ( splitMethod._baryNode )
1620 // make a node at barycenter
1621 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1622 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1623 nodes.push_back( gcNode );
1624 newNodes.Append( gcNode );
1626 if ( !splitMethod._faceBaryNode.empty() )
1628 // make or find baricentric nodes of faces
1629 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1630 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1632 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1633 volFace2BaryNode.insert
1634 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), (const SMDS_MeshNode*)0) ).first;
1637 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1638 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1640 nodes.push_back( iF_n->second = f_n->second );
1645 helper.SetElementsOnShape( true );
1646 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1647 const int* tetConn = splitMethod._connectivity;
1648 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1649 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1650 nodes[ tetConn[1] ],
1651 nodes[ tetConn[2] ],
1652 nodes[ tetConn[3] ]));
1654 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1656 // Split faces on sides of the split volume
1658 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1659 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1661 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1662 if ( nbNodes < 4 ) continue;
1664 // find an existing face
1665 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1666 volTool.GetFaceNodes( iF ) + nbNodes*iQ );
1667 while ( const SMDS_MeshElement* face = GetMeshDS()->FindFace( fNodes ))
1670 helper.SetElementsOnShape( false );
1671 vector< const SMDS_MeshElement* > triangles;
1673 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1674 if ( iF_n != splitMethod._faceBaryNode.end() )
1676 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1678 const SMDS_MeshNode* n1 = fNodes[iN];
1679 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%nbNodes*iQ];
1680 const SMDS_MeshNode *n3 = iF_n->second;
1681 if ( !volTool.IsFaceExternal( iF ))
1683 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1688 // among possible triangles create ones discribed by split method
1689 const int* nInd = volTool.GetFaceNodesIndices( iF );
1690 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1691 int iCom = 0; // common node of triangle faces to split into
1692 list< TTriangleFacet > facets;
1693 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1695 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1696 nInd[ iQ * ( (iCom+1)%nbNodes )],
1697 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1698 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1699 nInd[ iQ * ( (iCom+2)%nbNodes )],
1700 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1701 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1703 facets.push_back( t012 );
1704 facets.push_back( t023 );
1705 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1706 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1707 nInd[ iQ * ((iLast-1)%nbNodes )],
1708 nInd[ iQ * ((iLast )%nbNodes )]));
1712 list< TTriangleFacet >::iterator facet = facets.begin();
1713 for ( ; facet != facets.end(); ++facet )
1715 if ( !volTool.IsFaceExternal( iF ))
1716 swap( facet->_n2, facet->_n3 );
1717 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1718 volNodes[ facet->_n2 ],
1719 volNodes[ facet->_n3 ]));
1722 // find submesh to add new triangles in
1723 if ( !fSubMesh || !fSubMesh->Contains( face ))
1725 int shapeID = FindShape( face );
1726 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1728 for ( int i = 0; i < triangles.size(); ++i )
1730 if ( !triangles[i] ) continue;
1732 fSubMesh->AddElement( triangles[i]);
1733 newElems.Append( triangles[i] );
1735 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1736 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1739 } // loop on volume faces to split them into triangles
1741 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1743 } // loop on volumes to split
1745 myLastCreatedNodes = newNodes;
1746 myLastCreatedElems = newElems;
1749 //=======================================================================
1750 //function : AddToSameGroups
1751 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1752 //=======================================================================
1754 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1755 const SMDS_MeshElement* elemInGroups,
1756 SMESHDS_Mesh * aMesh)
1758 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1759 if (!groups.empty()) {
1760 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1761 for ( ; grIt != groups.end(); grIt++ ) {
1762 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1763 if ( group && group->Contains( elemInGroups ))
1764 group->SMDSGroup().Add( elemToAdd );
1770 //=======================================================================
1771 //function : RemoveElemFromGroups
1772 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1773 //=======================================================================
1774 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1775 SMESHDS_Mesh * aMesh)
1777 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1778 if (!groups.empty())
1780 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1781 for (; GrIt != groups.end(); GrIt++)
1783 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1784 if (!grp || grp->IsEmpty()) continue;
1785 grp->SMDSGroup().Remove(removeelem);
1790 //================================================================================
1792 * \brief Replace elemToRm by elemToAdd in the all groups
1794 //================================================================================
1796 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1797 const SMDS_MeshElement* elemToAdd,
1798 SMESHDS_Mesh * aMesh)
1800 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1801 if (!groups.empty()) {
1802 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1803 for ( ; grIt != groups.end(); grIt++ ) {
1804 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1805 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1806 group->SMDSGroup().Add( elemToAdd );
1811 //================================================================================
1813 * \brief Replace elemToRm by elemToAdd in the all groups
1815 //================================================================================
1817 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1818 const vector<const SMDS_MeshElement*>& elemToAdd,
1819 SMESHDS_Mesh * aMesh)
1821 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1822 if (!groups.empty())
1824 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1825 for ( ; grIt != groups.end(); grIt++ ) {
1826 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1827 if ( group && group->SMDSGroup().Remove( elemToRm ) )
1828 for ( int i = 0; i < elemToAdd.size(); ++i )
1829 group->SMDSGroup().Add( elemToAdd[ i ] );
1834 //=======================================================================
1835 //function : QuadToTri
1836 //purpose : Cut quadrangles into triangles.
1837 // theCrit is used to select a diagonal to cut
1838 //=======================================================================
1840 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1841 const bool the13Diag)
1843 myLastCreatedElems.Clear();
1844 myLastCreatedNodes.Clear();
1846 MESSAGE( "::QuadToTri()" );
1848 SMESHDS_Mesh * aMesh = GetMeshDS();
1850 Handle(Geom_Surface) surface;
1851 SMESH_MesherHelper helper( *GetMesh() );
1853 TIDSortedElemSet::iterator itElem;
1854 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1855 const SMDS_MeshElement* elem = *itElem;
1856 if ( !elem || elem->GetType() != SMDSAbs_Face )
1858 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1859 if(!isquad) continue;
1861 if(elem->NbNodes()==4) {
1862 // retrieve element nodes
1863 const SMDS_MeshNode* aNodes [4];
1864 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1866 while ( itN->more() )
1867 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1869 int aShapeId = FindShape( elem );
1870 const SMDS_MeshElement* newElem1 = 0;
1871 const SMDS_MeshElement* newElem2 = 0;
1873 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1874 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1877 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1878 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1880 myLastCreatedElems.Append(newElem1);
1881 myLastCreatedElems.Append(newElem2);
1882 // put a new triangle on the same shape and add to the same groups
1885 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1886 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1888 AddToSameGroups( newElem1, elem, aMesh );
1889 AddToSameGroups( newElem2, elem, aMesh );
1890 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
1891 aMesh->RemoveElement( elem );
1894 // Quadratic quadrangle
1896 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1898 // get surface elem is on
1899 int aShapeId = FindShape( elem );
1900 if ( aShapeId != helper.GetSubShapeID() ) {
1904 shape = aMesh->IndexToShape( aShapeId );
1905 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1906 TopoDS_Face face = TopoDS::Face( shape );
1907 surface = BRep_Tool::Surface( face );
1908 if ( !surface.IsNull() )
1909 helper.SetSubShape( shape );
1913 const SMDS_MeshNode* aNodes [8];
1914 const SMDS_MeshNode* inFaceNode = 0;
1915 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1917 while ( itN->more() ) {
1918 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1919 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
1920 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1922 inFaceNode = aNodes[ i-1 ];
1926 // find middle point for (0,1,2,3)
1927 // and create a node in this point;
1929 if ( surface.IsNull() ) {
1931 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1935 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
1938 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
1940 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1942 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1943 myLastCreatedNodes.Append(newN);
1945 // create a new element
1946 const SMDS_MeshElement* newElem1 = 0;
1947 const SMDS_MeshElement* newElem2 = 0;
1949 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1950 aNodes[6], aNodes[7], newN );
1951 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1952 newN, aNodes[4], aNodes[5] );
1955 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1956 aNodes[7], aNodes[4], newN );
1957 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1958 newN, aNodes[5], aNodes[6] );
1960 myLastCreatedElems.Append(newElem1);
1961 myLastCreatedElems.Append(newElem2);
1962 // put a new triangle on the same shape and add to the same groups
1965 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1966 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1968 AddToSameGroups( newElem1, elem, aMesh );
1969 AddToSameGroups( newElem2, elem, aMesh );
1970 aMesh->RemoveElement( elem );
1977 //=======================================================================
1978 //function : getAngle
1980 //=======================================================================
1982 double getAngle(const SMDS_MeshElement * tr1,
1983 const SMDS_MeshElement * tr2,
1984 const SMDS_MeshNode * n1,
1985 const SMDS_MeshNode * n2)
1987 double angle = 2*PI; // bad angle
1990 SMESH::Controls::TSequenceOfXYZ P1, P2;
1991 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
1992 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
1995 if(!tr1->IsQuadratic())
1996 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
1998 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
1999 if ( N1.SquareMagnitude() <= gp::Resolution() )
2001 if(!tr2->IsQuadratic())
2002 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2004 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2005 if ( N2.SquareMagnitude() <= gp::Resolution() )
2008 // find the first diagonal node n1 in the triangles:
2009 // take in account a diagonal link orientation
2010 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2011 for ( int t = 0; t < 2; t++ ) {
2012 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2013 int i = 0, iDiag = -1;
2014 while ( it->more()) {
2015 const SMDS_MeshElement *n = it->next();
2016 if ( n == n1 || n == n2 ) {
2020 if ( i - iDiag == 1 )
2021 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2030 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2033 angle = N1.Angle( N2 );
2038 // =================================================
2039 // class generating a unique ID for a pair of nodes
2040 // and able to return nodes by that ID
2041 // =================================================
2045 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2046 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2049 long GetLinkID (const SMDS_MeshNode * n1,
2050 const SMDS_MeshNode * n2) const
2052 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2055 bool GetNodes (const long theLinkID,
2056 const SMDS_MeshNode* & theNode1,
2057 const SMDS_MeshNode* & theNode2) const
2059 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2060 if ( !theNode1 ) return false;
2061 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2062 if ( !theNode2 ) return false;
2068 const SMESHDS_Mesh* myMesh;
2073 //=======================================================================
2074 //function : TriToQuad
2075 //purpose : Fuse neighbour triangles into quadrangles.
2076 // theCrit is used to select a neighbour to fuse with.
2077 // theMaxAngle is a max angle between element normals at which
2078 // fusion is still performed.
2079 //=======================================================================
2081 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2082 SMESH::Controls::NumericalFunctorPtr theCrit,
2083 const double theMaxAngle)
2085 myLastCreatedElems.Clear();
2086 myLastCreatedNodes.Clear();
2088 MESSAGE( "::TriToQuad()" );
2090 if ( !theCrit.get() )
2093 SMESHDS_Mesh * aMesh = GetMeshDS();
2095 // Prepare data for algo: build
2096 // 1. map of elements with their linkIDs
2097 // 2. map of linkIDs with their elements
2099 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2100 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2101 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2102 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2104 TIDSortedElemSet::iterator itElem;
2105 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2106 const SMDS_MeshElement* elem = *itElem;
2107 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2108 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2109 if(!IsTria) continue;
2111 // retrieve element nodes
2112 const SMDS_MeshNode* aNodes [4];
2113 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2116 aNodes[ i++ ] = cast2Node( itN->next() );
2117 aNodes[ 3 ] = aNodes[ 0 ];
2120 for ( i = 0; i < 3; i++ ) {
2121 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2122 // check if elements sharing a link can be fused
2123 itLE = mapLi_listEl.find( link );
2124 if ( itLE != mapLi_listEl.end() ) {
2125 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2127 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2128 //if ( FindShape( elem ) != FindShape( elem2 ))
2129 // continue; // do not fuse triangles laying on different shapes
2130 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2131 continue; // avoid making badly shaped quads
2132 (*itLE).second.push_back( elem );
2135 mapLi_listEl[ link ].push_back( elem );
2137 mapEl_setLi [ elem ].insert( link );
2140 // Clean the maps from the links shared by a sole element, ie
2141 // links to which only one element is bound in mapLi_listEl
2143 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2144 int nbElems = (*itLE).second.size();
2145 if ( nbElems < 2 ) {
2146 const SMDS_MeshElement* elem = (*itLE).second.front();
2147 SMESH_TLink link = (*itLE).first;
2148 mapEl_setLi[ elem ].erase( link );
2149 if ( mapEl_setLi[ elem ].empty() )
2150 mapEl_setLi.erase( elem );
2154 // Algo: fuse triangles into quadrangles
2156 while ( ! mapEl_setLi.empty() ) {
2157 // Look for the start element:
2158 // the element having the least nb of shared links
2159 const SMDS_MeshElement* startElem = 0;
2161 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2162 int nbLinks = (*itEL).second.size();
2163 if ( nbLinks < minNbLinks ) {
2164 startElem = (*itEL).first;
2165 minNbLinks = nbLinks;
2166 if ( minNbLinks == 1 )
2171 // search elements to fuse starting from startElem or links of elements
2172 // fused earlyer - startLinks
2173 list< SMESH_TLink > startLinks;
2174 while ( startElem || !startLinks.empty() ) {
2175 while ( !startElem && !startLinks.empty() ) {
2176 // Get an element to start, by a link
2177 SMESH_TLink linkId = startLinks.front();
2178 startLinks.pop_front();
2179 itLE = mapLi_listEl.find( linkId );
2180 if ( itLE != mapLi_listEl.end() ) {
2181 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2182 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2183 for ( ; itE != listElem.end() ; itE++ )
2184 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2186 mapLi_listEl.erase( itLE );
2191 // Get candidates to be fused
2192 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2193 const SMESH_TLink *link12, *link13;
2195 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2196 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2197 ASSERT( !setLi.empty() );
2198 set< SMESH_TLink >::iterator itLi;
2199 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2201 const SMESH_TLink & link = (*itLi);
2202 itLE = mapLi_listEl.find( link );
2203 if ( itLE == mapLi_listEl.end() )
2206 const SMDS_MeshElement* elem = (*itLE).second.front();
2208 elem = (*itLE).second.back();
2209 mapLi_listEl.erase( itLE );
2210 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2221 // add other links of elem to list of links to re-start from
2222 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2223 set< SMESH_TLink >::iterator it;
2224 for ( it = links.begin(); it != links.end(); it++ ) {
2225 const SMESH_TLink& link2 = (*it);
2226 if ( link2 != link )
2227 startLinks.push_back( link2 );
2231 // Get nodes of possible quadrangles
2232 const SMDS_MeshNode *n12 [4], *n13 [4];
2233 bool Ok12 = false, Ok13 = false;
2234 const SMDS_MeshNode *linkNode1, *linkNode2;
2236 linkNode1 = link12->first;
2237 linkNode2 = link12->second;
2238 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2242 linkNode1 = link13->first;
2243 linkNode2 = link13->second;
2244 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2248 // Choose a pair to fuse
2249 if ( Ok12 && Ok13 ) {
2250 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2251 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2252 double aBadRate12 = getBadRate( &quad12, theCrit );
2253 double aBadRate13 = getBadRate( &quad13, theCrit );
2254 if ( aBadRate13 < aBadRate12 )
2261 // and remove fused elems and removed links from the maps
2262 mapEl_setLi.erase( tr1 );
2264 mapEl_setLi.erase( tr2 );
2265 mapLi_listEl.erase( *link12 );
2266 if(tr1->NbNodes()==3) {
2267 const SMDS_MeshElement* newElem = 0;
2268 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2269 myLastCreatedElems.Append(newElem);
2270 AddToSameGroups( newElem, tr1, aMesh );
2271 int aShapeId = tr1->getshapeId();
2274 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2276 aMesh->RemoveElement( tr1 );
2277 aMesh->RemoveElement( tr2 );
2280 const SMDS_MeshNode* N1 [6];
2281 const SMDS_MeshNode* N2 [6];
2282 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2283 // now we receive following N1 and N2 (using numeration as above image)
2284 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2285 // i.e. first nodes from both arrays determ new diagonal
2286 const SMDS_MeshNode* aNodes[8];
2295 const SMDS_MeshElement* newElem = 0;
2296 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2297 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2298 myLastCreatedElems.Append(newElem);
2299 AddToSameGroups( newElem, tr1, aMesh );
2300 int aShapeId = tr1->getshapeId();
2303 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2305 aMesh->RemoveElement( tr1 );
2306 aMesh->RemoveElement( tr2 );
2307 // remove middle node (9)
2308 GetMeshDS()->RemoveNode( N1[4] );
2312 mapEl_setLi.erase( tr3 );
2313 mapLi_listEl.erase( *link13 );
2314 if(tr1->NbNodes()==3) {
2315 const SMDS_MeshElement* newElem = 0;
2316 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2317 myLastCreatedElems.Append(newElem);
2318 AddToSameGroups( newElem, tr1, aMesh );
2319 int aShapeId = tr1->getshapeId();
2322 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2324 aMesh->RemoveElement( tr1 );
2325 aMesh->RemoveElement( tr3 );
2328 const SMDS_MeshNode* N1 [6];
2329 const SMDS_MeshNode* N2 [6];
2330 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2331 // now we receive following N1 and N2 (using numeration as above image)
2332 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2333 // i.e. first nodes from both arrays determ new diagonal
2334 const SMDS_MeshNode* aNodes[8];
2343 const SMDS_MeshElement* newElem = 0;
2344 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2345 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2346 myLastCreatedElems.Append(newElem);
2347 AddToSameGroups( newElem, tr1, aMesh );
2348 int aShapeId = tr1->getshapeId();
2351 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2353 aMesh->RemoveElement( tr1 );
2354 aMesh->RemoveElement( tr3 );
2355 // remove middle node (9)
2356 GetMeshDS()->RemoveNode( N1[4] );
2360 // Next element to fuse: the rejected one
2362 startElem = Ok12 ? tr3 : tr2;
2364 } // if ( startElem )
2365 } // while ( startElem || !startLinks.empty() )
2366 } // while ( ! mapEl_setLi.empty() )
2372 /*#define DUMPSO(txt) \
2373 // cout << txt << endl;
2374 //=============================================================================
2378 //=============================================================================
2379 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2383 int tmp = idNodes[ i1 ];
2384 idNodes[ i1 ] = idNodes[ i2 ];
2385 idNodes[ i2 ] = tmp;
2386 gp_Pnt Ptmp = P[ i1 ];
2389 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2392 //=======================================================================
2393 //function : SortQuadNodes
2394 //purpose : Set 4 nodes of a quadrangle face in a good order.
2395 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2397 //=======================================================================
2399 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2404 for ( i = 0; i < 4; i++ ) {
2405 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2407 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2410 gp_Vec V1(P[0], P[1]);
2411 gp_Vec V2(P[0], P[2]);
2412 gp_Vec V3(P[0], P[3]);
2414 gp_Vec Cross1 = V1 ^ V2;
2415 gp_Vec Cross2 = V2 ^ V3;
2418 if (Cross1.Dot(Cross2) < 0)
2423 if (Cross1.Dot(Cross2) < 0)
2427 swap ( i, i + 1, idNodes, P );
2429 // for ( int ii = 0; ii < 4; ii++ ) {
2430 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2431 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2437 //=======================================================================
2438 //function : SortHexaNodes
2439 //purpose : Set 8 nodes of a hexahedron in a good order.
2440 // Return success status
2441 //=======================================================================
2443 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2448 DUMPSO( "INPUT: ========================================");
2449 for ( i = 0; i < 8; i++ ) {
2450 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2451 if ( !n ) return false;
2452 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2453 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2455 DUMPSO( "========================================");
2458 set<int> faceNodes; // ids of bottom face nodes, to be found
2459 set<int> checkedId1; // ids of tried 2-nd nodes
2460 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2461 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2462 int iMin, iLoop1 = 0;
2464 // Loop to try the 2-nd nodes
2466 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2468 // Find not checked 2-nd node
2469 for ( i = 1; i < 8; i++ )
2470 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2471 int id1 = idNodes[i];
2472 swap ( 1, i, idNodes, P );
2473 checkedId1.insert ( id1 );
2477 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2478 // ie that all but meybe one (id3 which is on the same face) nodes
2479 // lay on the same side from the triangle plane.
2481 bool manyInPlane = false; // more than 4 nodes lay in plane
2483 while ( ++iLoop2 < 6 ) {
2485 // get 1-2-3 plane coeffs
2486 Standard_Real A, B, C, D;
2487 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2488 if ( N.SquareMagnitude() > gp::Resolution() )
2490 gp_Pln pln ( P[0], N );
2491 pln.Coefficients( A, B, C, D );
2493 // find the node (iMin) closest to pln
2494 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2496 for ( i = 3; i < 8; i++ ) {
2497 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2498 if ( fabs( dist[i] ) < minDist ) {
2499 minDist = fabs( dist[i] );
2502 if ( fabs( dist[i] ) <= tol )
2503 idInPln.insert( idNodes[i] );
2506 // there should not be more than 4 nodes in bottom plane
2507 if ( idInPln.size() > 1 )
2509 DUMPSO( "### idInPln.size() = " << idInPln.size());
2510 // idInPlane does not contain the first 3 nodes
2511 if ( manyInPlane || idInPln.size() == 5)
2512 return false; // all nodes in one plane
2515 // set the 1-st node to be not in plane
2516 for ( i = 3; i < 8; i++ ) {
2517 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2518 DUMPSO( "### Reset 0-th node");
2519 swap( 0, i, idNodes, P );
2524 // reset to re-check second nodes
2525 leastDist = DBL_MAX;
2529 break; // from iLoop2;
2532 // check that the other 4 nodes are on the same side
2533 bool sameSide = true;
2534 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2535 for ( i = 3; sameSide && i < 8; i++ ) {
2537 sameSide = ( isNeg == dist[i] <= 0.);
2540 // keep best solution
2541 if ( sameSide && minDist < leastDist ) {
2542 leastDist = minDist;
2544 faceNodes.insert( idNodes[ 1 ] );
2545 faceNodes.insert( idNodes[ 2 ] );
2546 faceNodes.insert( idNodes[ iMin ] );
2547 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2548 << " leastDist = " << leastDist);
2549 if ( leastDist <= DBL_MIN )
2554 // set next 3-d node to check
2555 int iNext = 2 + iLoop2;
2557 DUMPSO( "Try 2-nd");
2558 swap ( 2, iNext, idNodes, P );
2560 } // while ( iLoop2 < 6 )
2563 if ( faceNodes.empty() ) return false;
2565 // Put the faceNodes in proper places
2566 for ( i = 4; i < 8; i++ ) {
2567 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2568 // find a place to put
2570 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2572 DUMPSO( "Set faceNodes");
2573 swap ( iTo, i, idNodes, P );
2578 // Set nodes of the found bottom face in good order
2579 DUMPSO( " Found bottom face: ");
2580 i = SortQuadNodes( theMesh, idNodes );
2582 gp_Pnt Ptmp = P[ i ];
2587 // for ( int ii = 0; ii < 4; ii++ ) {
2588 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2589 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2592 // Gravity center of the top and bottom faces
2593 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2594 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2596 // Get direction from the bottom to the top face
2597 gp_Vec upDir ( aGCb, aGCt );
2598 Standard_Real upDirSize = upDir.Magnitude();
2599 if ( upDirSize <= gp::Resolution() ) return false;
2602 // Assure that the bottom face normal points up
2603 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2604 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2605 if ( Nb.Dot( upDir ) < 0 ) {
2606 DUMPSO( "Reverse bottom face");
2607 swap( 1, 3, idNodes, P );
2610 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2611 Standard_Real minDist = DBL_MAX;
2612 for ( i = 4; i < 8; i++ ) {
2613 // projection of P[i] to the plane defined by P[0] and upDir
2614 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2615 Standard_Real sqDist = P[0].SquareDistance( Pp );
2616 if ( sqDist < minDist ) {
2621 DUMPSO( "Set 4-th");
2622 swap ( 4, iMin, idNodes, P );
2624 // Set nodes of the top face in good order
2625 DUMPSO( "Sort top face");
2626 i = SortQuadNodes( theMesh, &idNodes[4] );
2629 gp_Pnt Ptmp = P[ i ];
2634 // Assure that direction of the top face normal is from the bottom face
2635 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2636 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2637 if ( Nt.Dot( upDir ) < 0 ) {
2638 DUMPSO( "Reverse top face");
2639 swap( 5, 7, idNodes, P );
2642 // DUMPSO( "OUTPUT: ========================================");
2643 // for ( i = 0; i < 8; i++ ) {
2644 // float *p = ugrid->GetPoint(idNodes[i]);
2645 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2651 //================================================================================
2653 * \brief Return nodes linked to the given one
2654 * \param theNode - the node
2655 * \param linkedNodes - the found nodes
2656 * \param type - the type of elements to check
2658 * Medium nodes are ignored
2660 //================================================================================
2662 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2663 TIDSortedElemSet & linkedNodes,
2664 SMDSAbs_ElementType type )
2666 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2667 while ( elemIt->more() )
2669 const SMDS_MeshElement* elem = elemIt->next();
2670 if(elem->GetType() == SMDSAbs_0DElement)
2673 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2674 if ( elem->GetType() == SMDSAbs_Volume )
2676 SMDS_VolumeTool vol( elem );
2677 while ( nodeIt->more() ) {
2678 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2679 if ( theNode != n && vol.IsLinked( theNode, n ))
2680 linkedNodes.insert( n );
2685 for ( int i = 0; nodeIt->more(); ++i ) {
2686 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2687 if ( n == theNode ) {
2688 int iBefore = i - 1;
2690 if ( elem->IsQuadratic() ) {
2691 int nb = elem->NbNodes() / 2;
2692 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2693 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2695 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2696 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2703 //=======================================================================
2704 //function : laplacianSmooth
2705 //purpose : pulls theNode toward the center of surrounding nodes directly
2706 // connected to that node along an element edge
2707 //=======================================================================
2709 void laplacianSmooth(const SMDS_MeshNode* theNode,
2710 const Handle(Geom_Surface)& theSurface,
2711 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2713 // find surrounding nodes
2715 TIDSortedElemSet nodeSet;
2716 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2718 // compute new coodrs
2720 double coord[] = { 0., 0., 0. };
2721 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2722 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2723 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2724 if ( theSurface.IsNull() ) { // smooth in 3D
2725 coord[0] += node->X();
2726 coord[1] += node->Y();
2727 coord[2] += node->Z();
2729 else { // smooth in 2D
2730 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2731 gp_XY* uv = theUVMap[ node ];
2732 coord[0] += uv->X();
2733 coord[1] += uv->Y();
2736 int nbNodes = nodeSet.size();
2739 coord[0] /= nbNodes;
2740 coord[1] /= nbNodes;
2742 if ( !theSurface.IsNull() ) {
2743 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2744 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2745 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2751 coord[2] /= nbNodes;
2755 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2758 //=======================================================================
2759 //function : centroidalSmooth
2760 //purpose : pulls theNode toward the element-area-weighted centroid of the
2761 // surrounding elements
2762 //=======================================================================
2764 void centroidalSmooth(const SMDS_MeshNode* theNode,
2765 const Handle(Geom_Surface)& theSurface,
2766 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2768 gp_XYZ aNewXYZ(0.,0.,0.);
2769 SMESH::Controls::Area anAreaFunc;
2770 double totalArea = 0.;
2775 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2776 while ( elemIt->more() )
2778 const SMDS_MeshElement* elem = elemIt->next();
2781 gp_XYZ elemCenter(0.,0.,0.);
2782 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2783 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2784 int nn = elem->NbNodes();
2785 if(elem->IsQuadratic()) nn = nn/2;
2787 //while ( itN->more() ) {
2789 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2791 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2792 aNodePoints.push_back( aP );
2793 if ( !theSurface.IsNull() ) { // smooth in 2D
2794 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2795 gp_XY* uv = theUVMap[ aNode ];
2796 aP.SetCoord( uv->X(), uv->Y(), 0. );
2800 double elemArea = anAreaFunc.GetValue( aNodePoints );
2801 totalArea += elemArea;
2803 aNewXYZ += elemCenter * elemArea;
2805 aNewXYZ /= totalArea;
2806 if ( !theSurface.IsNull() ) {
2807 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2808 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2813 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2816 //=======================================================================
2817 //function : getClosestUV
2818 //purpose : return UV of closest projection
2819 //=======================================================================
2821 static bool getClosestUV (Extrema_GenExtPS& projector,
2822 const gp_Pnt& point,
2825 projector.Perform( point );
2826 if ( projector.IsDone() ) {
2827 double u, v, minVal = DBL_MAX;
2828 for ( int i = projector.NbExt(); i > 0; i-- )
2829 if ( projector.Value( i ) < minVal ) {
2830 minVal = projector.Value( i );
2831 projector.Point( i ).Parameter( u, v );
2833 result.SetCoord( u, v );
2839 //=======================================================================
2841 //purpose : Smooth theElements during theNbIterations or until a worst
2842 // element has aspect ratio <= theTgtAspectRatio.
2843 // Aspect Ratio varies in range [1.0, inf].
2844 // If theElements is empty, the whole mesh is smoothed.
2845 // theFixedNodes contains additionally fixed nodes. Nodes built
2846 // on edges and boundary nodes are always fixed.
2847 //=======================================================================
2849 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
2850 set<const SMDS_MeshNode*> & theFixedNodes,
2851 const SmoothMethod theSmoothMethod,
2852 const int theNbIterations,
2853 double theTgtAspectRatio,
2856 myLastCreatedElems.Clear();
2857 myLastCreatedNodes.Clear();
2859 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2861 if ( theTgtAspectRatio < 1.0 )
2862 theTgtAspectRatio = 1.0;
2864 const double disttol = 1.e-16;
2866 SMESH::Controls::AspectRatio aQualityFunc;
2868 SMESHDS_Mesh* aMesh = GetMeshDS();
2870 if ( theElems.empty() ) {
2871 // add all faces to theElems
2872 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2873 while ( fIt->more() ) {
2874 const SMDS_MeshElement* face = fIt->next();
2875 theElems.insert( face );
2878 // get all face ids theElems are on
2879 set< int > faceIdSet;
2880 TIDSortedElemSet::iterator itElem;
2882 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2883 int fId = FindShape( *itElem );
2884 // check that corresponding submesh exists and a shape is face
2886 faceIdSet.find( fId ) == faceIdSet.end() &&
2887 aMesh->MeshElements( fId )) {
2888 TopoDS_Shape F = aMesh->IndexToShape( fId );
2889 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2890 faceIdSet.insert( fId );
2893 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2895 // ===============================================
2896 // smooth elements on each TopoDS_Face separately
2897 // ===============================================
2899 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2900 for ( ; fId != faceIdSet.rend(); ++fId ) {
2901 // get face surface and submesh
2902 Handle(Geom_Surface) surface;
2903 SMESHDS_SubMesh* faceSubMesh = 0;
2905 double fToler2 = 0, f,l;
2906 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2907 bool isUPeriodic = false, isVPeriodic = false;
2909 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
2910 surface = BRep_Tool::Surface( face );
2911 faceSubMesh = aMesh->MeshElements( *fId );
2912 fToler2 = BRep_Tool::Tolerance( face );
2913 fToler2 *= fToler2 * 10.;
2914 isUPeriodic = surface->IsUPeriodic();
2917 isVPeriodic = surface->IsVPeriodic();
2920 surface->Bounds( u1, u2, v1, v2 );
2922 // ---------------------------------------------------------
2923 // for elements on a face, find movable and fixed nodes and
2924 // compute UV for them
2925 // ---------------------------------------------------------
2926 bool checkBoundaryNodes = false;
2927 bool isQuadratic = false;
2928 set<const SMDS_MeshNode*> setMovableNodes;
2929 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
2930 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
2931 list< const SMDS_MeshElement* > elemsOnFace;
2933 Extrema_GenExtPS projector;
2934 GeomAdaptor_Surface surfAdaptor;
2935 if ( !surface.IsNull() ) {
2936 surfAdaptor.Load( surface );
2937 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
2939 int nbElemOnFace = 0;
2940 itElem = theElems.begin();
2941 // loop on not yet smoothed elements: look for elems on a face
2942 while ( itElem != theElems.end() ) {
2943 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
2944 break; // all elements found
2946 const SMDS_MeshElement* elem = *itElem;
2947 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
2948 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
2952 elemsOnFace.push_back( elem );
2953 theElems.erase( itElem++ );
2957 isQuadratic = elem->IsQuadratic();
2959 // get movable nodes of elem
2960 const SMDS_MeshNode* node;
2961 SMDS_TypeOfPosition posType;
2962 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2963 int nn = 0, nbn = elem->NbNodes();
2964 if(elem->IsQuadratic())
2966 while ( nn++ < nbn ) {
2967 node = static_cast<const SMDS_MeshNode*>( itN->next() );
2968 const SMDS_PositionPtr& pos = node->GetPosition();
2969 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
2970 if (posType != SMDS_TOP_EDGE &&
2971 posType != SMDS_TOP_VERTEX &&
2972 theFixedNodes.find( node ) == theFixedNodes.end())
2974 // check if all faces around the node are on faceSubMesh
2975 // because a node on edge may be bound to face
2976 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
2978 if ( faceSubMesh ) {
2979 while ( eIt->more() && all ) {
2980 const SMDS_MeshElement* e = eIt->next();
2981 all = faceSubMesh->Contains( e );
2985 setMovableNodes.insert( node );
2987 checkBoundaryNodes = true;
2989 if ( posType == SMDS_TOP_3DSPACE )
2990 checkBoundaryNodes = true;
2993 if ( surface.IsNull() )
2996 // get nodes to check UV
2997 list< const SMDS_MeshNode* > uvCheckNodes;
2998 itN = elem->nodesIterator();
2999 nn = 0; nbn = elem->NbNodes();
3000 if(elem->IsQuadratic())
3002 while ( nn++ < nbn ) {
3003 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3004 if ( uvMap.find( node ) == uvMap.end() )
3005 uvCheckNodes.push_back( node );
3006 // add nodes of elems sharing node
3007 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3008 // while ( eIt->more() ) {
3009 // const SMDS_MeshElement* e = eIt->next();
3010 // if ( e != elem ) {
3011 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3012 // while ( nIt->more() ) {
3013 // const SMDS_MeshNode* n =
3014 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3015 // if ( uvMap.find( n ) == uvMap.end() )
3016 // uvCheckNodes.push_back( n );
3022 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3023 for ( ; n != uvCheckNodes.end(); ++n ) {
3026 const SMDS_PositionPtr& pos = node->GetPosition();
3027 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3029 switch ( posType ) {
3030 case SMDS_TOP_FACE: {
3031 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3032 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3035 case SMDS_TOP_EDGE: {
3036 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3037 Handle(Geom2d_Curve) pcurve;
3038 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3039 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3040 if ( !pcurve.IsNull() ) {
3041 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3042 uv = pcurve->Value( u ).XY();
3046 case SMDS_TOP_VERTEX: {
3047 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3048 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3049 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3054 // check existing UV
3055 bool project = true;
3056 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3057 double dist1 = DBL_MAX, dist2 = 0;
3058 if ( posType != SMDS_TOP_3DSPACE ) {
3059 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3060 project = dist1 > fToler2;
3062 if ( project ) { // compute new UV
3064 if ( !getClosestUV( projector, pNode, newUV )) {
3065 MESSAGE("Node Projection Failed " << node);
3069 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3071 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3073 if ( posType != SMDS_TOP_3DSPACE )
3074 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3075 if ( dist2 < dist1 )
3079 // store UV in the map
3080 listUV.push_back( uv );
3081 uvMap.insert( make_pair( node, &listUV.back() ));
3083 } // loop on not yet smoothed elements
3085 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3086 checkBoundaryNodes = true;
3088 // fix nodes on mesh boundary
3090 if ( checkBoundaryNodes ) {
3091 map< NLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3092 map< NLink, int >::iterator link_nb;
3093 // put all elements links to linkNbMap
3094 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3095 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3096 const SMDS_MeshElement* elem = (*elemIt);
3097 int nbn = elem->NbNodes();
3098 if(elem->IsQuadratic())
3100 // loop on elem links: insert them in linkNbMap
3101 const SMDS_MeshNode* curNode, *prevNode = elem->GetNodeWrap( nbn );
3102 for ( int iN = 0; iN < nbn; ++iN ) {
3103 curNode = elem->GetNode( iN );
3105 if ( curNode < prevNode ) link = make_pair( curNode , prevNode );
3106 else link = make_pair( prevNode , curNode );
3108 link_nb = linkNbMap.find( link );
3109 if ( link_nb == linkNbMap.end() )
3110 linkNbMap.insert( make_pair ( link, 1 ));
3115 // remove nodes that are in links encountered only once from setMovableNodes
3116 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3117 if ( link_nb->second == 1 ) {
3118 setMovableNodes.erase( link_nb->first.first );
3119 setMovableNodes.erase( link_nb->first.second );
3124 // -----------------------------------------------------
3125 // for nodes on seam edge, compute one more UV ( uvMap2 );
3126 // find movable nodes linked to nodes on seam and which
3127 // are to be smoothed using the second UV ( uvMap2 )
3128 // -----------------------------------------------------
3130 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3131 if ( !surface.IsNull() ) {
3132 TopExp_Explorer eExp( face, TopAbs_EDGE );
3133 for ( ; eExp.More(); eExp.Next() ) {
3134 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3135 if ( !BRep_Tool::IsClosed( edge, face ))
3137 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3138 if ( !sm ) continue;
3139 // find out which parameter varies for a node on seam
3142 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3143 if ( pcurve.IsNull() ) continue;
3144 uv1 = pcurve->Value( f );
3146 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3147 if ( pcurve.IsNull() ) continue;
3148 uv2 = pcurve->Value( f );
3149 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3151 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3152 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3154 // get nodes on seam and its vertices
3155 list< const SMDS_MeshNode* > seamNodes;
3156 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3157 while ( nSeamIt->more() ) {
3158 const SMDS_MeshNode* node = nSeamIt->next();
3159 if ( !isQuadratic || !IsMedium( node ))
3160 seamNodes.push_back( node );
3162 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3163 for ( ; vExp.More(); vExp.Next() ) {
3164 sm = aMesh->MeshElements( vExp.Current() );
3166 nSeamIt = sm->GetNodes();
3167 while ( nSeamIt->more() )
3168 seamNodes.push_back( nSeamIt->next() );
3171 // loop on nodes on seam
3172 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3173 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3174 const SMDS_MeshNode* nSeam = *noSeIt;
3175 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3176 if ( n_uv == uvMap.end() )
3179 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3180 // set the second UV
3181 listUV.push_back( *n_uv->second );
3182 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3183 if ( uvMap2.empty() )
3184 uvMap2 = uvMap; // copy the uvMap contents
3185 uvMap2[ nSeam ] = &listUV.back();
3187 // collect movable nodes linked to ones on seam in nodesNearSeam
3188 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3189 while ( eIt->more() ) {
3190 const SMDS_MeshElement* e = eIt->next();
3191 int nbUseMap1 = 0, nbUseMap2 = 0;
3192 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3193 int nn = 0, nbn = e->NbNodes();
3194 if(e->IsQuadratic()) nbn = nbn/2;
3195 while ( nn++ < nbn )
3197 const SMDS_MeshNode* n =
3198 static_cast<const SMDS_MeshNode*>( nIt->next() );
3200 setMovableNodes.find( n ) == setMovableNodes.end() )
3202 // add only nodes being closer to uv2 than to uv1
3203 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3204 0.5 * ( n->Y() + nSeam->Y() ),
3205 0.5 * ( n->Z() + nSeam->Z() ));
3207 getClosestUV( projector, pMid, uv );
3208 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3209 nodesNearSeam.insert( n );
3215 // for centroidalSmooth all element nodes must
3216 // be on one side of a seam
3217 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3218 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3220 while ( nn++ < nbn ) {
3221 const SMDS_MeshNode* n =
3222 static_cast<const SMDS_MeshNode*>( nIt->next() );
3223 setMovableNodes.erase( n );
3227 } // loop on nodes on seam
3228 } // loop on edge of a face
3229 } // if ( !face.IsNull() )
3231 if ( setMovableNodes.empty() ) {
3232 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3233 continue; // goto next face
3241 double maxRatio = -1., maxDisplacement = -1.;
3242 set<const SMDS_MeshNode*>::iterator nodeToMove;
3243 for ( it = 0; it < theNbIterations; it++ ) {
3244 maxDisplacement = 0.;
3245 nodeToMove = setMovableNodes.begin();
3246 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3247 const SMDS_MeshNode* node = (*nodeToMove);
3248 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3251 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3252 if ( theSmoothMethod == LAPLACIAN )
3253 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3255 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3257 // node displacement
3258 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3259 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3260 if ( aDispl > maxDisplacement )
3261 maxDisplacement = aDispl;
3263 // no node movement => exit
3264 //if ( maxDisplacement < 1.e-16 ) {
3265 if ( maxDisplacement < disttol ) {
3266 MESSAGE("-- no node movement --");
3270 // check elements quality
3272 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3273 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3274 const SMDS_MeshElement* elem = (*elemIt);
3275 if ( !elem || elem->GetType() != SMDSAbs_Face )
3277 SMESH::Controls::TSequenceOfXYZ aPoints;
3278 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3279 double aValue = aQualityFunc.GetValue( aPoints );
3280 if ( aValue > maxRatio )
3284 if ( maxRatio <= theTgtAspectRatio ) {
3285 MESSAGE("-- quality achived --");
3288 if (it+1 == theNbIterations) {
3289 MESSAGE("-- Iteration limit exceeded --");
3291 } // smoothing iterations
3293 MESSAGE(" Face id: " << *fId <<
3294 " Nb iterstions: " << it <<
3295 " Displacement: " << maxDisplacement <<
3296 " Aspect Ratio " << maxRatio);
3298 // ---------------------------------------
3299 // new nodes positions are computed,
3300 // record movement in DS and set new UV
3301 // ---------------------------------------
3302 nodeToMove = setMovableNodes.begin();
3303 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3304 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3305 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3306 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3307 if ( node_uv != uvMap.end() ) {
3308 gp_XY* uv = node_uv->second;
3310 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3314 // move medium nodes of quadratic elements
3317 SMESH_MesherHelper helper( *GetMesh() );
3318 if ( !face.IsNull() )
3319 helper.SetSubShape( face );
3320 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3321 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3322 const SMDS_VtkFace* QF =
3323 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3324 if(QF && QF->IsQuadratic()) {
3325 vector<const SMDS_MeshNode*> Ns;
3326 Ns.reserve(QF->NbNodes()+1);
3327 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3328 while ( anIter->more() )
3329 Ns.push_back( cast2Node(anIter->next()) );
3330 Ns.push_back( Ns[0] );
3332 for(int i=0; i<QF->NbNodes(); i=i+2) {
3333 if ( !surface.IsNull() ) {
3334 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3335 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3336 gp_XY uv = ( uv1 + uv2 ) / 2.;
3337 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3338 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3341 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3342 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3343 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3345 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3346 fabs( Ns[i+1]->Y() - y ) > disttol ||
3347 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3348 // we have to move i+1 node
3349 aMesh->MoveNode( Ns[i+1], x, y, z );
3356 } // loop on face ids
3360 //=======================================================================
3361 //function : isReverse
3362 //purpose : Return true if normal of prevNodes is not co-directied with
3363 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3364 // iNotSame is where prevNodes and nextNodes are different
3365 //=======================================================================
3367 static bool isReverse(vector<const SMDS_MeshNode*> prevNodes,
3368 vector<const SMDS_MeshNode*> nextNodes,
3372 int iBeforeNotSame = ( iNotSame == 0 ? nbNodes - 1 : iNotSame - 1 );
3373 int iAfterNotSame = ( iNotSame + 1 == nbNodes ? 0 : iNotSame + 1 );
3375 const SMDS_MeshNode* nB = prevNodes[ iBeforeNotSame ];
3376 const SMDS_MeshNode* nA = prevNodes[ iAfterNotSame ];
3377 const SMDS_MeshNode* nP = prevNodes[ iNotSame ];
3378 const SMDS_MeshNode* nN = nextNodes[ iNotSame ];
3380 gp_Pnt pB ( nB->X(), nB->Y(), nB->Z() );
3381 gp_Pnt pA ( nA->X(), nA->Y(), nA->Z() );
3382 gp_Pnt pP ( nP->X(), nP->Y(), nP->Z() );
3383 gp_Pnt pN ( nN->X(), nN->Y(), nN->Z() );
3385 gp_Vec vB ( pP, pB ), vA ( pP, pA ), vN ( pP, pN );
3387 return (vA ^ vB) * vN < 0.0;
3390 //=======================================================================
3392 * \brief Create elements by sweeping an element
3393 * \param elem - element to sweep
3394 * \param newNodesItVec - nodes generated from each node of the element
3395 * \param newElems - generated elements
3396 * \param nbSteps - number of sweeping steps
3397 * \param srcElements - to append elem for each generated element
3399 //=======================================================================
3401 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3402 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3403 list<const SMDS_MeshElement*>& newElems,
3405 SMESH_SequenceOfElemPtr& srcElements)
3407 //MESSAGE("sweepElement " << nbSteps);
3408 SMESHDS_Mesh* aMesh = GetMeshDS();
3410 // Loop on elem nodes:
3411 // find new nodes and detect same nodes indices
3412 int nbNodes = elem->NbNodes();
3413 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3414 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3415 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3416 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3418 int iNode, nbSame = 0, iNotSameNode = 0, iSameNode = 0;
3419 vector<int> sames(nbNodes);
3420 vector<bool> issimple(nbNodes);
3422 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3423 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3424 const SMDS_MeshNode* node = nnIt->first;
3425 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3426 if ( listNewNodes.empty() ) {
3430 issimple[iNode] = (listNewNodes.size()==nbSteps); // is node medium
3432 itNN[ iNode ] = listNewNodes.begin();
3433 prevNod[ iNode ] = node;
3434 nextNod[ iNode ] = listNewNodes.front();
3435 if( !elem->IsQuadratic() || !issimple[iNode] ) {
3436 if ( prevNod[ iNode ] != nextNod [ iNode ])
3437 iNotSameNode = iNode;
3441 sames[nbSame++] = iNode;
3446 //cerr<<" nbSame = "<<nbSame<<endl;
3447 if ( nbSame == nbNodes || nbSame > 2) {
3448 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3449 //INFOS( " Too many same nodes of element " << elem->GetID() );
3453 // if( elem->IsQuadratic() && nbSame>0 ) {
3454 // MESSAGE( "Can not rotate quadratic element " << elem->GetID() );
3458 int iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3459 int nbBaseNodes = ( elem->IsQuadratic() ? nbNodes/2 : nbNodes );
3461 iBeforeSame = ( iSameNode == 0 ? nbBaseNodes - 1 : iSameNode - 1 );
3462 iAfterSame = ( iSameNode + 1 == nbBaseNodes ? 0 : iSameNode + 1 );
3463 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3467 //cout<<" prevNod[0]="<< prevNod[0]<<" prevNod[1]="<< prevNod[1]
3468 // <<" prevNod[2]="<< prevNod[2]<<" prevNod[3]="<< prevNod[4]
3469 // <<" prevNod[4]="<< prevNod[4]<<" prevNod[5]="<< prevNod[5]
3470 // <<" prevNod[6]="<< prevNod[6]<<" prevNod[7]="<< prevNod[7]<<endl;
3472 // check element orientation
3474 if ( nbNodes > 2 && !isReverse( prevNod, nextNod, nbNodes, iNotSameNode )) {
3475 //MESSAGE("Reversed elem " << elem );
3479 std::swap( iBeforeSame, iAfterSame );
3482 // make new elements
3483 const SMDS_MeshElement* lastElem = elem;
3484 for (int iStep = 0; iStep < nbSteps; iStep++ ) {
3486 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3487 if(issimple[iNode]) {
3488 nextNod[ iNode ] = *itNN[ iNode ];
3492 if( elem->GetType()==SMDSAbs_Node ) {
3493 // we have to use two nodes
3494 midlNod[ iNode ] = *itNN[ iNode ];
3496 nextNod[ iNode ] = *itNN[ iNode ];
3499 else if(!elem->IsQuadratic() || lastElem->IsMediumNode(prevNod[iNode]) ) {
3500 // we have to use each second node
3502 nextNod[ iNode ] = *itNN[ iNode ];
3506 // we have to use two nodes
3507 midlNod[ iNode ] = *itNN[ iNode ];
3509 nextNod[ iNode ] = *itNN[ iNode ];
3514 SMDS_MeshElement* aNewElem = 0;
3515 if(!elem->IsPoly()) {
3516 switch ( nbNodes ) {
3520 if ( nbSame == 0 ) {
3522 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3524 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3530 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3531 nextNod[ 1 ], nextNod[ 0 ] );
3533 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3534 nextNod[ iNotSameNode ] );
3538 case 3: { // TRIANGLE or quadratic edge
3539 if(elem->GetType() == SMDSAbs_Face) { // TRIANGLE
3541 if ( nbSame == 0 ) // --- pentahedron
3542 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3543 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ] );
3545 else if ( nbSame == 1 ) // --- pyramid
3546 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3547 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3548 nextNod[ iSameNode ]);
3550 else // 2 same nodes: --- tetrahedron
3551 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ],
3552 nextNod[ iNotSameNode ]);
3554 else { // quadratic edge
3555 if(nbSame==0) { // quadratic quadrangle
3556 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], nextNod[1], prevNod[1],
3557 midlNod[0], nextNod[2], midlNod[1], prevNod[2]);
3559 else if(nbSame==1) { // quadratic triangle
3561 return; // medium node on axis
3563 else if(sames[0]==0) {
3564 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3565 nextNod[2], midlNod[1], prevNod[2]);
3567 else { // sames[0]==1
3568 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3569 midlNod[0], nextNod[2], prevNod[2]);
3578 case 4: { // QUADRANGLE
3580 if ( nbSame == 0 ) // --- hexahedron
3581 aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], prevNod[ 3 ],
3582 nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ], nextNod[ 3 ]);
3584 else if ( nbSame == 1 ) { // --- pyramid + pentahedron
3585 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3586 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3587 nextNod[ iSameNode ]);
3588 newElems.push_back( aNewElem );
3589 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3590 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3591 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3593 else if ( nbSame == 2 ) { // pentahedron
3594 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3595 // iBeforeSame is same too
3596 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3597 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3598 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3600 // iAfterSame is same too
3601 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3602 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3603 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3607 case 6: { // quadratic triangle
3608 // create pentahedron with 15 nodes
3610 if(i0>0) { // reversed case
3611 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[2], prevNod[1],
3612 nextNod[0], nextNod[2], nextNod[1],
3613 prevNod[5], prevNod[4], prevNod[3],
3614 nextNod[5], nextNod[4], nextNod[3],
3615 midlNod[0], midlNod[2], midlNod[1]);
3617 else { // not reversed case
3618 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3619 nextNod[0], nextNod[1], nextNod[2],
3620 prevNod[3], prevNod[4], prevNod[5],
3621 nextNod[3], nextNod[4], nextNod[5],
3622 midlNod[0], midlNod[1], midlNod[2]);
3625 else if(nbSame==1) {
3626 // 2d order pyramid of 13 nodes
3627 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5,
3628 // int n12,int n23,int n34,int n41,
3629 // int n15,int n25,int n35,int n45, int ID);
3631 int n1,n4,n41,n15,n45;
3632 if(i0>0) { // reversed case
3633 n1 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3634 n4 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3640 n1 = ( n5 == 0 ? nbBaseNodes - 1 : n5 - 1 );
3641 n4 = ( n5 + 1 == nbBaseNodes ? 0 : n5 + 1 );
3646 aNewElem = aMesh->AddVolume(prevNod[n1], nextNod[n1],
3647 nextNod[n4], prevNod[n4], prevNod[n5],
3648 midlNod[n1], nextNod[n41],
3649 midlNod[n4], prevNod[n41],
3650 prevNod[n15], nextNod[n15],
3651 nextNod[n45], prevNod[n45]);
3653 else if(nbSame==2) {
3654 // 2d order tetrahedron of 10 nodes
3655 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4,
3656 // int n12,int n23,int n31,
3657 // int n14,int n24,int n34, int ID);
3658 int n1 = iNotSameNode;
3659 int n2,n3,n12,n23,n31;
3660 if(i0>0) { // reversed case
3661 n2 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3662 n3 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3668 n2 = ( n1 + 1 == nbBaseNodes ? 0 : n1 + 1 );
3669 n3 = ( n1 == 0 ? nbBaseNodes - 1 : n1 - 1 );
3674 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3675 prevNod[n12], prevNod[n23], prevNod[n31],
3676 midlNod[n1], nextNod[n12], nextNod[n31]);
3680 case 8: { // quadratic quadrangle
3682 // create hexahedron with 20 nodes
3683 if(i0>0) { // reversed case
3684 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[3], prevNod[2], prevNod[1],
3685 nextNod[0], nextNod[3], nextNod[2], nextNod[1],
3686 prevNod[7], prevNod[6], prevNod[5], prevNod[4],
3687 nextNod[7], nextNod[6], nextNod[5], nextNod[4],
3688 midlNod[0], midlNod[3], midlNod[2], midlNod[1]);
3690 else { // not reversed case
3691 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3692 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3693 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3694 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3695 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3698 else if(nbSame==1) {
3699 // --- pyramid + pentahedron - can not be created since it is needed
3700 // additional middle node ot the center of face
3701 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3704 else if(nbSame==2) {
3705 // 2d order Pentahedron with 15 nodes
3706 //SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6,
3707 // int n12,int n23,int n31,int n45,int n56,int n64,
3708 // int n14,int n25,int n36, int ID);
3710 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3711 // iBeforeSame is same too
3718 // iAfterSame is same too
3724 int n12,n45,n14,n25;
3725 if(i0>0) { //reversed case
3737 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3738 prevNod[n4], prevNod[n5], nextNod[n5],
3739 prevNod[n12], midlNod[n2], nextNod[n12],
3740 prevNod[n45], midlNod[n5], nextNod[n45],
3741 prevNod[n14], prevNod[n25], nextNod[n25]);
3746 // realized for extrusion only
3747 //vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3748 //vector<int> quantities (nbNodes + 2);
3750 //quantities[0] = nbNodes; // bottom of prism
3751 //for (int inode = 0; inode < nbNodes; inode++) {
3752 // polyedre_nodes[inode] = prevNod[inode];
3755 //quantities[1] = nbNodes; // top of prism
3756 //for (int inode = 0; inode < nbNodes; inode++) {
3757 // polyedre_nodes[nbNodes + inode] = nextNod[inode];
3760 //for (int iface = 0; iface < nbNodes; iface++) {
3761 // quantities[iface + 2] = 4;
3762 // int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3763 // polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3764 // polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3765 // polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3766 // polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3768 //aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3775 // realized for extrusion only
3776 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3777 vector<int> quantities (nbNodes + 2);
3779 quantities[0] = nbNodes; // bottom of prism
3780 for (int inode = 0; inode < nbNodes; inode++) {
3781 polyedre_nodes[inode] = prevNod[inode];
3784 quantities[1] = nbNodes; // top of prism
3785 for (int inode = 0; inode < nbNodes; inode++) {
3786 polyedre_nodes[nbNodes + inode] = nextNod[inode];
3789 for (int iface = 0; iface < nbNodes; iface++) {
3790 quantities[iface + 2] = 4;
3791 int inextface = (iface == nbNodes - 1) ? 0 : iface + 1;
3792 polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface];
3793 polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface];
3794 polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface];
3795 polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface];
3797 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3801 newElems.push_back( aNewElem );
3802 myLastCreatedElems.Append(aNewElem);
3803 srcElements.Append( elem );
3804 lastElem = aNewElem;
3807 // set new prev nodes
3808 for ( iNode = 0; iNode < nbNodes; iNode++ )
3809 prevNod[ iNode ] = nextNod[ iNode ];
3814 //=======================================================================
3816 * \brief Create 1D and 2D elements around swept elements
3817 * \param mapNewNodes - source nodes and ones generated from them
3818 * \param newElemsMap - source elements and ones generated from them
3819 * \param elemNewNodesMap - nodes generated from each node of each element
3820 * \param elemSet - all swept elements
3821 * \param nbSteps - number of sweeping steps
3822 * \param srcElements - to append elem for each generated element
3824 //=======================================================================
3826 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3827 TElemOfElemListMap & newElemsMap,
3828 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3829 TIDSortedElemSet& elemSet,
3831 SMESH_SequenceOfElemPtr& srcElements)
3833 MESSAGE("makeWalls");
3834 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3835 SMESHDS_Mesh* aMesh = GetMeshDS();
3837 // Find nodes belonging to only one initial element - sweep them to get edges.
3839 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3840 for ( ; nList != mapNewNodes.end(); nList++ ) {
3841 const SMDS_MeshNode* node =
3842 static_cast<const SMDS_MeshNode*>( nList->first );
3843 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3844 int nbInitElems = 0;
3845 const SMDS_MeshElement* el = 0;
3846 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3847 while ( eIt->more() && nbInitElems < 2 ) {
3849 SMDSAbs_ElementType type = el->GetType();
3850 if ( type == SMDSAbs_Volume || type < highType ) continue;
3851 if ( type > highType ) {
3855 if ( elemSet.find(el) != elemSet.end() )
3858 if ( nbInitElems < 2 ) {
3859 bool NotCreateEdge = el && el->IsQuadratic() && el->IsMediumNode(node);
3860 if(!NotCreateEdge) {
3861 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3862 list<const SMDS_MeshElement*> newEdges;
3863 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3868 // Make a ceiling for each element ie an equal element of last new nodes.
3869 // Find free links of faces - make edges and sweep them into faces.
3871 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3872 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3873 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) {
3874 const SMDS_MeshElement* elem = itElem->first;
3875 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3877 if(itElem->second.size()==0) continue;
3879 if ( elem->GetType() == SMDSAbs_Edge ) {
3880 // create a ceiling edge
3881 if (!elem->IsQuadratic()) {
3882 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3883 vecNewNodes[ 1 ]->second.back())) {
3884 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3885 vecNewNodes[ 1 ]->second.back()));
3886 srcElements.Append( myLastCreatedElems.Last() );
3890 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3891 vecNewNodes[ 1 ]->second.back(),
3892 vecNewNodes[ 2 ]->second.back())) {
3893 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3894 vecNewNodes[ 1 ]->second.back(),
3895 vecNewNodes[ 2 ]->second.back()));
3896 srcElements.Append( myLastCreatedElems.Last() );
3900 if ( elem->GetType() != SMDSAbs_Face )
3903 bool hasFreeLinks = false;
3905 TIDSortedElemSet avoidSet;
3906 avoidSet.insert( elem );
3908 set<const SMDS_MeshNode*> aFaceLastNodes;
3909 int iNode, nbNodes = vecNewNodes.size();
3910 if(!elem->IsQuadratic()) {
3911 // loop on the face nodes
3912 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3913 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3914 // look for free links of the face
3915 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
3916 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3917 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3918 // check if a link is free
3919 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3920 hasFreeLinks = true;
3921 // make an edge and a ceiling for a new edge
3922 if ( !aMesh->FindEdge( n1, n2 )) {
3923 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
3924 srcElements.Append( myLastCreatedElems.Last() );
3926 n1 = vecNewNodes[ iNode ]->second.back();
3927 n2 = vecNewNodes[ iNext ]->second.back();
3928 if ( !aMesh->FindEdge( n1, n2 )) {
3929 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
3930 srcElements.Append( myLastCreatedElems.Last() );
3935 else { // elem is quadratic face
3936 int nbn = nbNodes/2;
3937 for ( iNode = 0; iNode < nbn; iNode++ ) {
3938 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3939 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
3940 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
3941 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
3942 // check if a link is free
3943 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
3944 hasFreeLinks = true;
3945 // make an edge and a ceiling for a new edge
3947 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
3948 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3949 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
3950 srcElements.Append( myLastCreatedElems.Last() );
3952 n1 = vecNewNodes[ iNode ]->second.back();
3953 n2 = vecNewNodes[ iNext ]->second.back();
3954 n3 = vecNewNodes[ iNode+nbn ]->second.back();
3955 if ( !aMesh->FindEdge( n1, n2, n3 )) {
3956 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
3957 srcElements.Append( myLastCreatedElems.Last() );
3961 for ( iNode = nbn; iNode < 2*nbn; iNode++ ) {
3962 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
3966 // sweep free links into faces
3968 if ( hasFreeLinks ) {
3969 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
3970 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
3972 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
3973 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3974 initNodeSet.insert( vecNewNodes[ iNode ]->first );
3975 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
3977 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
3978 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
3980 while ( iVol++ < volNb ) v++;
3981 // find indices of free faces of a volume and their source edges
3982 list< int > freeInd;
3983 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
3984 SMDS_VolumeTool vTool( *v );
3985 int iF, nbF = vTool.NbFaces();
3986 for ( iF = 0; iF < nbF; iF ++ ) {
3987 if (vTool.IsFreeFace( iF ) &&
3988 vTool.GetFaceNodes( iF, faceNodeSet ) &&
3989 initNodeSet != faceNodeSet) // except an initial face
3991 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
3993 freeInd.push_back( iF );
3994 // find source edge of a free face iF
3995 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
3996 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
3997 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
3998 initNodeSet.begin(), initNodeSet.end(),
3999 commonNodes.begin());
4000 if ( (*v)->IsQuadratic() )
4001 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4003 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4005 if ( !srcEdges.back() )
4007 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4008 << iF << " of volume #" << vTool.ID() << endl;
4013 if ( freeInd.empty() )
4016 // create faces for all steps;
4017 // if such a face has been already created by sweep of edge,
4018 // assure that its orientation is OK
4019 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4021 vTool.SetExternalNormal();
4022 const int nextShift = vTool.IsForward() ? +1 : -1;
4023 list< int >::iterator ind = freeInd.begin();
4024 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4025 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4027 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4028 int nbn = vTool.NbFaceNodes( *ind );
4029 if ( ! (*v)->IsPoly() )
4031 case 3: { ///// triangle
4032 const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4034 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4036 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4038 nodes[ 1 + nextShift ] };
4040 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4042 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4047 case 4: { ///// quadrangle
4048 const SMDS_MeshFace * f =
4049 aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4051 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4053 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4054 nodes[ 2 ], nodes[ 2+nextShift ] };
4056 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4058 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4059 newOrder[ 2 ], newOrder[ 3 ]));
4063 case 6: { /////// quadratic triangle
4064 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4],
4065 nodes[1], nodes[3], nodes[5] );
4067 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4069 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4071 nodes[2 + 2*nextShift],
4072 nodes[3 - 2*nextShift],
4074 nodes[3 + 2*nextShift]};
4076 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4078 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4087 default: /////// quadratic quadrangle
4088 const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4089 nodes[1], nodes[3], nodes[5], nodes[7] );
4091 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4093 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4094 nodes[4 - 2*nextShift],
4096 nodes[4 + 2*nextShift],
4098 nodes[5 - 2*nextShift],
4100 nodes[5 + 2*nextShift] };
4102 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4104 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4105 newOrder[ 2 ], newOrder[ 3 ],
4106 newOrder[ 4 ], newOrder[ 5 ],
4107 newOrder[ 6 ], newOrder[ 7 ]));
4111 else { //////// polygon
4113 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
4114 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4116 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4118 if ( !vTool.IsForward() )
4119 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4121 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4123 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4127 while ( srcElements.Length() < myLastCreatedElems.Length() )
4128 srcElements.Append( *srcEdge );
4130 } // loop on free faces
4132 // go to the next volume
4134 while ( iVol++ < nbVolumesByStep ) v++;
4137 } // loop on volumes of one step
4138 } // sweep free links into faces
4140 // Make a ceiling face with a normal external to a volume
4142 SMDS_VolumeTool lastVol( itElem->second.back() );
4144 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4146 lastVol.SetExternalNormal();
4147 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4148 int nbn = lastVol.NbFaceNodes( iF );
4151 if (!hasFreeLinks ||
4152 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4153 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4156 if (!hasFreeLinks ||
4157 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4158 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] ));
4161 if(itElem->second.back()->IsQuadratic()) {
4163 if (!hasFreeLinks ||
4164 !aMesh->FindFace(nodes[0], nodes[2], nodes[4],
4165 nodes[1], nodes[3], nodes[5]) ) {
4166 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4167 nodes[1], nodes[3], nodes[5]));
4171 if (!hasFreeLinks ||
4172 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4173 nodes[1], nodes[3], nodes[5], nodes[7]) )
4174 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4175 nodes[1], nodes[3], nodes[5], nodes[7]));
4179 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, &nodes[nbn] );
4180 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4181 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4185 while ( srcElements.Length() < myLastCreatedElems.Length() )
4186 srcElements.Append( myLastCreatedElems.Last() );
4188 } // loop on swept elements
4191 //=======================================================================
4192 //function : RotationSweep
4194 //=======================================================================
4196 SMESH_MeshEditor::PGroupIDs
4197 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4198 const gp_Ax1& theAxis,
4199 const double theAngle,
4200 const int theNbSteps,
4201 const double theTol,
4202 const bool theMakeGroups,
4203 const bool theMakeWalls)
4205 myLastCreatedElems.Clear();
4206 myLastCreatedNodes.Clear();
4208 // source elements for each generated one
4209 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4211 MESSAGE( "RotationSweep()");
4213 aTrsf.SetRotation( theAxis, theAngle );
4215 aTrsf2.SetRotation( theAxis, theAngle/2. );
4217 gp_Lin aLine( theAxis );
4218 double aSqTol = theTol * theTol;
4220 SMESHDS_Mesh* aMesh = GetMeshDS();
4222 TNodeOfNodeListMap mapNewNodes;
4223 TElemOfVecOfNnlmiMap mapElemNewNodes;
4224 TElemOfElemListMap newElemsMap;
4227 TIDSortedElemSet::iterator itElem;
4228 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4229 const SMDS_MeshElement* elem = *itElem;
4230 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4232 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4233 newNodesItVec.reserve( elem->NbNodes() );
4235 // loop on elem nodes
4236 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4237 while ( itN->more() ) {
4238 // check if a node has been already sweeped
4239 const SMDS_MeshNode* node = cast2Node( itN->next() );
4241 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4243 aXYZ.Coord( coord[0], coord[1], coord[2] );
4244 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4246 TNodeOfNodeListMapItr nIt = mapNewNodes.find( node );
4247 if ( nIt == mapNewNodes.end() ) {
4248 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4249 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4252 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4254 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4255 //bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4256 const SMDS_MeshNode * newNode = node;
4257 for ( int i = 0; i < theNbSteps; i++ ) {
4259 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4261 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4262 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4263 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4264 myLastCreatedNodes.Append(newNode);
4265 srcNodes.Append( node );
4266 listNewNodes.push_back( newNode );
4267 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4268 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4271 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4273 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4274 myLastCreatedNodes.Append(newNode);
4275 srcNodes.Append( node );
4276 listNewNodes.push_back( newNode );
4279 listNewNodes.push_back( newNode );
4280 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4281 listNewNodes.push_back( newNode );
4288 // if current elem is quadratic and current node is not medium
4289 // we have to check - may be it is needed to insert additional nodes
4290 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4291 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4292 if(listNewNodes.size()==theNbSteps) {
4293 listNewNodes.clear();
4295 //gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4297 //aXYZ.Coord( coord[0], coord[1], coord[2] );
4298 const SMDS_MeshNode * newNode = node;
4300 for(int i = 0; i<theNbSteps; i++) {
4301 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4302 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4303 cout<<" 3 AddNode: "<<newNode;
4304 myLastCreatedNodes.Append(newNode);
4305 listNewNodes.push_back( newNode );
4306 srcNodes.Append( node );
4307 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4308 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4309 cout<<" 4 AddNode: "<<newNode;
4310 myLastCreatedNodes.Append(newNode);
4311 srcNodes.Append( node );
4312 listNewNodes.push_back( newNode );
4316 listNewNodes.push_back( newNode );
4322 newNodesItVec.push_back( nIt );
4324 // make new elements
4325 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4329 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4331 PGroupIDs newGroupIDs;
4332 if ( theMakeGroups )
4333 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4339 //=======================================================================
4340 //function : CreateNode
4342 //=======================================================================
4343 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4346 const double tolnode,
4347 SMESH_SequenceOfNode& aNodes)
4349 myLastCreatedElems.Clear();
4350 myLastCreatedNodes.Clear();
4353 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4355 // try to search in sequence of existing nodes
4356 // if aNodes.Length()>0 we 'nave to use given sequence
4357 // else - use all nodes of mesh
4358 if(aNodes.Length()>0) {
4360 for(i=1; i<=aNodes.Length(); i++) {
4361 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4362 if(P1.Distance(P2)<tolnode)
4363 return aNodes.Value(i);
4367 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4368 while(itn->more()) {
4369 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4370 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4371 if(P1.Distance(P2)<tolnode)
4376 // create new node and return it
4377 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4378 myLastCreatedNodes.Append(NewNode);
4383 //=======================================================================
4384 //function : ExtrusionSweep
4386 //=======================================================================
4388 SMESH_MeshEditor::PGroupIDs
4389 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4390 const gp_Vec& theStep,
4391 const int theNbSteps,
4392 TElemOfElemListMap& newElemsMap,
4393 const bool theMakeGroups,
4395 const double theTolerance)
4397 ExtrusParam aParams;
4398 aParams.myDir = gp_Dir(theStep);
4399 aParams.myNodes.Clear();
4400 aParams.mySteps = new TColStd_HSequenceOfReal;
4402 for(i=1; i<=theNbSteps; i++)
4403 aParams.mySteps->Append(theStep.Magnitude());
4406 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4410 //=======================================================================
4411 //function : ExtrusionSweep
4413 //=======================================================================
4415 SMESH_MeshEditor::PGroupIDs
4416 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4417 ExtrusParam& theParams,
4418 TElemOfElemListMap& newElemsMap,
4419 const bool theMakeGroups,
4421 const double theTolerance)
4423 MESSAGE("ExtrusionSweep " << theMakeGroups << " " << theFlags << " " << theTolerance);
4424 myLastCreatedElems.Clear();
4425 myLastCreatedNodes.Clear();
4427 // source elements for each generated one
4428 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4430 SMESHDS_Mesh* aMesh = GetMeshDS();
4432 int nbsteps = theParams.mySteps->Length();
4434 TNodeOfNodeListMap mapNewNodes;
4435 //TNodeOfNodeVecMap mapNewNodes;
4436 TElemOfVecOfNnlmiMap mapElemNewNodes;
4437 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4440 TIDSortedElemSet::iterator itElem;
4441 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4442 // check element type
4443 const SMDS_MeshElement* elem = *itElem;
4444 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4447 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4448 //vector<TNodeOfNodeVecMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4449 newNodesItVec.reserve( elem->NbNodes() );
4451 // loop on elem nodes
4452 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4453 while ( itN->more() )
4455 // check if a node has been already sweeped
4456 const SMDS_MeshNode* node = cast2Node( itN->next() );
4457 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
4458 //TNodeOfNodeVecMap::iterator nIt = mapNewNodes.find( node );
4459 if ( nIt == mapNewNodes.end() ) {
4460 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4461 //nIt = mapNewNodes.insert( make_pair( node, vector<const SMDS_MeshNode*>() )).first;
4462 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4463 //vector<const SMDS_MeshNode*>& vecNewNodes = nIt->second;
4464 //vecNewNodes.reserve(nbsteps);
4467 double coord[] = { node->X(), node->Y(), node->Z() };
4468 //int nbsteps = theParams.mySteps->Length();
4469 for ( int i = 0; i < nbsteps; i++ ) {
4470 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4471 // create additional node
4472 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4473 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4474 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4475 if( theFlags & EXTRUSION_FLAG_SEW ) {
4476 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4477 theTolerance, theParams.myNodes);
4478 listNewNodes.push_back( newNode );
4481 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4482 myLastCreatedNodes.Append(newNode);
4483 srcNodes.Append( node );
4484 listNewNodes.push_back( newNode );
4487 //aTrsf.Transforms( coord[0], coord[1], coord[2] );
4488 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4489 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4490 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4491 if( theFlags & EXTRUSION_FLAG_SEW ) {
4492 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4493 theTolerance, theParams.myNodes);
4494 listNewNodes.push_back( newNode );
4495 //vecNewNodes[i]=newNode;
4498 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4499 myLastCreatedNodes.Append(newNode);
4500 srcNodes.Append( node );
4501 listNewNodes.push_back( newNode );
4502 //vecNewNodes[i]=newNode;
4507 // if current elem is quadratic and current node is not medium
4508 // we have to check - may be it is needed to insert additional nodes
4509 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
4510 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
4511 if(listNewNodes.size()==nbsteps) {
4512 listNewNodes.clear();
4513 double coord[] = { node->X(), node->Y(), node->Z() };
4514 for ( int i = 0; i < nbsteps; i++ ) {
4515 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4516 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4517 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4518 if( theFlags & EXTRUSION_FLAG_SEW ) {
4519 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4520 theTolerance, theParams.myNodes);
4521 listNewNodes.push_back( newNode );
4524 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4525 myLastCreatedNodes.Append(newNode);
4526 srcNodes.Append( node );
4527 listNewNodes.push_back( newNode );
4529 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4530 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4531 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4532 if( theFlags & EXTRUSION_FLAG_SEW ) {
4533 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4534 theTolerance, theParams.myNodes);
4535 listNewNodes.push_back( newNode );
4538 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4539 myLastCreatedNodes.Append(newNode);
4540 srcNodes.Append( node );
4541 listNewNodes.push_back( newNode );
4547 newNodesItVec.push_back( nIt );
4549 // make new elements
4550 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4553 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4554 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4556 PGroupIDs newGroupIDs;
4557 if ( theMakeGroups )
4558 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4564 //=======================================================================
4565 //class : SMESH_MeshEditor_PathPoint
4566 //purpose : auxiliary class
4567 //=======================================================================
4568 class SMESH_MeshEditor_PathPoint {
4570 SMESH_MeshEditor_PathPoint() {
4571 myPnt.SetCoord(99., 99., 99.);
4572 myTgt.SetCoord(1.,0.,0.);
4576 void SetPnt(const gp_Pnt& aP3D){
4579 void SetTangent(const gp_Dir& aTgt){
4582 void SetAngle(const double& aBeta){
4585 void SetParameter(const double& aPrm){
4588 const gp_Pnt& Pnt()const{
4591 const gp_Dir& Tangent()const{
4594 double Angle()const{
4597 double Parameter()const{
4609 //=======================================================================
4610 //function : ExtrusionAlongTrack
4612 //=======================================================================
4613 SMESH_MeshEditor::Extrusion_Error
4614 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4615 SMESH_subMesh* theTrack,
4616 const SMDS_MeshNode* theN1,
4617 const bool theHasAngles,
4618 list<double>& theAngles,
4619 const bool theLinearVariation,
4620 const bool theHasRefPoint,
4621 const gp_Pnt& theRefPoint,
4622 const bool theMakeGroups)
4624 MESSAGE("ExtrusionAlongTrack");
4625 myLastCreatedElems.Clear();
4626 myLastCreatedNodes.Clear();
4629 std::list<double> aPrms;
4630 TIDSortedElemSet::iterator itElem;
4633 TopoDS_Edge aTrackEdge;
4634 TopoDS_Vertex aV1, aV2;
4636 SMDS_ElemIteratorPtr aItE;
4637 SMDS_NodeIteratorPtr aItN;
4638 SMDSAbs_ElementType aTypeE;
4640 TNodeOfNodeListMap mapNewNodes;
4643 aNbE = theElements.size();
4646 return EXTR_NO_ELEMENTS;
4648 // 1.1 Track Pattern
4651 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4653 aItE = pSubMeshDS->GetElements();
4654 while ( aItE->more() ) {
4655 const SMDS_MeshElement* pE = aItE->next();
4656 aTypeE = pE->GetType();
4657 // Pattern must contain links only
4658 if ( aTypeE != SMDSAbs_Edge )
4659 return EXTR_PATH_NOT_EDGE;
4662 list<SMESH_MeshEditor_PathPoint> fullList;
4664 const TopoDS_Shape& aS = theTrack->GetSubShape();
4665 // Sub shape for the Pattern must be an Edge or Wire
4666 if( aS.ShapeType() == TopAbs_EDGE ) {
4667 aTrackEdge = TopoDS::Edge( aS );
4668 // the Edge must not be degenerated
4669 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4670 return EXTR_BAD_PATH_SHAPE;
4671 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4672 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4673 const SMDS_MeshNode* aN1 = aItN->next();
4674 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4675 const SMDS_MeshNode* aN2 = aItN->next();
4676 // starting node must be aN1 or aN2
4677 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4678 return EXTR_BAD_STARTING_NODE;
4679 aItN = pSubMeshDS->GetNodes();
4680 while ( aItN->more() ) {
4681 const SMDS_MeshNode* pNode = aItN->next();
4682 const SMDS_EdgePosition* pEPos =
4683 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4684 double aT = pEPos->GetUParameter();
4685 aPrms.push_back( aT );
4687 //Extrusion_Error err =
4688 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4690 else if( aS.ShapeType() == TopAbs_WIRE ) {
4691 list< SMESH_subMesh* > LSM;
4692 TopTools_SequenceOfShape Edges;
4693 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4694 while(itSM->more()) {
4695 SMESH_subMesh* SM = itSM->next();
4697 const TopoDS_Shape& aS = SM->GetSubShape();
4700 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4701 int startNid = theN1->GetID();
4702 TColStd_MapOfInteger UsedNums;
4703 int NbEdges = Edges.Length();
4705 for(; i<=NbEdges; i++) {
4707 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4708 for(; itLSM!=LSM.end(); itLSM++) {
4710 if(UsedNums.Contains(k)) continue;
4711 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4712 SMESH_subMesh* locTrack = *itLSM;
4713 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4714 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4715 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4716 const SMDS_MeshNode* aN1 = aItN->next();
4717 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4718 const SMDS_MeshNode* aN2 = aItN->next();
4719 // starting node must be aN1 or aN2
4720 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4721 // 2. Collect parameters on the track edge
4723 aItN = locMeshDS->GetNodes();
4724 while ( aItN->more() ) {
4725 const SMDS_MeshNode* pNode = aItN->next();
4726 const SMDS_EdgePosition* pEPos =
4727 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4728 double aT = pEPos->GetUParameter();
4729 aPrms.push_back( aT );
4731 list<SMESH_MeshEditor_PathPoint> LPP;
4732 //Extrusion_Error err =
4733 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4734 LLPPs.push_back(LPP);
4736 // update startN for search following egde
4737 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4738 else startNid = aN1->GetID();
4742 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4743 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4744 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4745 for(; itPP!=firstList.end(); itPP++) {
4746 fullList.push_back( *itPP );
4748 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4749 fullList.pop_back();
4751 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4752 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4753 itPP = currList.begin();
4754 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4755 gp_Dir D1 = PP1.Tangent();
4756 gp_Dir D2 = PP2.Tangent();
4757 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4758 (D1.Z()+D2.Z())/2 ) );
4759 PP1.SetTangent(Dnew);
4760 fullList.push_back(PP1);
4762 for(; itPP!=firstList.end(); itPP++) {
4763 fullList.push_back( *itPP );
4765 PP1 = fullList.back();
4766 fullList.pop_back();
4768 // if wire not closed
4769 fullList.push_back(PP1);
4773 return EXTR_BAD_PATH_SHAPE;
4776 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4777 theHasRefPoint, theRefPoint, theMakeGroups);
4781 //=======================================================================
4782 //function : ExtrusionAlongTrack
4784 //=======================================================================
4785 SMESH_MeshEditor::Extrusion_Error
4786 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4787 SMESH_Mesh* theTrack,
4788 const SMDS_MeshNode* theN1,
4789 const bool theHasAngles,
4790 list<double>& theAngles,
4791 const bool theLinearVariation,
4792 const bool theHasRefPoint,
4793 const gp_Pnt& theRefPoint,
4794 const bool theMakeGroups)
4796 myLastCreatedElems.Clear();
4797 myLastCreatedNodes.Clear();
4800 std::list<double> aPrms;
4801 TIDSortedElemSet::iterator itElem;
4804 TopoDS_Edge aTrackEdge;
4805 TopoDS_Vertex aV1, aV2;
4807 SMDS_ElemIteratorPtr aItE;
4808 SMDS_NodeIteratorPtr aItN;
4809 SMDSAbs_ElementType aTypeE;
4811 TNodeOfNodeListMap mapNewNodes;
4814 aNbE = theElements.size();
4817 return EXTR_NO_ELEMENTS;
4819 // 1.1 Track Pattern
4822 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4824 aItE = pMeshDS->elementsIterator();
4825 while ( aItE->more() ) {
4826 const SMDS_MeshElement* pE = aItE->next();
4827 aTypeE = pE->GetType();
4828 // Pattern must contain links only
4829 if ( aTypeE != SMDSAbs_Edge )
4830 return EXTR_PATH_NOT_EDGE;
4833 list<SMESH_MeshEditor_PathPoint> fullList;
4835 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4836 // Sub shape for the Pattern must be an Edge or Wire
4837 if( aS.ShapeType() == TopAbs_EDGE ) {
4838 aTrackEdge = TopoDS::Edge( aS );
4839 // the Edge must not be degenerated
4840 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4841 return EXTR_BAD_PATH_SHAPE;
4842 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4843 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4844 const SMDS_MeshNode* aN1 = aItN->next();
4845 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4846 const SMDS_MeshNode* aN2 = aItN->next();
4847 // starting node must be aN1 or aN2
4848 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4849 return EXTR_BAD_STARTING_NODE;
4850 aItN = pMeshDS->nodesIterator();
4851 while ( aItN->more() ) {
4852 const SMDS_MeshNode* pNode = aItN->next();
4853 if( pNode==aN1 || pNode==aN2 ) continue;
4854 const SMDS_EdgePosition* pEPos =
4855 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4856 double aT = pEPos->GetUParameter();
4857 aPrms.push_back( aT );
4859 //Extrusion_Error err =
4860 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4862 else if( aS.ShapeType() == TopAbs_WIRE ) {
4863 list< SMESH_subMesh* > LSM;
4864 TopTools_SequenceOfShape Edges;
4865 TopExp_Explorer eExp(aS, TopAbs_EDGE);
4866 for(; eExp.More(); eExp.Next()) {
4867 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
4868 if( BRep_Tool::Degenerated(E) ) continue;
4869 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
4875 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4876 int startNid = theN1->GetID();
4877 TColStd_MapOfInteger UsedNums;
4878 int NbEdges = Edges.Length();
4880 for(; i<=NbEdges; i++) {
4882 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4883 for(; itLSM!=LSM.end(); itLSM++) {
4885 if(UsedNums.Contains(k)) continue;
4886 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4887 SMESH_subMesh* locTrack = *itLSM;
4888 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4889 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4890 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4891 const SMDS_MeshNode* aN1 = aItN->next();
4892 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4893 const SMDS_MeshNode* aN2 = aItN->next();
4894 // starting node must be aN1 or aN2
4895 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4896 // 2. Collect parameters on the track edge
4898 aItN = locMeshDS->GetNodes();
4899 while ( aItN->more() ) {
4900 const SMDS_MeshNode* pNode = aItN->next();
4901 const SMDS_EdgePosition* pEPos =
4902 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4903 double aT = pEPos->GetUParameter();
4904 aPrms.push_back( aT );
4906 list<SMESH_MeshEditor_PathPoint> LPP;
4907 //Extrusion_Error err =
4908 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4909 LLPPs.push_back(LPP);
4911 // update startN for search following egde
4912 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4913 else startNid = aN1->GetID();
4917 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4918 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4919 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4920 for(; itPP!=firstList.end(); itPP++) {
4921 fullList.push_back( *itPP );
4923 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4924 fullList.pop_back();
4926 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4927 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4928 itPP = currList.begin();
4929 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4930 gp_Dir D1 = PP1.Tangent();
4931 gp_Dir D2 = PP2.Tangent();
4932 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4933 (D1.Z()+D2.Z())/2 ) );
4934 PP1.SetTangent(Dnew);
4935 fullList.push_back(PP1);
4937 for(; itPP!=currList.end(); itPP++) {
4938 fullList.push_back( *itPP );
4940 PP1 = fullList.back();
4941 fullList.pop_back();
4943 // if wire not closed
4944 fullList.push_back(PP1);
4948 return EXTR_BAD_PATH_SHAPE;
4951 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4952 theHasRefPoint, theRefPoint, theMakeGroups);
4956 //=======================================================================
4957 //function : MakeEdgePathPoints
4958 //purpose : auxilary for ExtrusionAlongTrack
4959 //=======================================================================
4960 SMESH_MeshEditor::Extrusion_Error
4961 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
4962 const TopoDS_Edge& aTrackEdge,
4964 list<SMESH_MeshEditor_PathPoint>& LPP)
4966 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
4968 aTolVec2=aTolVec*aTolVec;
4970 TopoDS_Vertex aV1, aV2;
4971 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4972 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
4973 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
4974 // 2. Collect parameters on the track edge
4975 aPrms.push_front( aT1 );
4976 aPrms.push_back( aT2 );
4979 if( FirstIsStart ) {
4990 SMESH_MeshEditor_PathPoint aPP;
4991 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
4992 std::list<double>::iterator aItD = aPrms.begin();
4993 for(; aItD != aPrms.end(); ++aItD) {
4997 aC3D->D1( aT, aP3D, aVec );
4998 aL2 = aVec.SquareMagnitude();
4999 if ( aL2 < aTolVec2 )
5000 return EXTR_CANT_GET_TANGENT;
5001 gp_Dir aTgt( aVec );
5003 aPP.SetTangent( aTgt );
5004 aPP.SetParameter( aT );
5011 //=======================================================================
5012 //function : MakeExtrElements
5013 //purpose : auxilary for ExtrusionAlongTrack
5014 //=======================================================================
5015 SMESH_MeshEditor::Extrusion_Error
5016 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5017 list<SMESH_MeshEditor_PathPoint>& fullList,
5018 const bool theHasAngles,
5019 list<double>& theAngles,
5020 const bool theLinearVariation,
5021 const bool theHasRefPoint,
5022 const gp_Pnt& theRefPoint,
5023 const bool theMakeGroups)
5025 MESSAGE("MakeExtrElements");
5026 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5027 int aNbTP = fullList.size();
5028 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5030 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5031 LinearAngleVariation(aNbTP-1, theAngles);
5033 vector<double> aAngles( aNbTP );
5035 for(; j<aNbTP; ++j) {
5038 if ( theHasAngles ) {
5040 std::list<double>::iterator aItD = theAngles.begin();
5041 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5043 aAngles[j] = anAngle;
5046 // fill vector of path points with angles
5047 //aPPs.resize(fullList.size());
5049 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5050 for(; itPP!=fullList.end(); itPP++) {
5052 SMESH_MeshEditor_PathPoint PP = *itPP;
5053 PP.SetAngle(aAngles[j]);
5057 TNodeOfNodeListMap mapNewNodes;
5058 TElemOfVecOfNnlmiMap mapElemNewNodes;
5059 TElemOfElemListMap newElemsMap;
5060 TIDSortedElemSet::iterator itElem;
5063 SMDSAbs_ElementType aTypeE;
5064 // source elements for each generated one
5065 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5067 // 3. Center of rotation aV0
5068 gp_Pnt aV0 = theRefPoint;
5070 if ( !theHasRefPoint ) {
5072 aGC.SetCoord( 0.,0.,0. );
5074 itElem = theElements.begin();
5075 for ( ; itElem != theElements.end(); itElem++ ) {
5076 const SMDS_MeshElement* elem = *itElem;
5078 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5079 while ( itN->more() ) {
5080 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5085 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5086 list<const SMDS_MeshNode*> aLNx;
5087 mapNewNodes[node] = aLNx;
5089 gp_XYZ aXYZ( aX, aY, aZ );
5097 } // if (!theHasRefPoint) {
5098 mapNewNodes.clear();
5100 // 4. Processing the elements
5101 SMESHDS_Mesh* aMesh = GetMeshDS();
5103 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5104 // check element type
5105 const SMDS_MeshElement* elem = *itElem;
5106 aTypeE = elem->GetType();
5107 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5110 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5111 newNodesItVec.reserve( elem->NbNodes() );
5113 // loop on elem nodes
5115 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5116 while ( itN->more() )
5119 // check if a node has been already processed
5120 const SMDS_MeshNode* node =
5121 static_cast<const SMDS_MeshNode*>( itN->next() );
5122 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5123 if ( nIt == mapNewNodes.end() ) {
5124 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5125 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5128 aX = node->X(); aY = node->Y(); aZ = node->Z();
5130 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5131 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5132 gp_Ax1 anAx1, anAxT1T0;
5133 gp_Dir aDT1x, aDT0x, aDT1T0;
5138 aPN0.SetCoord(aX, aY, aZ);
5140 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5142 aDT0x= aPP0.Tangent();
5143 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5145 for ( j = 1; j < aNbTP; ++j ) {
5146 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5148 aDT1x = aPP1.Tangent();
5149 aAngle1x = aPP1.Angle();
5151 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5153 gp_Vec aV01x( aP0x, aP1x );
5154 aTrsf.SetTranslation( aV01x );
5157 aV1x = aV0x.Transformed( aTrsf );
5158 aPN1 = aPN0.Transformed( aTrsf );
5160 // rotation 1 [ T1,T0 ]
5161 aAngleT1T0=-aDT1x.Angle( aDT0x );
5162 if (fabs(aAngleT1T0) > aTolAng) {
5164 anAxT1T0.SetLocation( aV1x );
5165 anAxT1T0.SetDirection( aDT1T0 );
5166 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5168 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5172 if ( theHasAngles ) {
5173 anAx1.SetLocation( aV1x );
5174 anAx1.SetDirection( aDT1x );
5175 aTrsfRot.SetRotation( anAx1, aAngle1x );
5177 aPN1 = aPN1.Transformed( aTrsfRot );
5181 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5182 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5183 // create additional node
5184 double x = ( aPN1.X() + aPN0.X() )/2.;
5185 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5186 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5187 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5188 myLastCreatedNodes.Append(newNode);
5189 srcNodes.Append( node );
5190 listNewNodes.push_back( newNode );
5195 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5196 myLastCreatedNodes.Append(newNode);
5197 srcNodes.Append( node );
5198 listNewNodes.push_back( newNode );
5208 // if current elem is quadratic and current node is not medium
5209 // we have to check - may be it is needed to insert additional nodes
5210 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5211 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5212 if(listNewNodes.size()==aNbTP-1) {
5213 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5214 gp_XYZ P(node->X(), node->Y(), node->Z());
5215 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5217 for(i=0; i<aNbTP-1; i++) {
5218 const SMDS_MeshNode* N = *it;
5219 double x = ( N->X() + P.X() )/2.;
5220 double y = ( N->Y() + P.Y() )/2.;
5221 double z = ( N->Z() + P.Z() )/2.;
5222 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5223 srcNodes.Append( node );
5224 myLastCreatedNodes.Append(newN);
5227 P = gp_XYZ(N->X(),N->Y(),N->Z());
5229 listNewNodes.clear();
5230 for(i=0; i<2*(aNbTP-1); i++) {
5231 listNewNodes.push_back(aNodes[i]);
5237 newNodesItVec.push_back( nIt );
5239 // make new elements
5240 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5241 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5242 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5245 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5247 if ( theMakeGroups )
5248 generateGroups( srcNodes, srcElems, "extruded");
5254 //=======================================================================
5255 //function : LinearAngleVariation
5256 //purpose : auxilary for ExtrusionAlongTrack
5257 //=======================================================================
5258 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5259 list<double>& Angles)
5261 int nbAngles = Angles.size();
5262 if( nbSteps > nbAngles ) {
5263 vector<double> theAngles(nbAngles);
5264 list<double>::iterator it = Angles.begin();
5266 for(; it!=Angles.end(); it++) {
5268 theAngles[i] = (*it);
5271 double rAn2St = double( nbAngles ) / double( nbSteps );
5272 double angPrev = 0, angle;
5273 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5274 double angCur = rAn2St * ( iSt+1 );
5275 double angCurFloor = floor( angCur );
5276 double angPrevFloor = floor( angPrev );
5277 if ( angPrevFloor == angCurFloor )
5278 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5280 int iP = int( angPrevFloor );
5281 double angPrevCeil = ceil(angPrev);
5282 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5284 int iC = int( angCurFloor );
5285 if ( iC < nbAngles )
5286 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5288 iP = int( angPrevCeil );
5290 angle += theAngles[ iC ];
5292 res.push_back(angle);
5297 for(; it!=res.end(); it++)
5298 Angles.push_back( *it );
5303 //================================================================================
5305 * \brief Move or copy theElements applying theTrsf to their nodes
5306 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5307 * \param theTrsf - transformation to apply
5308 * \param theCopy - if true, create translated copies of theElems
5309 * \param theMakeGroups - if true and theCopy, create translated groups
5310 * \param theTargetMesh - mesh to copy translated elements into
5311 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5313 //================================================================================
5315 SMESH_MeshEditor::PGroupIDs
5316 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5317 const gp_Trsf& theTrsf,
5319 const bool theMakeGroups,
5320 SMESH_Mesh* theTargetMesh)
5322 myLastCreatedElems.Clear();
5323 myLastCreatedNodes.Clear();
5325 bool needReverse = false;
5326 string groupPostfix;
5327 switch ( theTrsf.Form() ) {
5329 MESSAGE("gp_PntMirror");
5331 groupPostfix = "mirrored";
5334 MESSAGE("gp_Ax1Mirror");
5335 groupPostfix = "mirrored";
5338 MESSAGE("gp_Ax2Mirror");
5340 groupPostfix = "mirrored";
5343 MESSAGE("gp_Rotation");
5344 groupPostfix = "rotated";
5346 case gp_Translation:
5347 MESSAGE("gp_Translation");
5348 groupPostfix = "translated";
5351 MESSAGE("gp_Scale");
5352 groupPostfix = "scaled";
5354 case gp_CompoundTrsf: // different scale by axis
5355 MESSAGE("gp_CompoundTrsf");
5356 groupPostfix = "scaled";
5360 needReverse = false;
5361 groupPostfix = "transformed";
5364 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5365 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5366 SMESHDS_Mesh* aMesh = GetMeshDS();
5369 // map old node to new one
5370 TNodeNodeMap nodeMap;
5372 // elements sharing moved nodes; those of them which have all
5373 // nodes mirrored but are not in theElems are to be reversed
5374 TIDSortedElemSet inverseElemSet;
5376 // source elements for each generated one
5377 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5379 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5380 TIDSortedElemSet orphanNode;
5382 if ( theElems.empty() ) // transform the whole mesh
5385 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5386 while ( eIt->more() ) theElems.insert( eIt->next() );
5388 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5389 while ( nIt->more() )
5391 const SMDS_MeshNode* node = nIt->next();
5392 if ( node->NbInverseElements() == 0)
5393 orphanNode.insert( node );
5397 // loop on elements to transform nodes : first orphan nodes then elems
5398 TIDSortedElemSet::iterator itElem;
5399 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5400 for (int i=0; i<2; i++)
5401 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5402 const SMDS_MeshElement* elem = *itElem;
5406 // loop on elem nodes
5407 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5408 while ( itN->more() ) {
5410 const SMDS_MeshNode* node = cast2Node( itN->next() );
5411 // check if a node has been already transformed
5412 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5413 nodeMap.insert( make_pair ( node, node ));
5414 if ( !n2n_isnew.second )
5418 coord[0] = node->X();
5419 coord[1] = node->Y();
5420 coord[2] = node->Z();
5421 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5422 if ( theTargetMesh ) {
5423 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5424 n2n_isnew.first->second = newNode;
5425 myLastCreatedNodes.Append(newNode);
5426 srcNodes.Append( node );
5428 else if ( theCopy ) {
5429 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5430 n2n_isnew.first->second = newNode;
5431 myLastCreatedNodes.Append(newNode);
5432 srcNodes.Append( node );
5435 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5436 // node position on shape becomes invalid
5437 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5438 ( SMDS_SpacePosition::originSpacePosition() );
5441 // keep inverse elements
5442 if ( !theCopy && !theTargetMesh && needReverse ) {
5443 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5444 while ( invElemIt->more() ) {
5445 const SMDS_MeshElement* iel = invElemIt->next();
5446 inverseElemSet.insert( iel );
5452 // either create new elements or reverse mirrored ones
5453 if ( !theCopy && !needReverse && !theTargetMesh )
5456 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5457 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5458 theElems.insert( *invElemIt );
5460 // replicate or reverse elements
5461 // TODO revoir ordre reverse vtk
5463 REV_TETRA = 0, // = nbNodes - 4
5464 REV_PYRAMID = 1, // = nbNodes - 4
5465 REV_PENTA = 2, // = nbNodes - 4
5467 REV_HEXA = 4, // = nbNodes - 4
5471 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA
5472 { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID
5473 { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA
5474 { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE
5475 { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA
5476 { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD
5479 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5481 const SMDS_MeshElement* elem = *itElem;
5482 if ( !elem || elem->GetType() == SMDSAbs_Node )
5485 int nbNodes = elem->NbNodes();
5486 int elemType = elem->GetType();
5488 if (elem->IsPoly()) {
5489 // Polygon or Polyhedral Volume
5490 switch ( elemType ) {
5493 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5495 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5496 while (itN->more()) {
5497 const SMDS_MeshNode* node =
5498 static_cast<const SMDS_MeshNode*>(itN->next());
5499 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5500 if (nodeMapIt == nodeMap.end())
5501 break; // not all nodes transformed
5503 // reverse mirrored faces and volumes
5504 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5506 poly_nodes[iNode] = (*nodeMapIt).second;
5510 if ( iNode != nbNodes )
5511 continue; // not all nodes transformed
5513 if ( theTargetMesh ) {
5514 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5515 srcElems.Append( elem );
5517 else if ( theCopy ) {
5518 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5519 srcElems.Append( elem );
5522 aMesh->ChangePolygonNodes(elem, poly_nodes);
5526 case SMDSAbs_Volume:
5528 // ATTENTION: Reversing is not yet done!!!
5529 const SMDS_VtkVolume* aPolyedre =
5530 dynamic_cast<const SMDS_VtkVolume*>( elem );
5532 MESSAGE("Warning: bad volumic element");
5536 vector<const SMDS_MeshNode*> poly_nodes;
5537 vector<int> quantities;
5539 bool allTransformed = true;
5540 int nbFaces = aPolyedre->NbFaces();
5541 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5542 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5543 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5544 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5545 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5546 if (nodeMapIt == nodeMap.end()) {
5547 allTransformed = false; // not all nodes transformed
5549 poly_nodes.push_back((*nodeMapIt).second);
5552 quantities.push_back(nbFaceNodes);
5554 if ( !allTransformed )
5555 continue; // not all nodes transformed
5557 if ( theTargetMesh ) {
5558 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5559 srcElems.Append( elem );
5561 else if ( theCopy ) {
5562 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5563 srcElems.Append( elem );
5566 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5576 int* i = index[ FORWARD ];
5577 if ( needReverse && nbNodes > 2) {// reverse mirrored faces and volumes
5578 if ( elemType == SMDSAbs_Face )
5579 i = index[ REV_FACE ];
5581 i = index[ nbNodes - 4 ];
5583 if(elem->IsQuadratic()) {
5584 static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
5587 if(nbNodes==3) { // quadratic edge
5588 static int anIds[] = {1,0,2};
5591 else if(nbNodes==6) { // quadratic triangle
5592 static int anIds[] = {0,2,1,5,4,3};
5595 else if(nbNodes==8) { // quadratic quadrangle
5596 static int anIds[] = {0,3,2,1,7,6,5,4};
5599 else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes
5600 static int anIds[] = {0,2,1,3,6,5,4,7,9,8};
5603 else if(nbNodes==13) { // quadratic pyramid of 13 nodes
5604 static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10};
5607 else if(nbNodes==15) { // quadratic pentahedron with 15 nodes
5608 static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13};
5611 else { // nbNodes==20 - quadratic hexahedron with 20 nodes
5612 static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17};
5618 // find transformed nodes
5619 vector<const SMDS_MeshNode*> nodes(nbNodes);
5621 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5622 while ( itN->more() ) {
5623 const SMDS_MeshNode* node =
5624 static_cast<const SMDS_MeshNode*>( itN->next() );
5625 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5626 if ( nodeMapIt == nodeMap.end() )
5627 break; // not all nodes transformed
5628 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5630 if ( iNode != nbNodes )
5631 continue; // not all nodes transformed
5633 if ( theTargetMesh ) {
5634 if ( SMDS_MeshElement* copy =
5635 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5636 myLastCreatedElems.Append( copy );
5637 srcElems.Append( elem );
5640 else if ( theCopy ) {
5641 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5642 srcElems.Append( elem );
5645 // reverse element as it was reversed by transformation
5647 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5651 PGroupIDs newGroupIDs;
5653 if ( ( theMakeGroups && theCopy ) ||
5654 ( theMakeGroups && theTargetMesh ) )
5655 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5661 ////=======================================================================
5662 ////function : Scale
5664 ////=======================================================================
5666 //SMESH_MeshEditor::PGroupIDs
5667 //SMESH_MeshEditor::Scale (TIDSortedElemSet & theElems,
5668 // const gp_Pnt& thePoint,
5669 // const std::list<double>& theScaleFact,
5670 // const bool theCopy,
5671 // const bool theMakeGroups,
5672 // SMESH_Mesh* theTargetMesh)
5674 // MESSAGE("Scale");
5675 // myLastCreatedElems.Clear();
5676 // myLastCreatedNodes.Clear();
5678 // SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5679 // SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5680 // SMESHDS_Mesh* aMesh = GetMeshDS();
5682 // double scaleX=1.0, scaleY=1.0, scaleZ=1.0;
5683 // std::list<double>::const_iterator itS = theScaleFact.begin();
5685 // if(theScaleFact.size()==1) {
5689 // if(theScaleFact.size()==2) {
5694 // if(theScaleFact.size()>2) {
5701 // // map old node to new one
5702 // TNodeNodeMap nodeMap;
5704 // // elements sharing moved nodes; those of them which have all
5705 // // nodes mirrored but are not in theElems are to be reversed
5706 // TIDSortedElemSet inverseElemSet;
5708 // // source elements for each generated one
5709 // SMESH_SequenceOfElemPtr srcElems, srcNodes;
5711 // // loop on theElems
5712 // TIDSortedElemSet::iterator itElem;
5713 // for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5714 // const SMDS_MeshElement* elem = *itElem;
5718 // // loop on elem nodes
5719 // SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5720 // while ( itN->more() ) {
5722 // // check if a node has been already transformed
5723 // const SMDS_MeshNode* node = cast2Node( itN->next() );
5724 // pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5725 // nodeMap.insert( make_pair ( node, node ));
5726 // if ( !n2n_isnew.second )
5729 // //double coord[3];
5730 // //coord[0] = node->X();
5731 // //coord[1] = node->Y();
5732 // //coord[2] = node->Z();
5733 // //theTrsf.Transforms( coord[0], coord[1], coord[2] );
5734 // double dx = (node->X() - thePoint.X()) * scaleX;
5735 // double dy = (node->Y() - thePoint.Y()) * scaleY;
5736 // double dz = (node->Z() - thePoint.Z()) * scaleZ;
5737 // if ( theTargetMesh ) {
5738 // //const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5739 // const SMDS_MeshNode * newNode =
5740 // aTgtMesh->AddNode( thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz );
5741 // n2n_isnew.first->second = newNode;
5742 // myLastCreatedNodes.Append(newNode);
5743 // srcNodes.Append( node );
5745 // else if ( theCopy ) {
5746 // //const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5747 // const SMDS_MeshNode * newNode =
5748 // aMesh->AddNode( thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz );
5749 // n2n_isnew.first->second = newNode;
5750 // myLastCreatedNodes.Append(newNode);
5751 // srcNodes.Append( node );
5754 // //aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5755 // aMesh->MoveNode( node, thePoint.X()+dx, thePoint.Y()+dy, thePoint.Z()+dz );
5756 // // node position on shape becomes invalid
5757 // const_cast< SMDS_MeshNode* > ( node )->SetPosition
5758 // ( SMDS_SpacePosition::originSpacePosition() );
5761 // // keep inverse elements
5762 // //if ( !theCopy && !theTargetMesh && needReverse ) {
5763 // // SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5764 // // while ( invElemIt->more() ) {
5765 // // const SMDS_MeshElement* iel = invElemIt->next();
5766 // // inverseElemSet.insert( iel );
5772 // // either create new elements or reverse mirrored ones
5773 // //if ( !theCopy && !needReverse && !theTargetMesh )
5774 // if ( !theCopy && !theTargetMesh )
5775 // return PGroupIDs();
5777 // TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5778 // for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5779 // theElems.insert( *invElemIt );
5781 // // replicate or reverse elements
5784 // REV_TETRA = 0, // = nbNodes - 4
5785 // REV_PYRAMID = 1, // = nbNodes - 4
5786 // REV_PENTA = 2, // = nbNodes - 4
5788 // REV_HEXA = 4, // = nbNodes - 4
5791 // int index[][8] = {
5792 // { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA
5793 // { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID
5794 // { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA
5795 // { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE
5796 // { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA
5797 // { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD
5800 // for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5802 // const SMDS_MeshElement* elem = *itElem;
5803 // if ( !elem || elem->GetType() == SMDSAbs_Node )
5806 // int nbNodes = elem->NbNodes();
5807 // int elemType = elem->GetType();
5809 // if (elem->IsPoly()) {
5810 // // Polygon or Polyhedral Volume
5811 // switch ( elemType ) {
5812 // case SMDSAbs_Face:
5814 // vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5816 // SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5817 // while (itN->more()) {
5818 // const SMDS_MeshNode* node =
5819 // static_cast<const SMDS_MeshNode*>(itN->next());
5820 // TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5821 // if (nodeMapIt == nodeMap.end())
5822 // break; // not all nodes transformed
5823 // //if (needReverse) {
5824 // // // reverse mirrored faces and volumes
5825 // // poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5827 // poly_nodes[iNode] = (*nodeMapIt).second;
5831 // if ( iNode != nbNodes )
5832 // continue; // not all nodes transformed
5834 // if ( theTargetMesh ) {
5835 // myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5836 // srcElems.Append( elem );
5838 // else if ( theCopy ) {
5839 // myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5840 // srcElems.Append( elem );
5843 // aMesh->ChangePolygonNodes(elem, poly_nodes);
5847 // case SMDSAbs_Volume:
5849 // // ATTENTION: Reversing is not yet done!!!
5850 // const SMDS_VtkVolume* aPolyedre =
5851 // dynamic_cast<const SMDS_VtkVolume*>( elem );
5852 // if (!aPolyedre) {
5853 // MESSAGE("Warning: bad volumic element");
5857 // vector<const SMDS_MeshNode*> poly_nodes;
5858 // vector<int> quantities;
5860 // bool allTransformed = true;
5861 // int nbFaces = aPolyedre->NbFaces();
5862 // for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5863 // int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5864 // for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5865 // const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5866 // TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5867 // if (nodeMapIt == nodeMap.end()) {
5868 // allTransformed = false; // not all nodes transformed
5870 // poly_nodes.push_back((*nodeMapIt).second);
5873 // quantities.push_back(nbFaceNodes);
5875 // if ( !allTransformed )
5876 // continue; // not all nodes transformed
5878 // if ( theTargetMesh ) {
5879 // myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5880 // srcElems.Append( elem );
5882 // else if ( theCopy ) {
5883 // myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5884 // srcElems.Append( elem );
5887 // aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5896 // // Regular elements
5897 // int* i = index[ FORWARD ];
5898 // //if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes
5899 // // if ( elemType == SMDSAbs_Face )
5900 // // i = index[ REV_FACE ];
5902 // // i = index[ nbNodes - 4 ];
5904 // if(elem->IsQuadratic()) {
5905 // static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
5907 // //if(needReverse) {
5908 // // if(nbNodes==3) { // quadratic edge
5909 // // static int anIds[] = {1,0,2};
5912 // // else if(nbNodes==6) { // quadratic triangle
5913 // // static int anIds[] = {0,2,1,5,4,3};
5916 // // else if(nbNodes==8) { // quadratic quadrangle
5917 // // static int anIds[] = {0,3,2,1,7,6,5,4};
5920 // // else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes
5921 // // static int anIds[] = {0,2,1,3,6,5,4,7,9,8};
5924 // // else if(nbNodes==13) { // quadratic pyramid of 13 nodes
5925 // // static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10};
5928 // // else if(nbNodes==15) { // quadratic pentahedron with 15 nodes
5929 // // static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13};
5932 // // else { // nbNodes==20 - quadratic hexahedron with 20 nodes
5933 // // static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17};
5939 // // find transformed nodes
5940 // vector<const SMDS_MeshNode*> nodes(nbNodes);
5942 // SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5943 // while ( itN->more() ) {
5944 // const SMDS_MeshNode* node =
5945 // static_cast<const SMDS_MeshNode*>( itN->next() );
5946 // TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5947 // if ( nodeMapIt == nodeMap.end() )
5948 // break; // not all nodes transformed
5949 // nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5951 // if ( iNode != nbNodes )
5952 // continue; // not all nodes transformed
5954 // if ( theTargetMesh ) {
5955 // if ( SMDS_MeshElement* copy =
5956 // targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5957 // myLastCreatedElems.Append( copy );
5958 // srcElems.Append( elem );
5961 // else if ( theCopy ) {
5962 // if ( SMDS_MeshElement* copy = AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5963 // myLastCreatedElems.Append( copy );
5964 // srcElems.Append( elem );
5968 // // reverse element as it was reversed by transformation
5969 // if ( nbNodes > 2 )
5970 // aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5974 // PGroupIDs newGroupIDs;
5976 // if ( theMakeGroups && theCopy ||
5977 // theMakeGroups && theTargetMesh ) {
5978 // string groupPostfix = "scaled";
5979 // newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5982 // return newGroupIDs;
5986 //=======================================================================
5988 * \brief Create groups of elements made during transformation
5989 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5990 * \param elemGens - elements making corresponding myLastCreatedElems
5991 * \param postfix - to append to names of new groups
5993 //=======================================================================
5995 SMESH_MeshEditor::PGroupIDs
5996 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5997 const SMESH_SequenceOfElemPtr& elemGens,
5998 const std::string& postfix,
5999 SMESH_Mesh* targetMesh)
6001 PGroupIDs newGroupIDs( new list<int> );
6002 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6004 // Sort existing groups by types and collect their names
6006 // to store an old group and a generated new one
6007 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
6008 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6010 set< string > groupNames;
6012 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
6013 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6014 while ( groupIt->more() ) {
6015 SMESH_Group * group = groupIt->next();
6016 if ( !group ) continue;
6017 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6018 if ( !groupDS || groupDS->IsEmpty() ) continue;
6019 groupNames.insert( group->GetName() );
6020 groupDS->SetStoreName( group->GetName() );
6021 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
6026 // loop on nodes and elements
6027 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6029 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6030 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6031 if ( gens.Length() != elems.Length() )
6032 throw SALOME_Exception(LOCALIZED("invalid args"));
6034 // loop on created elements
6035 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6037 const SMDS_MeshElement* sourceElem = gens( iElem );
6038 if ( !sourceElem ) {
6039 MESSAGE("generateGroups(): NULL source element");
6042 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6043 if ( groupsOldNew.empty() ) {
6044 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6045 ++iElem; // skip all elements made by sourceElem
6048 // collect all elements made by sourceElem
6049 list< const SMDS_MeshElement* > resultElems;
6050 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6051 if ( resElem != sourceElem )
6052 resultElems.push_back( resElem );
6053 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6054 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6055 if ( resElem != sourceElem )
6056 resultElems.push_back( resElem );
6057 // do not generate element groups from node ones
6058 if ( sourceElem->GetType() == SMDSAbs_Node &&
6059 elems( iElem )->GetType() != SMDSAbs_Node )
6062 // add resultElems to groups made by ones the sourceElem belongs to
6063 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6064 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6066 SMESHDS_GroupBase* oldGroup = gOldNew->first;
6067 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
6069 SMDS_MeshGroup* & newGroup = gOldNew->second;
6070 if ( !newGroup )// create a new group
6073 string name = oldGroup->GetStoreName();
6074 if ( !targetMesh ) {
6078 while ( !groupNames.insert( name ).second ) // name exists
6084 TCollection_AsciiString nbStr(nb+1);
6085 name.resize( name.rfind('_')+1 );
6086 name += nbStr.ToCString();
6093 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
6095 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
6096 newGroup = & groupDS->SMDSGroup();
6097 newGroupIDs->push_back( id );
6100 // fill in a new group
6101 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6102 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6103 newGroup->Add( *resElemIt );
6106 } // loop on created elements
6107 }// loop on nodes and elements
6112 //================================================================================
6114 * \brief Return list of group of nodes close to each other within theTolerance
6115 * Search among theNodes or in the whole mesh if theNodes is empty using
6116 * an Octree algorithm
6118 //================================================================================
6120 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6121 const double theTolerance,
6122 TListOfListOfNodes & theGroupsOfNodes)
6124 myLastCreatedElems.Clear();
6125 myLastCreatedNodes.Clear();
6127 if ( theNodes.empty() )
6128 { // get all nodes in the mesh
6129 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6130 while ( nIt->more() )
6131 theNodes.insert( theNodes.end(),nIt->next());
6134 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6138 //=======================================================================
6140 * \brief Implementation of search for the node closest to point
6142 //=======================================================================
6144 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6146 //---------------------------------------------------------------------
6148 * \brief Constructor
6150 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6152 myMesh = ( SMESHDS_Mesh* ) theMesh;
6154 TIDSortedNodeSet nodes;
6156 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6157 while ( nIt->more() )
6158 nodes.insert( nodes.end(), nIt->next() );
6160 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6162 // get max size of a leaf box
6163 SMESH_OctreeNode* tree = myOctreeNode;
6164 while ( !tree->isLeaf() )
6166 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6170 myHalfLeafSize = tree->maxSize() / 2.;
6173 //---------------------------------------------------------------------
6175 * \brief Move node and update myOctreeNode accordingly
6177 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6179 myOctreeNode->UpdateByMoveNode( node, toPnt );
6180 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6183 //---------------------------------------------------------------------
6185 * \brief Do it's job
6187 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6189 map<double, const SMDS_MeshNode*> dist2Nodes;
6190 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6191 if ( !dist2Nodes.empty() )
6192 return dist2Nodes.begin()->second;
6193 list<const SMDS_MeshNode*> nodes;
6194 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6196 double minSqDist = DBL_MAX;
6197 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6199 // sort leafs by their distance from thePnt
6200 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6201 TDistTreeMap treeMap;
6202 list< SMESH_OctreeNode* > treeList;
6203 list< SMESH_OctreeNode* >::iterator trIt;
6204 treeList.push_back( myOctreeNode );
6206 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6207 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6208 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6210 SMESH_OctreeNode* tree = *trIt;
6211 if ( !tree->isLeaf() ) // put children to the queue
6213 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6214 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6215 while ( cIt->more() )
6216 treeList.push_back( cIt->next() );
6218 else if ( tree->NbNodes() ) // put a tree to the treeMap
6220 const Bnd_B3d& box = tree->getBox();
6221 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6222 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6223 if ( !it_in.second ) // not unique distance to box center
6224 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6227 // find distance after which there is no sense to check tree's
6228 double sqLimit = DBL_MAX;
6229 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6230 if ( treeMap.size() > 5 ) {
6231 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6232 const Bnd_B3d& box = closestTree->getBox();
6233 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6234 sqLimit = limit * limit;
6236 // get all nodes from trees
6237 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6238 if ( sqDist_tree->first > sqLimit )
6240 SMESH_OctreeNode* tree = sqDist_tree->second;
6241 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6244 // find closest among nodes
6245 minSqDist = DBL_MAX;
6246 const SMDS_MeshNode* closestNode = 0;
6247 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6248 for ( ; nIt != nodes.end(); ++nIt ) {
6249 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6250 if ( minSqDist > sqDist ) {
6258 //---------------------------------------------------------------------
6262 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6264 //---------------------------------------------------------------------
6266 * \brief Return the node tree
6268 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6271 SMESH_OctreeNode* myOctreeNode;
6272 SMESHDS_Mesh* myMesh;
6273 double myHalfLeafSize; // max size of a leaf box
6276 //=======================================================================
6278 * \brief Return SMESH_NodeSearcher
6280 //=======================================================================
6282 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6284 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6287 // ========================================================================
6288 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6290 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6291 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6292 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6294 //=======================================================================
6296 * \brief Octal tree of bounding boxes of elements
6298 //=======================================================================
6300 class ElementBndBoxTree : public SMESH_Octree
6304 ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(), double tolerance = NodeRadius );
6305 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
6306 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6307 ~ElementBndBoxTree();
6310 ElementBndBoxTree() {}
6311 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6312 void buildChildrenData();
6313 Bnd_B3d* buildRootBox();
6315 //!< Bounding box of element
6316 struct ElementBox : public Bnd_B3d
6318 const SMDS_MeshElement* _element;
6319 int _refCount; // an ElementBox can be included in several tree branches
6320 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6322 vector< ElementBox* > _elements;
6325 //================================================================================
6327 * \brief ElementBndBoxTree creation
6329 //================================================================================
6331 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6332 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6334 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6335 _elements.reserve( nbElems );
6337 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6338 while ( elemIt->more() )
6339 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6341 if ( _elements.size() > MaxNbElemsInLeaf )
6347 //================================================================================
6351 //================================================================================
6353 ElementBndBoxTree::~ElementBndBoxTree()
6355 for ( int i = 0; i < _elements.size(); ++i )
6356 if ( --_elements[i]->_refCount <= 0 )
6357 delete _elements[i];
6360 //================================================================================
6362 * \brief Return the maximal box
6364 //================================================================================
6366 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6368 Bnd_B3d* box = new Bnd_B3d;
6369 for ( int i = 0; i < _elements.size(); ++i )
6370 box->Add( *_elements[i] );
6374 //================================================================================
6376 * \brief Redistrubute element boxes among children
6378 //================================================================================
6380 void ElementBndBoxTree::buildChildrenData()
6382 for ( int i = 0; i < _elements.size(); ++i )
6384 for (int j = 0; j < 8; j++)
6386 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6388 _elements[i]->_refCount++;
6389 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6392 _elements[i]->_refCount--;
6396 for (int j = 0; j < 8; j++)
6398 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6399 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6400 child->myIsLeaf = true;
6402 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6403 child->_elements.resize( child->_elements.size() ); // compact
6407 //================================================================================
6409 * \brief Return elements which can include the point
6411 //================================================================================
6413 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6414 TIDSortedElemSet& foundElems)
6416 if ( level() && getBox().IsOut( point.XYZ() ))
6421 for ( int i = 0; i < _elements.size(); ++i )
6422 if ( !_elements[i]->IsOut( point.XYZ() ))
6423 foundElems.insert( _elements[i]->_element );
6427 for (int i = 0; i < 8; i++)
6428 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6432 //================================================================================
6434 * \brief Return elements which can be intersected by the line
6436 //================================================================================
6438 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6439 TIDSortedElemSet& foundElems)
6441 if ( level() && getBox().IsOut( line ))
6446 for ( int i = 0; i < _elements.size(); ++i )
6447 if ( !_elements[i]->IsOut( line ))
6448 foundElems.insert( _elements[i]->_element );
6452 for (int i = 0; i < 8; i++)
6453 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6457 //================================================================================
6459 * \brief Construct the element box
6461 //================================================================================
6463 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6467 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6468 while ( nIt->more() )
6469 Add( SMESH_TNodeXYZ( cast2Node( nIt->next() )));
6470 Enlarge( tolerance );
6475 //=======================================================================
6477 * \brief Implementation of search for the elements by point and
6478 * of classification of point in 2D mesh
6480 //=======================================================================
6482 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6484 SMESHDS_Mesh* _mesh;
6485 SMDS_ElemIteratorPtr _meshPartIt;
6486 ElementBndBoxTree* _ebbTree;
6487 SMESH_NodeSearcherImpl* _nodeSearcher;
6488 SMDSAbs_ElementType _elementType;
6490 bool _outerFacesFound;
6491 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6493 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6494 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6495 ~SMESH_ElementSearcherImpl()
6497 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6498 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6500 virtual int FindElementsByPoint(const gp_Pnt& point,
6501 SMDSAbs_ElementType type,
6502 vector< const SMDS_MeshElement* >& foundElements);
6503 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6505 void GetElementsNearLine( const gp_Ax1& line,
6506 SMDSAbs_ElementType type,
6507 vector< const SMDS_MeshElement* >& foundElems);
6508 double getTolerance();
6509 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6510 const double tolerance, double & param);
6511 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6512 bool isOuterBoundary(const SMDS_MeshElement* face) const
6514 return _outerFaces.empty() || _outerFaces.count(face);
6516 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6518 const SMDS_MeshElement* _face;
6520 bool _coincides; //!< the line lays in face plane
6521 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6522 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6524 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6527 TIDSortedElemSet _faces;
6528 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6529 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6533 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6535 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6536 << ", _coincides="<<i._coincides << ")";
6539 //=======================================================================
6541 * \brief define tolerance for search
6543 //=======================================================================
6545 double SMESH_ElementSearcherImpl::getTolerance()
6547 if ( _tolerance < 0 )
6549 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6552 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6554 double boxSize = _nodeSearcher->getTree()->maxSize();
6555 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6557 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6559 double boxSize = _ebbTree->maxSize();
6560 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6562 if ( _tolerance == 0 )
6564 // define tolerance by size of a most complex element
6565 int complexType = SMDSAbs_Volume;
6566 while ( complexType > SMDSAbs_All &&
6567 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6569 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6571 if ( complexType == int( SMDSAbs_Node ))
6573 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6575 if ( meshInfo.NbNodes() > 2 )
6576 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6580 SMDS_ElemIteratorPtr elemIt =
6581 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6582 const SMDS_MeshElement* elem = elemIt->next();
6583 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6584 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6586 while ( nodeIt->more() )
6588 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6589 elemSize = max( dist, elemSize );
6592 _tolerance = 1e-4 * elemSize;
6598 //================================================================================
6600 * \brief Find intersection of the line and an edge of face and return parameter on line
6602 //================================================================================
6604 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6605 const SMDS_MeshElement* face,
6612 GeomAPI_ExtremaCurveCurve anExtCC;
6613 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6615 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6616 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6618 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6619 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6620 anExtCC.Init( lineCurve, edge);
6621 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6623 Quantity_Parameter pl, pe;
6624 anExtCC.LowerDistanceParameters( pl, pe );
6626 if ( ++nbInts == 2 )
6630 if ( nbInts > 0 ) param /= nbInts;
6633 //================================================================================
6635 * \brief Find all faces belonging to the outer boundary of mesh
6637 //================================================================================
6639 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6641 if ( _outerFacesFound ) return;
6643 // Collect all outer faces by passing from one outer face to another via their links
6644 // and BTW find out if there are internal faces at all.
6646 // checked links and links where outer boundary meets internal one
6647 set< SMESH_TLink > visitedLinks, seamLinks;
6649 // links to treat with already visited faces sharing them
6650 list < TFaceLink > startLinks;
6652 // load startLinks with the first outerFace
6653 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6654 _outerFaces.insert( outerFace );
6656 TIDSortedElemSet emptySet;
6657 while ( !startLinks.empty() )
6659 const SMESH_TLink& link = startLinks.front()._link;
6660 TIDSortedElemSet& faces = startLinks.front()._faces;
6662 outerFace = *faces.begin();
6663 // find other faces sharing the link
6664 const SMDS_MeshElement* f;
6665 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6668 // select another outer face among the found
6669 const SMDS_MeshElement* outerFace2 = 0;
6670 if ( faces.size() == 2 )
6672 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6674 else if ( faces.size() > 2 )
6676 seamLinks.insert( link );
6678 // link direction within the outerFace
6679 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6680 SMESH_TNodeXYZ( link.node2()));
6681 int i1 = outerFace->GetNodeIndex( link.node1() );
6682 int i2 = outerFace->GetNodeIndex( link.node2() );
6683 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6684 if ( rev ) n1n2.Reverse();
6686 gp_XYZ ofNorm, fNorm;
6687 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6689 // direction from the link inside outerFace
6690 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6691 // sort all other faces by angle with the dirInOF
6692 map< double, const SMDS_MeshElement* > angle2Face;
6693 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6694 for ( ; face != faces.end(); ++face )
6696 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6698 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6699 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6700 if ( angle < 0 ) angle += 2*PI;
6701 angle2Face.insert( make_pair( angle, *face ));
6703 if ( !angle2Face.empty() )
6704 outerFace2 = angle2Face.begin()->second;
6707 // store the found outer face and add its links to continue seaching from
6710 _outerFaces.insert( outerFace );
6711 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6712 for ( int i = 0; i < nbNodes; ++i )
6714 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6715 if ( visitedLinks.insert( link2 ).second )
6716 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6719 startLinks.pop_front();
6721 _outerFacesFound = true;
6723 if ( !seamLinks.empty() )
6725 // There are internal boundaries touching the outher one,
6726 // find all faces of internal boundaries in order to find
6727 // faces of boundaries of holes, if any.
6732 _outerFaces.clear();
6736 //=======================================================================
6738 * \brief Find elements of given type where the given point is IN or ON.
6739 * Returns nb of found elements and elements them-selves.
6741 * 'ALL' type means elements of any type excluding nodes and 0D elements
6743 //=======================================================================
6745 int SMESH_ElementSearcherImpl::
6746 FindElementsByPoint(const gp_Pnt& point,
6747 SMDSAbs_ElementType type,
6748 vector< const SMDS_MeshElement* >& foundElements)
6750 foundElements.clear();
6752 double tolerance = getTolerance();
6754 // =================================================================================
6755 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6757 if ( !_nodeSearcher )
6758 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6760 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6761 if ( !closeNode ) return foundElements.size();
6763 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6764 return foundElements.size(); // to far from any node
6766 if ( type == SMDSAbs_Node )
6768 foundElements.push_back( closeNode );
6772 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6773 while ( elemIt->more() )
6774 foundElements.push_back( elemIt->next() );
6777 // =================================================================================
6778 else // elements more complex than 0D
6780 if ( !_ebbTree || _elementType != type )
6782 if ( _ebbTree ) delete _ebbTree;
6783 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6785 TIDSortedElemSet suspectElems;
6786 _ebbTree->getElementsNearPoint( point, suspectElems );
6787 TIDSortedElemSet::iterator elem = suspectElems.begin();
6788 for ( ; elem != suspectElems.end(); ++elem )
6789 if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6790 foundElements.push_back( *elem );
6792 return foundElements.size();
6795 //================================================================================
6797 * \brief Classify the given point in the closed 2D mesh
6799 //================================================================================
6801 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6803 double tolerance = getTolerance();
6804 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6806 if ( _ebbTree ) delete _ebbTree;
6807 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6809 // Algo: analyse transition of a line starting at the point through mesh boundary;
6810 // try three lines parallel to axis of the coordinate system and perform rough
6811 // analysis. If solution is not clear perform thorough analysis.
6813 const int nbAxes = 3;
6814 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6815 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6816 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6817 multimap< int, int > nbInt2Axis; // to find the simplest case
6818 for ( int axis = 0; axis < nbAxes; ++axis )
6820 gp_Ax1 lineAxis( point, axisDir[axis]);
6821 gp_Lin line ( lineAxis );
6823 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6824 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6826 // Intersect faces with the line
6828 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6829 TIDSortedElemSet::iterator face = suspectFaces.begin();
6830 for ( ; face != suspectFaces.end(); ++face )
6834 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6835 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6837 // perform intersection
6838 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6839 if ( !intersection.IsDone() )
6841 if ( intersection.IsInQuadric() )
6843 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6845 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6847 gp_Pnt intersectionPoint = intersection.Point(1);
6848 if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6849 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6852 // Analyse intersections roughly
6854 int nbInter = u2inters.size();
6858 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6859 if ( nbInter == 1 ) // not closed mesh
6860 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6862 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6865 if ( (f<0) == (l<0) )
6868 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6869 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6870 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6873 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6875 if ( _outerFacesFound ) break; // pass to thorough analysis
6877 } // three attempts - loop on CS axes
6879 // Analyse intersections thoroughly.
6880 // We make two loops maximum, on the first one we only exclude touching intersections,
6881 // on the second, if situation is still unclear, we gather and use information on
6882 // position of faces (internal or outer). If faces position is already gathered,
6883 // we make the second loop right away.
6885 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6887 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6888 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6890 int axis = nb_axis->second;
6891 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6893 gp_Ax1 lineAxis( point, axisDir[axis]);
6894 gp_Lin line ( lineAxis );
6896 // add tangent intersections to u2inters
6898 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6899 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6900 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6901 u2inters.insert(make_pair( param, *tgtInt ));
6902 tangentInters[ axis ].clear();
6904 // Count intersections before and after the point excluding touching ones.
6905 // If hasPositionInfo we count intersections of outer boundary only
6907 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6908 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6909 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6910 bool ok = ! u_int1->second._coincides;
6911 while ( ok && u_int1 != u2inters.end() )
6913 double u = u_int1->first;
6914 bool touchingInt = false;
6915 if ( ++u_int2 != u2inters.end() )
6917 // skip intersections at the same point (if the line passes through edge or node)
6919 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6925 // skip tangent intersections
6927 const SMDS_MeshElement* prevFace = u_int1->second._face;
6928 while ( ok && u_int2->second._coincides )
6930 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6936 ok = ( u_int2 != u2inters.end() );
6941 // skip intersections at the same point after tangent intersections
6944 double u2 = u_int2->first;
6946 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6952 // decide if we skipped a touching intersection
6953 if ( nbSamePnt + nbTgt > 0 )
6955 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6956 map< double, TInters >::iterator u_int = u_int1;
6957 for ( ; u_int != u_int2; ++u_int )
6959 if ( u_int->second._coincides ) continue;
6960 double dot = u_int->second._faceNorm * line.Direction();
6961 if ( dot > maxDot ) maxDot = dot;
6962 if ( dot < minDot ) minDot = dot;
6964 touchingInt = ( minDot*maxDot < 0 );
6969 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6980 u_int1 = u_int2; // to next intersection
6982 } // loop on intersections with one line
6986 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6989 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6992 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6993 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6995 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6998 if ( (f<0) == (l<0) )
7001 if ( hasPositionInfo )
7002 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7004 } // loop on intersections of the tree lines - thorough analysis
7006 if ( !hasPositionInfo )
7008 // gather info on faces position - is face in the outer boundary or not
7009 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7010 findOuterBoundary( u2inters.begin()->second._face );
7013 } // two attempts - with and w/o faces position info in the mesh
7015 return TopAbs_UNKNOWN;
7018 //=======================================================================
7020 * \brief Return elements possibly intersecting the line
7022 //=======================================================================
7024 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7025 SMDSAbs_ElementType type,
7026 vector< const SMDS_MeshElement* >& foundElems)
7028 if ( !_ebbTree || _elementType != type )
7030 if ( _ebbTree ) delete _ebbTree;
7031 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7033 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7034 _ebbTree->getElementsNearLine( line, suspectFaces );
7035 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7038 //=======================================================================
7040 * \brief Return SMESH_ElementSearcher
7042 //=======================================================================
7044 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7046 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7049 //=======================================================================
7051 * \brief Return SMESH_ElementSearcher
7053 //=======================================================================
7055 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7057 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7060 //=======================================================================
7062 * \brief Return true if the point is IN or ON of the element
7064 //=======================================================================
7066 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7068 if ( element->GetType() == SMDSAbs_Volume)
7070 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7073 // get ordered nodes
7075 vector< gp_XYZ > xyz;
7076 vector<const SMDS_MeshNode*> nodeList;
7078 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7079 if ( element->IsQuadratic() ) {
7080 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7081 nodeIt = f->interlacedNodesElemIterator();
7082 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7083 nodeIt = e->interlacedNodesElemIterator();
7085 while ( nodeIt->more() )
7087 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7088 xyz.push_back( SMESH_TNodeXYZ(node) );
7089 nodeList.push_back(node);
7092 int i, nbNodes = element->NbNodes();
7094 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7096 // compute face normal
7097 gp_Vec faceNorm(0,0,0);
7098 xyz.push_back( xyz.front() );
7099 nodeList.push_back( nodeList.front() );
7100 for ( i = 0; i < nbNodes; ++i )
7102 gp_Vec edge1( xyz[i+1], xyz[i]);
7103 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7104 faceNorm += edge1 ^ edge2;
7106 double normSize = faceNorm.Magnitude();
7107 if ( normSize <= tol )
7109 // degenerated face: point is out if it is out of all face edges
7110 for ( i = 0; i < nbNodes; ++i )
7112 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7113 if ( !isOut( &edge, point, tol ))
7118 faceNorm /= normSize;
7120 // check if the point lays on face plane
7121 gp_Vec n2p( xyz[0], point );
7122 if ( fabs( n2p * faceNorm ) > tol )
7123 return true; // not on face plane
7125 // check if point is out of face boundary:
7126 // define it by closest transition of a ray point->infinity through face boundary
7127 // on the face plane.
7128 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7129 // to find intersections of the ray with the boundary.
7131 gp_Vec plnNorm = ray ^ faceNorm;
7132 normSize = plnNorm.Magnitude();
7133 if ( normSize <= tol ) return false; // point coincides with the first node
7134 plnNorm /= normSize;
7135 // for each node of the face, compute its signed distance to the plane
7136 vector<double> dist( nbNodes + 1);
7137 for ( i = 0; i < nbNodes; ++i )
7139 gp_Vec n2p( xyz[i], point );
7140 dist[i] = n2p * plnNorm;
7142 dist.back() = dist.front();
7143 // find the closest intersection
7145 double rClosest, distClosest = 1e100;;
7147 for ( i = 0; i < nbNodes; ++i )
7150 if ( fabs( dist[i]) < tol )
7152 else if ( fabs( dist[i+1]) < tol )
7154 else if ( dist[i] * dist[i+1] < 0 )
7155 r = dist[i] / ( dist[i] - dist[i+1] );
7157 continue; // no intersection
7158 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7159 gp_Vec p2int ( point, pInt);
7160 if ( p2int * ray > -tol ) // right half-space
7162 double intDist = p2int.SquareMagnitude();
7163 if ( intDist < distClosest )
7168 distClosest = intDist;
7173 return true; // no intesections - out
7175 // analyse transition
7176 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7177 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7178 gp_Vec p2int ( point, pClosest );
7179 bool out = (edgeNorm * p2int) < -tol;
7180 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7183 // ray pass through a face node; analyze transition through an adjacent edge
7184 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7185 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7186 gp_Vec edgeAdjacent( p1, p2 );
7187 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7188 bool out2 = (edgeNorm2 * p2int) < -tol;
7190 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7191 return covexCorner ? (out || out2) : (out && out2);
7193 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7195 // point is out of edge if it is NOT ON any straight part of edge
7196 // (we consider quadratic edge as being composed of two straight parts)
7197 for ( i = 1; i < nbNodes; ++i )
7199 gp_Vec edge( xyz[i-1], xyz[i]);
7200 gp_Vec n1p ( xyz[i-1], point);
7201 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7204 gp_Vec n2p( xyz[i], point );
7205 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7207 return false; // point is ON this part
7211 // Node or 0D element -------------------------------------------------------------------------
7213 gp_Vec n2p ( xyz[0], point );
7214 return n2p.Magnitude() <= tol;
7219 //=======================================================================
7220 //function : SimplifyFace
7222 //=======================================================================
7223 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7224 vector<const SMDS_MeshNode *>& poly_nodes,
7225 vector<int>& quantities) const
7227 int nbNodes = faceNodes.size();
7232 set<const SMDS_MeshNode*> nodeSet;
7234 // get simple seq of nodes
7235 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7236 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7237 int iSimple = 0, nbUnique = 0;
7239 simpleNodes[iSimple++] = faceNodes[0];
7241 for (int iCur = 1; iCur < nbNodes; iCur++) {
7242 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7243 simpleNodes[iSimple++] = faceNodes[iCur];
7244 if (nodeSet.insert( faceNodes[iCur] ).second)
7248 int nbSimple = iSimple;
7249 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7259 bool foundLoop = (nbSimple > nbUnique);
7262 set<const SMDS_MeshNode*> loopSet;
7263 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7264 const SMDS_MeshNode* n = simpleNodes[iSimple];
7265 if (!loopSet.insert( n ).second) {
7269 int iC = 0, curLast = iSimple;
7270 for (; iC < curLast; iC++) {
7271 if (simpleNodes[iC] == n) break;
7273 int loopLen = curLast - iC;
7275 // create sub-element
7277 quantities.push_back(loopLen);
7278 for (; iC < curLast; iC++) {
7279 poly_nodes.push_back(simpleNodes[iC]);
7282 // shift the rest nodes (place from the first loop position)
7283 for (iC = curLast + 1; iC < nbSimple; iC++) {
7284 simpleNodes[iC - loopLen] = simpleNodes[iC];
7286 nbSimple -= loopLen;
7289 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7290 } // while (foundLoop)
7294 quantities.push_back(iSimple);
7295 for (int i = 0; i < iSimple; i++)
7296 poly_nodes.push_back(simpleNodes[i]);
7302 //=======================================================================
7303 //function : MergeNodes
7304 //purpose : In each group, the cdr of nodes are substituted by the first one
7306 //=======================================================================
7308 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7310 MESSAGE("MergeNodes");
7311 myLastCreatedElems.Clear();
7312 myLastCreatedNodes.Clear();
7314 SMESHDS_Mesh* aMesh = GetMeshDS();
7316 TNodeNodeMap nodeNodeMap; // node to replace - new node
7317 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7318 list< int > rmElemIds, rmNodeIds;
7320 // Fill nodeNodeMap and elems
7322 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7323 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7324 list<const SMDS_MeshNode*>& nodes = *grIt;
7325 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7326 const SMDS_MeshNode* nToKeep = *nIt;
7327 //MESSAGE("node to keep " << nToKeep->GetID());
7328 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7329 const SMDS_MeshNode* nToRemove = *nIt;
7330 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7331 if ( nToRemove != nToKeep ) {
7332 //MESSAGE(" node to remove " << nToRemove->GetID());
7333 rmNodeIds.push_back( nToRemove->GetID() );
7334 AddToSameGroups( nToKeep, nToRemove, aMesh );
7337 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7338 while ( invElemIt->more() ) {
7339 const SMDS_MeshElement* elem = invElemIt->next();
7344 // Change element nodes or remove an element
7346 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7347 for ( ; eIt != elems.end(); eIt++ ) {
7348 const SMDS_MeshElement* elem = *eIt;
7349 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7350 int nbNodes = elem->NbNodes();
7351 int aShapeId = FindShape( elem );
7353 set<const SMDS_MeshNode*> nodeSet;
7354 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7355 int iUnique = 0, iCur = 0, nbRepl = 0;
7356 vector<int> iRepl( nbNodes );
7358 // get new seq of nodes
7359 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7360 while ( itN->more() ) {
7361 const SMDS_MeshNode* n =
7362 static_cast<const SMDS_MeshNode*>( itN->next() );
7364 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7365 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7367 // BUG 0020185: begin
7369 bool stopRecur = false;
7370 set<const SMDS_MeshNode*> nodesRecur;
7371 nodesRecur.insert(n);
7372 while (!stopRecur) {
7373 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7374 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7375 n = (*nnIt_i).second;
7376 if (!nodesRecur.insert(n).second) {
7377 // error: recursive dependancy
7386 iRepl[ nbRepl++ ] = iCur;
7388 curNodes[ iCur ] = n;
7389 bool isUnique = nodeSet.insert( n ).second;
7391 uniqueNodes[ iUnique++ ] = n;
7392 if ( nbRepl && iRepl[ nbRepl-1 ] == iCur )
7393 --nbRepl; // n do not stick to a node of the elem
7398 // Analyse element topology after replacement
7401 int nbUniqueNodes = nodeSet.size();
7402 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7403 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7404 // Polygons and Polyhedral volumes
7405 if (elem->IsPoly()) {
7407 if (elem->GetType() == SMDSAbs_Face) {
7409 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7411 for (; inode < nbNodes; inode++) {
7412 face_nodes[inode] = curNodes[inode];
7415 vector<const SMDS_MeshNode *> polygons_nodes;
7416 vector<int> quantities;
7417 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7420 for (int iface = 0; iface < nbNew; iface++) {
7421 int nbNodes = quantities[iface];
7422 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7423 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7424 poly_nodes[ii] = polygons_nodes[inode];
7426 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7427 myLastCreatedElems.Append(newElem);
7429 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7432 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7433 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7434 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7436 if (nbNew > 0) quid = nbNew - 1;
7437 vector<int> newquant(quantities.begin()+quid, quantities.end());
7438 const SMDS_MeshElement* newElem = 0;
7439 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7440 myLastCreatedElems.Append(newElem);
7441 if ( aShapeId && newElem )
7442 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7443 rmElemIds.push_back(elem->GetID());
7446 rmElemIds.push_back(elem->GetID());
7450 else if (elem->GetType() == SMDSAbs_Volume) {
7451 // Polyhedral volume
7452 if (nbUniqueNodes < 4) {
7453 rmElemIds.push_back(elem->GetID());
7456 // each face has to be analyzed in order to check volume validity
7457 const SMDS_VtkVolume* aPolyedre =
7458 dynamic_cast<const SMDS_VtkVolume*>( elem );
7460 int nbFaces = aPolyedre->NbFaces();
7462 vector<const SMDS_MeshNode *> poly_nodes;
7463 vector<int> quantities;
7465 for (int iface = 1; iface <= nbFaces; iface++) {
7466 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7467 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7469 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7470 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7471 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7472 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7473 faceNode = (*nnIt).second;
7475 faceNodes[inode - 1] = faceNode;
7478 SimplifyFace(faceNodes, poly_nodes, quantities);
7481 if (quantities.size() > 3) {
7482 // to be done: remove coincident faces
7485 if (quantities.size() > 3)
7487 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7488 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7489 const SMDS_MeshElement* newElem = 0;
7490 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7491 myLastCreatedElems.Append(newElem);
7492 if ( aShapeId && newElem )
7493 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7494 rmElemIds.push_back(elem->GetID());
7498 rmElemIds.push_back(elem->GetID());
7509 // TODO not all the possible cases are solved. Find something more generic?
7510 switch ( nbNodes ) {
7511 case 2: ///////////////////////////////////// EDGE
7512 isOk = false; break;
7513 case 3: ///////////////////////////////////// TRIANGLE
7514 isOk = false; break;
7516 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7518 else { //////////////////////////////////// QUADRANGLE
7519 if ( nbUniqueNodes < 3 )
7521 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7522 isOk = false; // opposite nodes stick
7523 //MESSAGE("isOk " << isOk);
7526 case 6: ///////////////////////////////////// PENTAHEDRON
7527 if ( nbUniqueNodes == 4 ) {
7528 // ---------------------------------> tetrahedron
7530 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7531 // all top nodes stick: reverse a bottom
7532 uniqueNodes[ 0 ] = curNodes [ 1 ];
7533 uniqueNodes[ 1 ] = curNodes [ 0 ];
7535 else if (nbRepl == 3 &&
7536 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7537 // all bottom nodes stick: set a top before
7538 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7539 uniqueNodes[ 0 ] = curNodes [ 3 ];
7540 uniqueNodes[ 1 ] = curNodes [ 4 ];
7541 uniqueNodes[ 2 ] = curNodes [ 5 ];
7543 else if (nbRepl == 4 &&
7544 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7545 // a lateral face turns into a line: reverse a bottom
7546 uniqueNodes[ 0 ] = curNodes [ 1 ];
7547 uniqueNodes[ 1 ] = curNodes [ 0 ];
7552 else if ( nbUniqueNodes == 5 ) {
7553 // PENTAHEDRON --------------------> 2 tetrahedrons
7554 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7555 // a bottom node sticks with a linked top one
7557 SMDS_MeshElement* newElem =
7558 aMesh->AddVolume(curNodes[ 3 ],
7561 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7562 myLastCreatedElems.Append(newElem);
7564 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7565 // 2. : reverse a bottom
7566 uniqueNodes[ 0 ] = curNodes [ 1 ];
7567 uniqueNodes[ 1 ] = curNodes [ 0 ];
7577 if(elem->IsQuadratic()) { // Quadratic quadrangle
7589 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7592 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7594 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7595 uniqueNodes[0] = curNodes[0];
7596 uniqueNodes[1] = curNodes[2];
7597 uniqueNodes[2] = curNodes[3];
7598 uniqueNodes[3] = curNodes[5];
7599 uniqueNodes[4] = curNodes[6];
7600 uniqueNodes[5] = curNodes[7];
7603 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7604 uniqueNodes[0] = curNodes[0];
7605 uniqueNodes[1] = curNodes[1];
7606 uniqueNodes[2] = curNodes[2];
7607 uniqueNodes[3] = curNodes[4];
7608 uniqueNodes[4] = curNodes[5];
7609 uniqueNodes[5] = curNodes[6];
7612 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7613 uniqueNodes[0] = curNodes[1];
7614 uniqueNodes[1] = curNodes[2];
7615 uniqueNodes[2] = curNodes[3];
7616 uniqueNodes[3] = curNodes[5];
7617 uniqueNodes[4] = curNodes[6];
7618 uniqueNodes[5] = curNodes[0];
7621 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7622 uniqueNodes[0] = curNodes[0];
7623 uniqueNodes[1] = curNodes[1];
7624 uniqueNodes[2] = curNodes[3];
7625 uniqueNodes[3] = curNodes[4];
7626 uniqueNodes[4] = curNodes[6];
7627 uniqueNodes[5] = curNodes[7];
7630 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7631 uniqueNodes[0] = curNodes[0];
7632 uniqueNodes[1] = curNodes[2];
7633 uniqueNodes[2] = curNodes[3];
7634 uniqueNodes[3] = curNodes[1];
7635 uniqueNodes[4] = curNodes[6];
7636 uniqueNodes[5] = curNodes[7];
7639 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7640 uniqueNodes[0] = curNodes[0];
7641 uniqueNodes[1] = curNodes[1];
7642 uniqueNodes[2] = curNodes[2];
7643 uniqueNodes[3] = curNodes[4];
7644 uniqueNodes[4] = curNodes[5];
7645 uniqueNodes[5] = curNodes[7];
7648 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7649 uniqueNodes[0] = curNodes[0];
7650 uniqueNodes[1] = curNodes[1];
7651 uniqueNodes[2] = curNodes[3];
7652 uniqueNodes[3] = curNodes[4];
7653 uniqueNodes[4] = curNodes[2];
7654 uniqueNodes[5] = curNodes[7];
7657 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7658 uniqueNodes[0] = curNodes[0];
7659 uniqueNodes[1] = curNodes[1];
7660 uniqueNodes[2] = curNodes[2];
7661 uniqueNodes[3] = curNodes[4];
7662 uniqueNodes[4] = curNodes[5];
7663 uniqueNodes[5] = curNodes[3];
7668 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7671 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7675 //////////////////////////////////// HEXAHEDRON
7677 SMDS_VolumeTool hexa (elem);
7678 hexa.SetExternalNormal();
7679 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7680 //////////////////////// HEX ---> 1 tetrahedron
7681 for ( int iFace = 0; iFace < 6; iFace++ ) {
7682 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7683 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7684 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7685 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7686 // one face turns into a point ...
7687 int iOppFace = hexa.GetOppFaceIndex( iFace );
7688 ind = hexa.GetFaceNodesIndices( iOppFace );
7690 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7691 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7694 if ( nbStick == 1 ) {
7695 // ... and the opposite one - into a triangle.
7697 ind = hexa.GetFaceNodesIndices( iFace );
7698 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7705 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7706 //////////////////////// HEX ---> 1 prism
7707 int nbTria = 0, iTria[3];
7708 const int *ind; // indices of face nodes
7709 // look for triangular faces
7710 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7711 ind = hexa.GetFaceNodesIndices( iFace );
7712 TIDSortedNodeSet faceNodes;
7713 for ( iCur = 0; iCur < 4; iCur++ )
7714 faceNodes.insert( curNodes[ind[iCur]] );
7715 if ( faceNodes.size() == 3 )
7716 iTria[ nbTria++ ] = iFace;
7718 // check if triangles are opposite
7719 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7722 // set nodes of the bottom triangle
7723 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7725 for ( iCur = 0; iCur < 4; iCur++ )
7726 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7727 indB.push_back( ind[iCur] );
7728 if ( !hexa.IsForward() )
7729 std::swap( indB[0], indB[2] );
7730 for ( iCur = 0; iCur < 3; iCur++ )
7731 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7732 // set nodes of the top triangle
7733 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7734 for ( iCur = 0; iCur < 3; ++iCur )
7735 for ( int j = 0; j < 4; ++j )
7736 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7738 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7744 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7745 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7746 for ( int iFace = 0; iFace < 6; iFace++ ) {
7747 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7748 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7749 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7750 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7751 // one face turns into a point ...
7752 int iOppFace = hexa.GetOppFaceIndex( iFace );
7753 ind = hexa.GetFaceNodesIndices( iOppFace );
7755 iUnique = 2; // reverse a tetrahedron 1 bottom
7756 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7757 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7759 else if ( iUnique >= 0 )
7760 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7762 if ( nbStick == 0 ) {
7763 // ... and the opposite one is a quadrangle
7765 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7766 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7769 SMDS_MeshElement* newElem =
7770 aMesh->AddVolume(curNodes[ind[ 0 ]],
7773 curNodes[indTop[ 0 ]]);
7774 myLastCreatedElems.Append(newElem);
7776 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7783 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7784 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7785 // find indices of quad and tri faces
7786 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7787 for ( iFace = 0; iFace < 6; iFace++ ) {
7788 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7790 for ( iCur = 0; iCur < 4; iCur++ )
7791 nodeSet.insert( curNodes[ind[ iCur ]] );
7792 nbUniqueNodes = nodeSet.size();
7793 if ( nbUniqueNodes == 3 )
7794 iTriFace[ nbTri++ ] = iFace;
7795 else if ( nbUniqueNodes == 4 )
7796 iQuadFace[ nbQuad++ ] = iFace;
7798 if (nbQuad == 2 && nbTri == 4 &&
7799 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7800 // 2 opposite quadrangles stuck with a diagonal;
7801 // sample groups of merged indices: (0-4)(2-6)
7802 // --------------------------------------------> 2 tetrahedrons
7803 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7804 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7805 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7806 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7807 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7808 // stuck with 0-2 diagonal
7816 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7817 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7818 // stuck with 1-3 diagonal
7830 uniqueNodes[ 0 ] = curNodes [ i0 ];
7831 uniqueNodes[ 1 ] = curNodes [ i1d ];
7832 uniqueNodes[ 2 ] = curNodes [ i3d ];
7833 uniqueNodes[ 3 ] = curNodes [ i0t ];
7836 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7840 myLastCreatedElems.Append(newElem);
7842 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7845 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7846 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7847 // --------------------------------------------> prism
7848 // find 2 opposite triangles
7850 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7851 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7852 // find indices of kept and replaced nodes
7853 // and fill unique nodes of 2 opposite triangles
7854 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7855 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7856 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7857 // fill unique nodes
7860 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7861 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7862 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7864 // iCur of a linked node of the opposite face (make normals co-directed):
7865 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7866 // check that correspondent corners of triangles are linked
7867 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7870 uniqueNodes[ iUnique ] = n;
7871 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7880 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7883 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7890 } // switch ( nbNodes )
7892 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7894 if ( isOk ) { // the elem remains valid after sticking nodes
7895 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7897 // Change nodes of polyedre
7898 const SMDS_VtkVolume* aPolyedre =
7899 dynamic_cast<const SMDS_VtkVolume*>( elem );
7901 int nbFaces = aPolyedre->NbFaces();
7903 vector<const SMDS_MeshNode *> poly_nodes;
7904 vector<int> quantities (nbFaces);
7906 for (int iface = 1; iface <= nbFaces; iface++) {
7907 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7908 quantities[iface - 1] = nbFaceNodes;
7910 for (inode = 1; inode <= nbFaceNodes; inode++) {
7911 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7913 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7914 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7915 curNode = (*nnIt).second;
7917 poly_nodes.push_back(curNode);
7920 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7923 else // replace non-polyhedron elements
7925 const SMDSAbs_ElementType etyp = elem->GetType();
7926 const int elemId = elem->GetID();
7927 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7928 uniqueNodes.resize(nbUniqueNodes);
7930 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7932 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7933 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7934 if ( sm && newElem )
7935 sm->AddElement( newElem );
7936 if ( elem != newElem )
7937 ReplaceElemInGroups( elem, newElem, aMesh );
7941 // Remove invalid regular element or invalid polygon
7942 rmElemIds.push_back( elem->GetID() );
7945 } // loop on elements
7947 // Remove bad elements, then equal nodes (order important)
7949 Remove( rmElemIds, false );
7950 Remove( rmNodeIds, true );
7955 // ========================================================
7956 // class : SortableElement
7957 // purpose : allow sorting elements basing on their nodes
7958 // ========================================================
7959 class SortableElement : public set <const SMDS_MeshElement*>
7963 SortableElement( const SMDS_MeshElement* theElem )
7966 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7967 while ( nodeIt->more() )
7968 this->insert( nodeIt->next() );
7971 const SMDS_MeshElement* Get() const
7974 void Set(const SMDS_MeshElement* e) const
7979 mutable const SMDS_MeshElement* myElem;
7982 //=======================================================================
7983 //function : FindEqualElements
7984 //purpose : Return list of group of elements built on the same nodes.
7985 // Search among theElements or in the whole mesh if theElements is empty
7986 //=======================================================================
7987 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7988 TListOfListOfElementsID & theGroupsOfElementsID)
7990 myLastCreatedElems.Clear();
7991 myLastCreatedNodes.Clear();
7993 typedef set<const SMDS_MeshElement*> TElemsSet;
7994 typedef map< SortableElement, int > TMapOfNodeSet;
7995 typedef list<int> TGroupOfElems;
7998 if ( theElements.empty() )
7999 { // get all elements in the mesh
8000 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8001 while ( eIt->more() )
8002 elems.insert( elems.end(), eIt->next());
8005 elems = theElements;
8007 vector< TGroupOfElems > arrayOfGroups;
8008 TGroupOfElems groupOfElems;
8009 TMapOfNodeSet mapOfNodeSet;
8011 TElemsSet::iterator elemIt = elems.begin();
8012 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
8013 const SMDS_MeshElement* curElem = *elemIt;
8014 SortableElement SE(curElem);
8017 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8018 if( !(pp.second) ) {
8019 TMapOfNodeSet::iterator& itSE = pp.first;
8020 ind = (*itSE).second;
8021 arrayOfGroups[ind].push_back(curElem->GetID());
8024 groupOfElems.clear();
8025 groupOfElems.push_back(curElem->GetID());
8026 arrayOfGroups.push_back(groupOfElems);
8031 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8032 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8033 groupOfElems = *groupIt;
8034 if ( groupOfElems.size() > 1 ) {
8035 groupOfElems.sort();
8036 theGroupsOfElementsID.push_back(groupOfElems);
8041 //=======================================================================
8042 //function : MergeElements
8043 //purpose : In each given group, substitute all elements by the first one.
8044 //=======================================================================
8046 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8048 myLastCreatedElems.Clear();
8049 myLastCreatedNodes.Clear();
8051 typedef list<int> TListOfIDs;
8052 TListOfIDs rmElemIds; // IDs of elems to remove
8054 SMESHDS_Mesh* aMesh = GetMeshDS();
8056 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8057 while ( groupsIt != theGroupsOfElementsID.end() ) {
8058 TListOfIDs& aGroupOfElemID = *groupsIt;
8059 aGroupOfElemID.sort();
8060 int elemIDToKeep = aGroupOfElemID.front();
8061 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8062 aGroupOfElemID.pop_front();
8063 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8064 while ( idIt != aGroupOfElemID.end() ) {
8065 int elemIDToRemove = *idIt;
8066 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8067 // add the kept element in groups of removed one (PAL15188)
8068 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8069 rmElemIds.push_back( elemIDToRemove );
8075 Remove( rmElemIds, false );
8078 //=======================================================================
8079 //function : MergeEqualElements
8080 //purpose : Remove all but one of elements built on the same nodes.
8081 //=======================================================================
8083 void SMESH_MeshEditor::MergeEqualElements()
8085 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
8086 to merge equal elements in the whole mesh */
8087 TListOfListOfElementsID aGroupsOfElementsID;
8088 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8089 MergeElements(aGroupsOfElementsID);
8092 //=======================================================================
8093 //function : FindFaceInSet
8094 //purpose : Return a face having linked nodes n1 and n2 and which is
8095 // - not in avoidSet,
8096 // - in elemSet provided that !elemSet.empty()
8097 // i1 and i2 optionally returns indices of n1 and n2
8098 //=======================================================================
8100 const SMDS_MeshElement*
8101 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8102 const SMDS_MeshNode* n2,
8103 const TIDSortedElemSet& elemSet,
8104 const TIDSortedElemSet& avoidSet,
8110 const SMDS_MeshElement* face = 0;
8112 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8113 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8114 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8116 //MESSAGE("in while ( invElemIt->more() && !face )");
8117 const SMDS_MeshElement* elem = invElemIt->next();
8118 if (avoidSet.count( elem ))
8120 if ( !elemSet.empty() && !elemSet.count( elem ))
8123 i1 = elem->GetNodeIndex( n1 );
8124 // find a n2 linked to n1
8125 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8126 for ( int di = -1; di < 2 && !face; di += 2 )
8128 i2 = (i1+di+nbN) % nbN;
8129 if ( elem->GetNode( i2 ) == n2 )
8132 if ( !face && elem->IsQuadratic())
8134 // analysis for quadratic elements using all nodes
8135 const SMDS_VtkFace* F =
8136 dynamic_cast<const SMDS_VtkFace*>(elem);
8137 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8138 // use special nodes iterator
8139 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8140 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8141 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8143 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8144 if ( n1 == prevN && n2 == n )
8148 else if ( n2 == prevN && n1 == n )
8150 face = elem; swap( i1, i2 );
8156 if ( n1ind ) *n1ind = i1;
8157 if ( n2ind ) *n2ind = i2;
8161 //=======================================================================
8162 //function : findAdjacentFace
8164 //=======================================================================
8166 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8167 const SMDS_MeshNode* n2,
8168 const SMDS_MeshElement* elem)
8170 TIDSortedElemSet elemSet, avoidSet;
8172 avoidSet.insert ( elem );
8173 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8176 //=======================================================================
8177 //function : FindFreeBorder
8179 //=======================================================================
8181 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8183 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8184 const SMDS_MeshNode* theSecondNode,
8185 const SMDS_MeshNode* theLastNode,
8186 list< const SMDS_MeshNode* > & theNodes,
8187 list< const SMDS_MeshElement* >& theFaces)
8189 if ( !theFirstNode || !theSecondNode )
8191 // find border face between theFirstNode and theSecondNode
8192 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8196 theFaces.push_back( curElem );
8197 theNodes.push_back( theFirstNode );
8198 theNodes.push_back( theSecondNode );
8200 //vector<const SMDS_MeshNode*> nodes;
8201 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8202 TIDSortedElemSet foundElems;
8203 bool needTheLast = ( theLastNode != 0 );
8205 while ( nStart != theLastNode ) {
8206 if ( nStart == theFirstNode )
8207 return !needTheLast;
8209 // find all free border faces sharing form nStart
8211 list< const SMDS_MeshElement* > curElemList;
8212 list< const SMDS_MeshNode* > nStartList;
8213 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8214 while ( invElemIt->more() ) {
8215 const SMDS_MeshElement* e = invElemIt->next();
8216 if ( e == curElem || foundElems.insert( e ).second ) {
8218 int iNode = 0, nbNodes = e->NbNodes();
8219 //const SMDS_MeshNode* nodes[nbNodes+1];
8220 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8222 if(e->IsQuadratic()) {
8223 const SMDS_VtkFace* F =
8224 dynamic_cast<const SMDS_VtkFace*>(e);
8225 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8226 // use special nodes iterator
8227 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8228 while( anIter->more() ) {
8229 nodes[ iNode++ ] = cast2Node(anIter->next());
8233 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8234 while ( nIt->more() )
8235 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8237 nodes[ iNode ] = nodes[ 0 ];
8239 for ( iNode = 0; iNode < nbNodes; iNode++ )
8240 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8241 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8242 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8244 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8245 curElemList.push_back( e );
8249 // analyse the found
8251 int nbNewBorders = curElemList.size();
8252 if ( nbNewBorders == 0 ) {
8253 // no free border furthermore
8254 return !needTheLast;
8256 else if ( nbNewBorders == 1 ) {
8257 // one more element found
8259 nStart = nStartList.front();
8260 curElem = curElemList.front();
8261 theFaces.push_back( curElem );
8262 theNodes.push_back( nStart );
8265 // several continuations found
8266 list< const SMDS_MeshElement* >::iterator curElemIt;
8267 list< const SMDS_MeshNode* >::iterator nStartIt;
8268 // check if one of them reached the last node
8269 if ( needTheLast ) {
8270 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8271 curElemIt!= curElemList.end();
8272 curElemIt++, nStartIt++ )
8273 if ( *nStartIt == theLastNode ) {
8274 theFaces.push_back( *curElemIt );
8275 theNodes.push_back( *nStartIt );
8279 // find the best free border by the continuations
8280 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8281 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8282 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8283 curElemIt!= curElemList.end();
8284 curElemIt++, nStartIt++ )
8286 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8287 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8288 // find one more free border
8289 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8293 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8294 // choice: clear a worse one
8295 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8296 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8297 contNodes[ iWorse ].clear();
8298 contFaces[ iWorse ].clear();
8301 if ( contNodes[0].empty() && contNodes[1].empty() )
8304 // append the best free border
8305 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8306 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8307 theNodes.pop_back(); // remove nIgnore
8308 theNodes.pop_back(); // remove nStart
8309 theFaces.pop_back(); // remove curElem
8310 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8311 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8312 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8313 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8316 } // several continuations found
8317 } // while ( nStart != theLastNode )
8322 //=======================================================================
8323 //function : CheckFreeBorderNodes
8324 //purpose : Return true if the tree nodes are on a free border
8325 //=======================================================================
8327 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8328 const SMDS_MeshNode* theNode2,
8329 const SMDS_MeshNode* theNode3)
8331 list< const SMDS_MeshNode* > nodes;
8332 list< const SMDS_MeshElement* > faces;
8333 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8336 //=======================================================================
8337 //function : SewFreeBorder
8339 //=======================================================================
8341 SMESH_MeshEditor::Sew_Error
8342 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8343 const SMDS_MeshNode* theBordSecondNode,
8344 const SMDS_MeshNode* theBordLastNode,
8345 const SMDS_MeshNode* theSideFirstNode,
8346 const SMDS_MeshNode* theSideSecondNode,
8347 const SMDS_MeshNode* theSideThirdNode,
8348 const bool theSideIsFreeBorder,
8349 const bool toCreatePolygons,
8350 const bool toCreatePolyedrs)
8352 myLastCreatedElems.Clear();
8353 myLastCreatedNodes.Clear();
8355 MESSAGE("::SewFreeBorder()");
8356 Sew_Error aResult = SEW_OK;
8358 // ====================================
8359 // find side nodes and elements
8360 // ====================================
8362 list< const SMDS_MeshNode* > nSide[ 2 ];
8363 list< const SMDS_MeshElement* > eSide[ 2 ];
8364 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8365 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8369 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8370 nSide[0], eSide[0])) {
8371 MESSAGE(" Free Border 1 not found " );
8372 aResult = SEW_BORDER1_NOT_FOUND;
8374 if (theSideIsFreeBorder) {
8377 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8378 nSide[1], eSide[1])) {
8379 MESSAGE(" Free Border 2 not found " );
8380 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8383 if ( aResult != SEW_OK )
8386 if (!theSideIsFreeBorder) {
8390 // -------------------------------------------------------------------------
8392 // 1. If nodes to merge are not coincident, move nodes of the free border
8393 // from the coord sys defined by the direction from the first to last
8394 // nodes of the border to the correspondent sys of the side 2
8395 // 2. On the side 2, find the links most co-directed with the correspondent
8396 // links of the free border
8397 // -------------------------------------------------------------------------
8399 // 1. Since sewing may break if there are volumes to split on the side 2,
8400 // we wont move nodes but just compute new coordinates for them
8401 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8402 TNodeXYZMap nBordXYZ;
8403 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8404 list< const SMDS_MeshNode* >::iterator nBordIt;
8406 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8407 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8408 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8409 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8410 double tol2 = 1.e-8;
8411 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8412 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8413 // Need node movement.
8415 // find X and Z axes to create trsf
8416 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8418 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8420 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8423 gp_Ax3 toBordAx( Pb1, Zb, X );
8424 gp_Ax3 fromSideAx( Ps1, Zs, X );
8425 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8427 gp_Trsf toBordSys, fromSide2Sys;
8428 toBordSys.SetTransformation( toBordAx );
8429 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8430 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8433 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8434 const SMDS_MeshNode* n = *nBordIt;
8435 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8436 toBordSys.Transforms( xyz );
8437 fromSide2Sys.Transforms( xyz );
8438 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8442 // just insert nodes XYZ in the nBordXYZ map
8443 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8444 const SMDS_MeshNode* n = *nBordIt;
8445 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8449 // 2. On the side 2, find the links most co-directed with the correspondent
8450 // links of the free border
8452 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8453 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8454 sideNodes.push_back( theSideFirstNode );
8456 bool hasVolumes = false;
8457 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8458 set<long> foundSideLinkIDs, checkedLinkIDs;
8459 SMDS_VolumeTool volume;
8460 //const SMDS_MeshNode* faceNodes[ 4 ];
8462 const SMDS_MeshNode* sideNode;
8463 const SMDS_MeshElement* sideElem;
8464 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8465 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8466 nBordIt = bordNodes.begin();
8468 // border node position and border link direction to compare with
8469 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8470 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8471 // choose next side node by link direction or by closeness to
8472 // the current border node:
8473 bool searchByDir = ( *nBordIt != theBordLastNode );
8475 // find the next node on the Side 2
8477 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8479 checkedLinkIDs.clear();
8480 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8482 // loop on inverse elements of current node (prevSideNode) on the Side 2
8483 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8484 while ( invElemIt->more() )
8486 const SMDS_MeshElement* elem = invElemIt->next();
8487 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8488 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8489 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8490 bool isVolume = volume.Set( elem );
8491 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8492 if ( isVolume ) // --volume
8494 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8495 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8496 if(elem->IsQuadratic()) {
8497 const SMDS_VtkFace* F =
8498 dynamic_cast<const SMDS_VtkFace*>(elem);
8499 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8500 // use special nodes iterator
8501 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8502 while( anIter->more() ) {
8503 nodes[ iNode ] = cast2Node(anIter->next());
8504 if ( nodes[ iNode++ ] == prevSideNode )
8505 iPrevNode = iNode - 1;
8509 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8510 while ( nIt->more() ) {
8511 nodes[ iNode ] = cast2Node( nIt->next() );
8512 if ( nodes[ iNode++ ] == prevSideNode )
8513 iPrevNode = iNode - 1;
8516 // there are 2 links to check
8521 // loop on links, to be precise, on the second node of links
8522 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8523 const SMDS_MeshNode* n = nodes[ iNode ];
8525 if ( !volume.IsLinked( n, prevSideNode ))
8529 if ( iNode ) // a node before prevSideNode
8530 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8531 else // a node after prevSideNode
8532 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8534 // check if this link was already used
8535 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8536 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8537 if (!isJustChecked &&
8538 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8540 // test a link geometrically
8541 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8542 bool linkIsBetter = false;
8543 double dot = 0.0, dist = 0.0;
8544 if ( searchByDir ) { // choose most co-directed link
8545 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8546 linkIsBetter = ( dot > maxDot );
8548 else { // choose link with the node closest to bordPos
8549 dist = ( nextXYZ - bordPos ).SquareModulus();
8550 linkIsBetter = ( dist < minDist );
8552 if ( linkIsBetter ) {
8561 } // loop on inverse elements of prevSideNode
8564 MESSAGE(" Cant find path by links of the Side 2 ");
8565 return SEW_BAD_SIDE_NODES;
8567 sideNodes.push_back( sideNode );
8568 sideElems.push_back( sideElem );
8569 foundSideLinkIDs.insert ( linkID );
8570 prevSideNode = sideNode;
8572 if ( *nBordIt == theBordLastNode )
8573 searchByDir = false;
8575 // find the next border link to compare with
8576 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8577 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8578 // move to next border node if sideNode is before forward border node (bordPos)
8579 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8580 prevBordNode = *nBordIt;
8582 bordPos = nBordXYZ[ *nBordIt ];
8583 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8584 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8588 while ( sideNode != theSideSecondNode );
8590 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8591 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8592 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8594 } // end nodes search on the side 2
8596 // ============================
8597 // sew the border to the side 2
8598 // ============================
8600 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8601 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8603 TListOfListOfNodes nodeGroupsToMerge;
8604 if ( nbNodes[0] == nbNodes[1] ||
8605 ( theSideIsFreeBorder && !theSideThirdNode)) {
8607 // all nodes are to be merged
8609 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8610 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8611 nIt[0]++, nIt[1]++ )
8613 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8614 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8615 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8620 // insert new nodes into the border and the side to get equal nb of segments
8622 // get normalized parameters of nodes on the borders
8623 //double param[ 2 ][ maxNbNodes ];
8625 param[0] = new double [ maxNbNodes ];
8626 param[1] = new double [ maxNbNodes ];
8628 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8629 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8630 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8631 const SMDS_MeshNode* nPrev = *nIt;
8632 double bordLength = 0;
8633 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8634 const SMDS_MeshNode* nCur = *nIt;
8635 gp_XYZ segment (nCur->X() - nPrev->X(),
8636 nCur->Y() - nPrev->Y(),
8637 nCur->Z() - nPrev->Z());
8638 double segmentLen = segment.Modulus();
8639 bordLength += segmentLen;
8640 param[ iBord ][ iNode ] = bordLength;
8643 // normalize within [0,1]
8644 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8645 param[ iBord ][ iNode ] /= bordLength;
8649 // loop on border segments
8650 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8651 int i[ 2 ] = { 0, 0 };
8652 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8653 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8655 TElemOfNodeListMap insertMap;
8656 TElemOfNodeListMap::iterator insertMapIt;
8658 // key: elem to insert nodes into
8659 // value: 2 nodes to insert between + nodes to be inserted
8661 bool next[ 2 ] = { false, false };
8663 // find min adjacent segment length after sewing
8664 double nextParam = 10., prevParam = 0;
8665 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8666 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8667 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8668 if ( i[ iBord ] > 0 )
8669 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8671 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8672 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8673 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8675 // choose to insert or to merge nodes
8676 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8677 if ( Abs( du ) <= minSegLen * 0.2 ) {
8680 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8681 const SMDS_MeshNode* n0 = *nIt[0];
8682 const SMDS_MeshNode* n1 = *nIt[1];
8683 nodeGroupsToMerge.back().push_back( n1 );
8684 nodeGroupsToMerge.back().push_back( n0 );
8685 // position of node of the border changes due to merge
8686 param[ 0 ][ i[0] ] += du;
8687 // move n1 for the sake of elem shape evaluation during insertion.
8688 // n1 will be removed by MergeNodes() anyway
8689 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8690 next[0] = next[1] = true;
8695 int intoBord = ( du < 0 ) ? 0 : 1;
8696 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8697 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8698 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8699 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8700 if ( intoBord == 1 ) {
8701 // move node of the border to be on a link of elem of the side
8702 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8703 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8704 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8705 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8706 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8708 insertMapIt = insertMap.find( elem );
8709 bool notFound = ( insertMapIt == insertMap.end() );
8710 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8712 // insert into another link of the same element:
8713 // 1. perform insertion into the other link of the elem
8714 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8715 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8716 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8717 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8718 // 2. perform insertion into the link of adjacent faces
8720 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8722 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8726 if (toCreatePolyedrs) {
8727 // perform insertion into the links of adjacent volumes
8728 UpdateVolumes(n12, n22, nodeList);
8730 // 3. find an element appeared on n1 and n2 after the insertion
8731 insertMap.erase( elem );
8732 elem = findAdjacentFace( n1, n2, 0 );
8734 if ( notFound || otherLink ) {
8735 // add element and nodes of the side into the insertMap
8736 insertMapIt = insertMap.insert
8737 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8738 (*insertMapIt).second.push_back( n1 );
8739 (*insertMapIt).second.push_back( n2 );
8741 // add node to be inserted into elem
8742 (*insertMapIt).second.push_back( nIns );
8743 next[ 1 - intoBord ] = true;
8746 // go to the next segment
8747 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8748 if ( next[ iBord ] ) {
8749 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8751 nPrev[ iBord ] = *nIt[ iBord ];
8752 nIt[ iBord ]++; i[ iBord ]++;
8756 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8758 // perform insertion of nodes into elements
8760 for (insertMapIt = insertMap.begin();
8761 insertMapIt != insertMap.end();
8764 const SMDS_MeshElement* elem = (*insertMapIt).first;
8765 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8766 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8767 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8769 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8771 if ( !theSideIsFreeBorder ) {
8772 // look for and insert nodes into the faces adjacent to elem
8774 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8776 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8781 if (toCreatePolyedrs) {
8782 // perform insertion into the links of adjacent volumes
8783 UpdateVolumes(n1, n2, nodeList);
8789 } // end: insert new nodes
8791 MergeNodes ( nodeGroupsToMerge );
8796 //=======================================================================
8797 //function : InsertNodesIntoLink
8798 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8799 // and theBetweenNode2 and split theElement
8800 //=======================================================================
8802 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8803 const SMDS_MeshNode* theBetweenNode1,
8804 const SMDS_MeshNode* theBetweenNode2,
8805 list<const SMDS_MeshNode*>& theNodesToInsert,
8806 const bool toCreatePoly)
8808 if ( theFace->GetType() != SMDSAbs_Face ) return;
8810 // find indices of 2 link nodes and of the rest nodes
8811 int iNode = 0, il1, il2, i3, i4;
8812 il1 = il2 = i3 = i4 = -1;
8813 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8814 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8816 if(theFace->IsQuadratic()) {
8817 const SMDS_VtkFace* F =
8818 dynamic_cast<const SMDS_VtkFace*>(theFace);
8819 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8820 // use special nodes iterator
8821 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8822 while( anIter->more() ) {
8823 const SMDS_MeshNode* n = cast2Node(anIter->next());
8824 if ( n == theBetweenNode1 )
8826 else if ( n == theBetweenNode2 )
8832 nodes[ iNode++ ] = n;
8836 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8837 while ( nodeIt->more() ) {
8838 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8839 if ( n == theBetweenNode1 )
8841 else if ( n == theBetweenNode2 )
8847 nodes[ iNode++ ] = n;
8850 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8853 // arrange link nodes to go one after another regarding the face orientation
8854 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8855 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8860 aNodesToInsert.reverse();
8862 // check that not link nodes of a quadrangles are in good order
8863 int nbFaceNodes = theFace->NbNodes();
8864 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8870 if (toCreatePoly || theFace->IsPoly()) {
8873 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8875 // add nodes of face up to first node of link
8878 if(theFace->IsQuadratic()) {
8879 const SMDS_VtkFace* F =
8880 dynamic_cast<const SMDS_VtkFace*>(theFace);
8881 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8882 // use special nodes iterator
8883 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8884 while( anIter->more() && !isFLN ) {
8885 const SMDS_MeshNode* n = cast2Node(anIter->next());
8886 poly_nodes[iNode++] = n;
8887 if (n == nodes[il1]) {
8891 // add nodes to insert
8892 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8893 for (; nIt != aNodesToInsert.end(); nIt++) {
8894 poly_nodes[iNode++] = *nIt;
8896 // add nodes of face starting from last node of link
8897 while ( anIter->more() ) {
8898 poly_nodes[iNode++] = cast2Node(anIter->next());
8902 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8903 while ( nodeIt->more() && !isFLN ) {
8904 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8905 poly_nodes[iNode++] = n;
8906 if (n == nodes[il1]) {
8910 // add nodes to insert
8911 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8912 for (; nIt != aNodesToInsert.end(); nIt++) {
8913 poly_nodes[iNode++] = *nIt;
8915 // add nodes of face starting from last node of link
8916 while ( nodeIt->more() ) {
8917 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8918 poly_nodes[iNode++] = n;
8922 // edit or replace the face
8923 SMESHDS_Mesh *aMesh = GetMeshDS();
8925 if (theFace->IsPoly()) {
8926 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8929 int aShapeId = FindShape( theFace );
8931 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8932 myLastCreatedElems.Append(newElem);
8933 if ( aShapeId && newElem )
8934 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8936 aMesh->RemoveElement(theFace);
8941 SMESHDS_Mesh *aMesh = GetMeshDS();
8942 if( !theFace->IsQuadratic() ) {
8944 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8945 int nbLinkNodes = 2 + aNodesToInsert.size();
8946 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8947 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8948 linkNodes[ 0 ] = nodes[ il1 ];
8949 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8950 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8951 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8952 linkNodes[ iNode++ ] = *nIt;
8954 // decide how to split a quadrangle: compare possible variants
8955 // and choose which of splits to be a quadrangle
8956 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8957 if ( nbFaceNodes == 3 ) {
8958 iBestQuad = nbSplits;
8961 else if ( nbFaceNodes == 4 ) {
8962 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8963 double aBestRate = DBL_MAX;
8964 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8966 double aBadRate = 0;
8967 // evaluate elements quality
8968 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8969 if ( iSplit == iQuad ) {
8970 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8974 aBadRate += getBadRate( &quad, aCrit );
8977 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8979 nodes[ iSplit < iQuad ? i4 : i3 ]);
8980 aBadRate += getBadRate( &tria, aCrit );
8984 if ( aBadRate < aBestRate ) {
8986 aBestRate = aBadRate;
8991 // create new elements
8992 int aShapeId = FindShape( theFace );
8995 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8996 SMDS_MeshElement* newElem = 0;
8997 if ( iSplit == iBestQuad )
8998 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9003 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9005 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9006 myLastCreatedElems.Append(newElem);
9007 if ( aShapeId && newElem )
9008 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9011 // change nodes of theFace
9012 const SMDS_MeshNode* newNodes[ 4 ];
9013 newNodes[ 0 ] = linkNodes[ i1 ];
9014 newNodes[ 1 ] = linkNodes[ i2 ];
9015 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9016 newNodes[ 3 ] = nodes[ i4 ];
9017 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9018 const SMDS_MeshElement* newElem = 0;
9019 if (iSplit == iBestQuad)
9020 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9022 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9023 myLastCreatedElems.Append(newElem);
9024 if ( aShapeId && newElem )
9025 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9026 } // end if(!theFace->IsQuadratic())
9027 else { // theFace is quadratic
9028 // we have to split theFace on simple triangles and one simple quadrangle
9030 int nbshift = tmp*2;
9031 // shift nodes in nodes[] by nbshift
9033 for(i=0; i<nbshift; i++) {
9034 const SMDS_MeshNode* n = nodes[0];
9035 for(j=0; j<nbFaceNodes-1; j++) {
9036 nodes[j] = nodes[j+1];
9038 nodes[nbFaceNodes-1] = n;
9040 il1 = il1 - nbshift;
9041 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9042 // n0 n1 n2 n0 n1 n2
9043 // +-----+-----+ +-----+-----+
9052 // create new elements
9053 int aShapeId = FindShape( theFace );
9056 if(nbFaceNodes==6) { // quadratic triangle
9057 SMDS_MeshElement* newElem =
9058 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9059 myLastCreatedElems.Append(newElem);
9060 if ( aShapeId && newElem )
9061 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9062 if(theFace->IsMediumNode(nodes[il1])) {
9063 // create quadrangle
9064 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9065 myLastCreatedElems.Append(newElem);
9066 if ( aShapeId && newElem )
9067 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9073 // create quadrangle
9074 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9075 myLastCreatedElems.Append(newElem);
9076 if ( aShapeId && newElem )
9077 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9083 else { // nbFaceNodes==8 - quadratic quadrangle
9084 SMDS_MeshElement* newElem =
9085 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9086 myLastCreatedElems.Append(newElem);
9087 if ( aShapeId && newElem )
9088 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9089 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9090 myLastCreatedElems.Append(newElem);
9091 if ( aShapeId && newElem )
9092 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9093 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9094 myLastCreatedElems.Append(newElem);
9095 if ( aShapeId && newElem )
9096 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9097 if(theFace->IsMediumNode(nodes[il1])) {
9098 // create quadrangle
9099 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9100 myLastCreatedElems.Append(newElem);
9101 if ( aShapeId && newElem )
9102 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9108 // create quadrangle
9109 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9110 myLastCreatedElems.Append(newElem);
9111 if ( aShapeId && newElem )
9112 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9118 // create needed triangles using n1,n2,n3 and inserted nodes
9119 int nbn = 2 + aNodesToInsert.size();
9120 //const SMDS_MeshNode* aNodes[nbn];
9121 vector<const SMDS_MeshNode*> aNodes(nbn);
9122 aNodes[0] = nodes[n1];
9123 aNodes[nbn-1] = nodes[n2];
9124 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9125 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9126 aNodes[iNode++] = *nIt;
9128 for(i=1; i<nbn; i++) {
9129 SMDS_MeshElement* newElem =
9130 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9131 myLastCreatedElems.Append(newElem);
9132 if ( aShapeId && newElem )
9133 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9137 aMesh->RemoveElement(theFace);
9140 //=======================================================================
9141 //function : UpdateVolumes
9143 //=======================================================================
9144 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9145 const SMDS_MeshNode* theBetweenNode2,
9146 list<const SMDS_MeshNode*>& theNodesToInsert)
9148 myLastCreatedElems.Clear();
9149 myLastCreatedNodes.Clear();
9151 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9152 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9153 const SMDS_MeshElement* elem = invElemIt->next();
9155 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9156 SMDS_VolumeTool aVolume (elem);
9157 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9160 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9161 int iface, nbFaces = aVolume.NbFaces();
9162 vector<const SMDS_MeshNode *> poly_nodes;
9163 vector<int> quantities (nbFaces);
9165 for (iface = 0; iface < nbFaces; iface++) {
9166 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9167 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9168 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9170 for (int inode = 0; inode < nbFaceNodes; inode++) {
9171 poly_nodes.push_back(faceNodes[inode]);
9173 if (nbInserted == 0) {
9174 if (faceNodes[inode] == theBetweenNode1) {
9175 if (faceNodes[inode + 1] == theBetweenNode2) {
9176 nbInserted = theNodesToInsert.size();
9178 // add nodes to insert
9179 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9180 for (; nIt != theNodesToInsert.end(); nIt++) {
9181 poly_nodes.push_back(*nIt);
9185 else if (faceNodes[inode] == theBetweenNode2) {
9186 if (faceNodes[inode + 1] == theBetweenNode1) {
9187 nbInserted = theNodesToInsert.size();
9189 // add nodes to insert in reversed order
9190 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9192 for (; nIt != theNodesToInsert.begin(); nIt--) {
9193 poly_nodes.push_back(*nIt);
9195 poly_nodes.push_back(*nIt);
9202 quantities[iface] = nbFaceNodes + nbInserted;
9205 // Replace or update the volume
9206 SMESHDS_Mesh *aMesh = GetMeshDS();
9208 if (elem->IsPoly()) {
9209 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9213 int aShapeId = FindShape( elem );
9215 SMDS_MeshElement* newElem =
9216 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9217 myLastCreatedElems.Append(newElem);
9218 if (aShapeId && newElem)
9219 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9221 aMesh->RemoveElement(elem);
9226 //=======================================================================
9228 * \brief Convert elements contained in a submesh to quadratic
9229 * \return int - nb of checked elements
9231 //=======================================================================
9233 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9234 SMESH_MesherHelper& theHelper,
9235 const bool theForce3d)
9238 if( !theSm ) return nbElem;
9240 vector<int> nbNodeInFaces;
9241 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9242 while(ElemItr->more())
9245 const SMDS_MeshElement* elem = ElemItr->next();
9246 if( !elem || elem->IsQuadratic() ) continue;
9248 int id = elem->GetID();
9249 int nbNodes = elem->NbNodes();
9250 SMDSAbs_ElementType aType = elem->GetType();
9252 vector<const SMDS_MeshNode *> nodes (elem->begin_nodes(), elem->end_nodes());
9253 if ( elem->GetEntityType() == SMDSEntity_Polyhedra )
9254 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9256 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9258 const SMDS_MeshElement* NewElem = 0;
9264 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9272 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9275 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9278 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9283 case SMDSAbs_Volume :
9288 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9291 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9294 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9297 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9298 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9301 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9308 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9310 theSm->AddElement( NewElem );
9312 // if (!GetMeshDS()->isCompacted())
9313 // GetMeshDS()->compactMesh();
9317 //=======================================================================
9318 //function : ConvertToQuadratic
9320 //=======================================================================
9321 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9323 SMESHDS_Mesh* meshDS = GetMeshDS();
9325 SMESH_MesherHelper aHelper(*myMesh);
9326 aHelper.SetIsQuadratic( true );
9328 int nbCheckedElems = 0;
9329 if ( myMesh->HasShapeToMesh() )
9331 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9333 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9334 while ( smIt->more() ) {
9335 SMESH_subMesh* sm = smIt->next();
9336 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9337 aHelper.SetSubShape( sm->GetSubShape() );
9338 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9343 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9344 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9346 SMESHDS_SubMesh *smDS = 0;
9347 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9348 while(aEdgeItr->more())
9350 const SMDS_MeshEdge* edge = aEdgeItr->next();
9351 if(edge && !edge->IsQuadratic())
9353 int id = edge->GetID();
9354 //MESSAGE("edge->GetID() " << id);
9355 const SMDS_MeshNode* n1 = edge->GetNode(0);
9356 const SMDS_MeshNode* n2 = edge->GetNode(1);
9358 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9360 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9361 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9364 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9365 while(aFaceItr->more())
9367 const SMDS_MeshFace* face = aFaceItr->next();
9368 if(!face || face->IsQuadratic() ) continue;
9370 int id = face->GetID();
9371 int nbNodes = face->NbNodes();
9372 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9374 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9376 SMDS_MeshFace * NewFace = 0;
9380 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9383 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9386 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9388 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9390 vector<int> nbNodeInFaces;
9391 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9392 while(aVolumeItr->more())
9394 const SMDS_MeshVolume* volume = aVolumeItr->next();
9395 if(!volume || volume->IsQuadratic() ) continue;
9397 int id = volume->GetID();
9398 int nbNodes = volume->NbNodes();
9399 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9400 if ( volume->GetEntityType() == SMDSEntity_Polyhedra )
9401 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9403 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9405 SMDS_MeshVolume * NewVolume = 0;
9409 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9410 nodes[3], id, theForce3d );
9413 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9414 nodes[3], nodes[4], id, theForce3d);
9417 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9418 nodes[3], nodes[4], nodes[5], id, theForce3d);
9421 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9422 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9425 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9427 ReplaceElemInGroups(volume, NewVolume, meshDS);
9432 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9433 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9434 aHelper.FixQuadraticElements();
9438 //================================================================================
9440 * \brief Makes given elements quadratic
9441 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9442 * \param theElements - elements to make quadratic
9444 //================================================================================
9446 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9447 TIDSortedElemSet& theElements)
9449 if ( theElements.empty() ) return;
9451 // we believe that all theElements are of the same type
9452 SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9454 // get all nodes shared by theElements
9455 TIDSortedNodeSet allNodes;
9456 TIDSortedElemSet::iterator eIt = theElements.begin();
9457 for ( ; eIt != theElements.end(); ++eIt )
9458 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9460 // complete theElements with elements of lower dim whose all nodes are in allNodes
9462 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9463 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9464 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9465 for ( ; nIt != allNodes.end(); ++nIt )
9467 const SMDS_MeshNode* n = *nIt;
9468 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9469 while ( invIt->more() )
9471 const SMDS_MeshElement* e = invIt->next();
9472 if ( e->IsQuadratic() )
9474 quadAdjacentElems[ e->GetType() ].insert( e );
9477 if ( e->GetType() >= elemType )
9479 continue; // same type of more complex linear element
9482 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9483 continue; // e is already checked
9487 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9488 while ( nodeIt->more() && allIn )
9489 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9491 theElements.insert(e );
9495 SMESH_MesherHelper helper(*myMesh);
9496 helper.SetIsQuadratic( true );
9498 // add links of quadratic adjacent elements to the helper
9500 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9501 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9502 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9504 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9506 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9507 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9508 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9510 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9512 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9513 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9514 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9516 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9519 // make quadratic elements instead of linear ones
9521 SMESHDS_Mesh* meshDS = GetMeshDS();
9522 SMESHDS_SubMesh* smDS = 0;
9523 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9525 const SMDS_MeshElement* elem = *eIt;
9526 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9529 int id = elem->GetID();
9530 SMDSAbs_ElementType type = elem->GetType();
9531 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9533 if ( !smDS || !smDS->Contains( elem ))
9534 smDS = meshDS->MeshElements( elem->getshapeId() );
9535 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9537 SMDS_MeshElement * newElem = 0;
9538 switch( nodes.size() )
9540 case 4: // cases for most multiple element types go first (for optimization)
9541 if ( type == SMDSAbs_Volume )
9542 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9544 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9547 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9548 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9551 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9554 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9557 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9558 nodes[4], id, theForce3d);
9561 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9562 nodes[4], nodes[5], id, theForce3d);
9566 ReplaceElemInGroups( elem, newElem, meshDS);
9567 if( newElem && smDS )
9568 smDS->AddElement( newElem );
9571 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9572 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9573 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9574 helper.FixQuadraticElements();
9578 //=======================================================================
9580 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9581 * \return int - nb of checked elements
9583 //=======================================================================
9585 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9586 SMDS_ElemIteratorPtr theItr,
9587 const int theShapeID)
9590 SMESHDS_Mesh* meshDS = GetMeshDS();
9592 while( theItr->more() )
9594 const SMDS_MeshElement* elem = theItr->next();
9596 if( elem && elem->IsQuadratic())
9598 int id = elem->GetID();
9599 int nbCornerNodes = elem->NbCornerNodes();
9600 SMDSAbs_ElementType aType = elem->GetType();
9602 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9604 //remove a quadratic element
9605 if ( !theSm || !theSm->Contains( elem ))
9606 theSm = meshDS->MeshElements( elem->getshapeId() );
9607 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9609 // remove medium nodes
9610 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9611 if ( nodes[i]->NbInverseElements() == 0 )
9612 meshDS->RemoveFreeNode( nodes[i], theSm );
9614 // add a linear element
9615 nodes.resize( nbCornerNodes );
9616 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9617 ReplaceElemInGroups(elem, newElem, meshDS);
9618 if( theSm && newElem )
9619 theSm->AddElement( newElem );
9625 //=======================================================================
9626 //function : ConvertFromQuadratic
9628 //=======================================================================
9630 bool SMESH_MeshEditor::ConvertFromQuadratic()
9632 int nbCheckedElems = 0;
9633 if ( myMesh->HasShapeToMesh() )
9635 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9637 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9638 while ( smIt->more() ) {
9639 SMESH_subMesh* sm = smIt->next();
9640 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9641 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9647 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9648 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9650 SMESHDS_SubMesh *aSM = 0;
9651 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9659 //================================================================================
9661 * \brief Return true if all medium nodes of the element are in the node set
9663 //================================================================================
9665 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9667 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9668 if ( !nodeSet.count( elem->GetNode(i) ))
9674 //================================================================================
9676 * \brief Makes given elements linear
9678 //================================================================================
9680 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9682 if ( theElements.empty() ) return;
9684 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9685 set<int> mediumNodeIDs;
9686 TIDSortedElemSet::iterator eIt = theElements.begin();
9687 for ( ; eIt != theElements.end(); ++eIt )
9689 const SMDS_MeshElement* e = *eIt;
9690 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9691 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9694 // replace given elements by linear ones
9695 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9696 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9697 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9699 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9700 // except those elements sharing medium nodes of quadratic element whose medium nodes
9701 // are not all in mediumNodeIDs
9703 // get remaining medium nodes
9704 TIDSortedNodeSet mediumNodes;
9705 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9706 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9707 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9708 mediumNodes.insert( mediumNodes.end(), n );
9710 // find more quadratic elements to convert
9711 TIDSortedElemSet moreElemsToConvert;
9712 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9713 for ( ; nIt != mediumNodes.end(); ++nIt )
9715 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9716 while ( invIt->more() )
9718 const SMDS_MeshElement* e = invIt->next();
9719 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9721 // find a more complex element including e and
9722 // whose medium nodes are not in mediumNodes
9723 bool complexFound = false;
9724 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9726 SMDS_ElemIteratorPtr invIt2 =
9727 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9728 while ( invIt2->more() )
9730 const SMDS_MeshElement* eComplex = invIt2->next();
9731 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9733 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9734 if ( nbCommonNodes == e->NbNodes())
9736 complexFound = true;
9737 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9743 if ( !complexFound )
9744 moreElemsToConvert.insert( e );
9748 elemIt = SMDS_ElemIteratorPtr
9749 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9750 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9753 //=======================================================================
9754 //function : SewSideElements
9756 //=======================================================================
9758 SMESH_MeshEditor::Sew_Error
9759 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9760 TIDSortedElemSet& theSide2,
9761 const SMDS_MeshNode* theFirstNode1,
9762 const SMDS_MeshNode* theFirstNode2,
9763 const SMDS_MeshNode* theSecondNode1,
9764 const SMDS_MeshNode* theSecondNode2)
9766 myLastCreatedElems.Clear();
9767 myLastCreatedNodes.Clear();
9769 MESSAGE ("::::SewSideElements()");
9770 if ( theSide1.size() != theSide2.size() )
9771 return SEW_DIFF_NB_OF_ELEMENTS;
9773 Sew_Error aResult = SEW_OK;
9775 // 1. Build set of faces representing each side
9776 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9777 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9779 // =======================================================================
9780 // 1. Build set of faces representing each side:
9781 // =======================================================================
9782 // a. build set of nodes belonging to faces
9783 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9784 // c. create temporary faces representing side of volumes if correspondent
9785 // face does not exist
9787 SMESHDS_Mesh* aMesh = GetMeshDS();
9788 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9789 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9790 set<const SMDS_MeshElement*> faceSet1, faceSet2;
9791 set<const SMDS_MeshElement*> volSet1, volSet2;
9792 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9793 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9794 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9795 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9796 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9797 int iSide, iFace, iNode;
9799 list<const SMDS_MeshElement* > tempFaceList;
9800 for ( iSide = 0; iSide < 2; iSide++ ) {
9801 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9802 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9803 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9804 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9805 set<const SMDS_MeshElement*>::iterator vIt;
9806 TIDSortedElemSet::iterator eIt;
9807 set<const SMDS_MeshNode*>::iterator nIt;
9809 // check that given nodes belong to given elements
9810 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9811 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9812 int firstIndex = -1, secondIndex = -1;
9813 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9814 const SMDS_MeshElement* elem = *eIt;
9815 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9816 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9817 if ( firstIndex > -1 && secondIndex > -1 ) break;
9819 if ( firstIndex < 0 || secondIndex < 0 ) {
9820 // we can simply return until temporary faces created
9821 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9824 // -----------------------------------------------------------
9825 // 1a. Collect nodes of existing faces
9826 // and build set of face nodes in order to detect missing
9827 // faces corresponding to sides of volumes
9828 // -----------------------------------------------------------
9830 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9832 // loop on the given element of a side
9833 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9834 //const SMDS_MeshElement* elem = *eIt;
9835 const SMDS_MeshElement* elem = *eIt;
9836 if ( elem->GetType() == SMDSAbs_Face ) {
9837 faceSet->insert( elem );
9838 set <const SMDS_MeshNode*> faceNodeSet;
9839 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9840 while ( nodeIt->more() ) {
9841 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9842 nodeSet->insert( n );
9843 faceNodeSet.insert( n );
9845 setOfFaceNodeSet.insert( faceNodeSet );
9847 else if ( elem->GetType() == SMDSAbs_Volume )
9848 volSet->insert( elem );
9850 // ------------------------------------------------------------------------------
9851 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9852 // ------------------------------------------------------------------------------
9854 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9855 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9856 while ( fIt->more() ) { // loop on faces sharing a node
9857 const SMDS_MeshElement* f = fIt->next();
9858 if ( faceSet->find( f ) == faceSet->end() ) {
9859 // check if all nodes are in nodeSet and
9860 // complete setOfFaceNodeSet if they are
9861 set <const SMDS_MeshNode*> faceNodeSet;
9862 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9863 bool allInSet = true;
9864 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9865 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9866 if ( nodeSet->find( n ) == nodeSet->end() )
9869 faceNodeSet.insert( n );
9872 faceSet->insert( f );
9873 setOfFaceNodeSet.insert( faceNodeSet );
9879 // -------------------------------------------------------------------------
9880 // 1c. Create temporary faces representing sides of volumes if correspondent
9881 // face does not exist
9882 // -------------------------------------------------------------------------
9884 if ( !volSet->empty() ) {
9885 //int nodeSetSize = nodeSet->size();
9887 // loop on given volumes
9888 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9889 SMDS_VolumeTool vol (*vIt);
9890 // loop on volume faces: find free faces
9891 // --------------------------------------
9892 list<const SMDS_MeshElement* > freeFaceList;
9893 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9894 if ( !vol.IsFreeFace( iFace ))
9896 // check if there is already a face with same nodes in a face set
9897 const SMDS_MeshElement* aFreeFace = 0;
9898 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9899 int nbNodes = vol.NbFaceNodes( iFace );
9900 set <const SMDS_MeshNode*> faceNodeSet;
9901 vol.GetFaceNodes( iFace, faceNodeSet );
9902 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9904 // no such a face is given but it still can exist, check it
9905 if ( nbNodes == 3 ) {
9906 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9908 else if ( nbNodes == 4 ) {
9909 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9912 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9913 aFreeFace = aMesh->FindFace(poly_nodes);
9917 // create a temporary face
9918 if ( nbNodes == 3 ) {
9919 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9920 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9922 else if ( nbNodes == 4 ) {
9923 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9924 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9927 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9928 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9929 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9933 freeFaceList.push_back( aFreeFace );
9934 tempFaceList.push_back( aFreeFace );
9937 } // loop on faces of a volume
9939 // choose one of several free faces
9940 // --------------------------------------
9941 if ( freeFaceList.size() > 1 ) {
9942 // choose a face having max nb of nodes shared by other elems of a side
9943 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9944 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9945 while ( fIt != freeFaceList.end() ) { // loop on free faces
9946 int nbSharedNodes = 0;
9947 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9948 while ( nodeIt->more() ) { // loop on free face nodes
9949 const SMDS_MeshNode* n =
9950 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9951 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9952 while ( invElemIt->more() ) {
9953 const SMDS_MeshElement* e = invElemIt->next();
9954 if ( faceSet->find( e ) != faceSet->end() )
9956 if ( elemSet->find( e ) != elemSet->end() )
9960 if ( nbSharedNodes >= maxNbNodes ) {
9961 maxNbNodes = nbSharedNodes;
9965 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9967 if ( freeFaceList.size() > 1 )
9969 // could not choose one face, use another way
9970 // choose a face most close to the bary center of the opposite side
9971 gp_XYZ aBC( 0., 0., 0. );
9972 set <const SMDS_MeshNode*> addedNodes;
9973 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9974 eIt = elemSet2->begin();
9975 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9976 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9977 while ( nodeIt->more() ) { // loop on free face nodes
9978 const SMDS_MeshNode* n =
9979 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9980 if ( addedNodes.insert( n ).second )
9981 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9984 aBC /= addedNodes.size();
9985 double minDist = DBL_MAX;
9986 fIt = freeFaceList.begin();
9987 while ( fIt != freeFaceList.end() ) { // loop on free faces
9989 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9990 while ( nodeIt->more() ) { // loop on free face nodes
9991 const SMDS_MeshNode* n =
9992 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9993 gp_XYZ p( n->X(),n->Y(),n->Z() );
9994 dist += ( aBC - p ).SquareModulus();
9996 if ( dist < minDist ) {
9998 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10001 fIt = freeFaceList.erase( fIt++ );
10004 } // choose one of several free faces of a volume
10006 if ( freeFaceList.size() == 1 ) {
10007 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10008 faceSet->insert( aFreeFace );
10009 // complete a node set with nodes of a found free face
10010 // for ( iNode = 0; iNode < ; iNode++ )
10011 // nodeSet->insert( fNodes[ iNode ] );
10014 } // loop on volumes of a side
10016 // // complete a set of faces if new nodes in a nodeSet appeared
10017 // // ----------------------------------------------------------
10018 // if ( nodeSetSize != nodeSet->size() ) {
10019 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10020 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10021 // while ( fIt->more() ) { // loop on faces sharing a node
10022 // const SMDS_MeshElement* f = fIt->next();
10023 // if ( faceSet->find( f ) == faceSet->end() ) {
10024 // // check if all nodes are in nodeSet and
10025 // // complete setOfFaceNodeSet if they are
10026 // set <const SMDS_MeshNode*> faceNodeSet;
10027 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10028 // bool allInSet = true;
10029 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10030 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10031 // if ( nodeSet->find( n ) == nodeSet->end() )
10032 // allInSet = false;
10034 // faceNodeSet.insert( n );
10036 // if ( allInSet ) {
10037 // faceSet->insert( f );
10038 // setOfFaceNodeSet.insert( faceNodeSet );
10044 } // Create temporary faces, if there are volumes given
10047 if ( faceSet1.size() != faceSet2.size() ) {
10048 // delete temporary faces: they are in reverseElements of actual nodes
10049 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10050 // while ( tmpFaceIt->more() )
10051 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10052 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10053 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10054 // aMesh->RemoveElement(*tmpFaceIt);
10055 MESSAGE("Diff nb of faces");
10056 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10059 // ============================================================
10060 // 2. Find nodes to merge:
10061 // bind a node to remove to a node to put instead
10062 // ============================================================
10064 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10065 if ( theFirstNode1 != theFirstNode2 )
10066 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
10067 if ( theSecondNode1 != theSecondNode2 )
10068 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
10070 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10071 set< long > linkIdSet; // links to process
10072 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10074 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10075 list< NLink > linkList[2];
10076 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10077 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10078 // loop on links in linkList; find faces by links and append links
10079 // of the found faces to linkList
10080 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10081 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10082 NLink link[] = { *linkIt[0], *linkIt[1] };
10083 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10084 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
10087 // by links, find faces in the face sets,
10088 // and find indices of link nodes in the found faces;
10089 // in a face set, there is only one or no face sharing a link
10090 // ---------------------------------------------------------------
10092 const SMDS_MeshElement* face[] = { 0, 0 };
10093 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
10094 vector<const SMDS_MeshNode*> fnodes1(9);
10095 vector<const SMDS_MeshNode*> fnodes2(9);
10096 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
10097 vector<const SMDS_MeshNode*> notLinkNodes1(6);
10098 vector<const SMDS_MeshNode*> notLinkNodes2(6);
10099 int iLinkNode[2][2];
10100 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10101 const SMDS_MeshNode* n1 = link[iSide].first;
10102 const SMDS_MeshNode* n2 = link[iSide].second;
10103 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10104 set< const SMDS_MeshElement* > fMap;
10105 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
10106 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
10107 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10108 while ( fIt->more() ) { // loop on faces sharing a node
10109 const SMDS_MeshElement* f = fIt->next();
10110 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10111 ! fMap.insert( f ).second ) // f encounters twice
10113 if ( face[ iSide ] ) {
10114 MESSAGE( "2 faces per link " );
10115 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
10119 faceSet->erase( f );
10120 // get face nodes and find ones of a link
10125 fnodes1.resize(f->NbNodes()+1);
10126 notLinkNodes1.resize(f->NbNodes()-2);
10129 fnodes2.resize(f->NbNodes()+1);
10130 notLinkNodes2.resize(f->NbNodes()-2);
10133 if(!f->IsQuadratic()) {
10134 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
10135 while ( nIt->more() ) {
10136 const SMDS_MeshNode* n =
10137 static_cast<const SMDS_MeshNode*>( nIt->next() );
10139 iLinkNode[ iSide ][ 0 ] = iNode;
10141 else if ( n == n2 ) {
10142 iLinkNode[ iSide ][ 1 ] = iNode;
10144 //else if ( notLinkNodes[ iSide ][ 0 ] )
10145 // notLinkNodes[ iSide ][ 1 ] = n;
10147 // notLinkNodes[ iSide ][ 0 ] = n;
10151 notLinkNodes1[nbl] = n;
10152 //notLinkNodes1.push_back(n);
10154 notLinkNodes2[nbl] = n;
10155 //notLinkNodes2.push_back(n);
10157 //faceNodes[ iSide ][ iNode++ ] = n;
10159 fnodes1[iNode++] = n;
10162 fnodes2[iNode++] = n;
10166 else { // f->IsQuadratic()
10167 const SMDS_VtkFace* F =
10168 dynamic_cast<const SMDS_VtkFace*>(f);
10169 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10170 // use special nodes iterator
10171 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
10172 while ( anIter->more() ) {
10173 const SMDS_MeshNode* n =
10174 static_cast<const SMDS_MeshNode*>( anIter->next() );
10176 iLinkNode[ iSide ][ 0 ] = iNode;
10178 else if ( n == n2 ) {
10179 iLinkNode[ iSide ][ 1 ] = iNode;
10184 notLinkNodes1[nbl] = n;
10187 notLinkNodes2[nbl] = n;
10191 fnodes1[iNode++] = n;
10194 fnodes2[iNode++] = n;
10198 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
10200 fnodes1[iNode] = fnodes1[0];
10203 fnodes2[iNode] = fnodes1[0];
10210 // check similarity of elements of the sides
10211 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10212 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10213 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10214 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10217 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10219 break; // do not return because it s necessary to remove tmp faces
10222 // set nodes to merge
10223 // -------------------
10225 if ( face[0] && face[1] ) {
10226 int nbNodes = face[0]->NbNodes();
10227 if ( nbNodes != face[1]->NbNodes() ) {
10228 MESSAGE("Diff nb of face nodes");
10229 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10230 break; // do not return because it s necessary to remove tmp faces
10232 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
10233 if ( nbNodes == 3 ) {
10234 //nReplaceMap.insert( TNodeNodeMap::value_type
10235 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10236 nReplaceMap.insert( TNodeNodeMap::value_type
10237 ( notLinkNodes1[0], notLinkNodes2[0] ));
10240 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10241 // analyse link orientation in faces
10242 int i1 = iLinkNode[ iSide ][ 0 ];
10243 int i2 = iLinkNode[ iSide ][ 1 ];
10244 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10245 // if notLinkNodes are the first and the last ones, then
10246 // their order does not correspond to the link orientation
10247 if (( i1 == 1 && i2 == 2 ) ||
10248 ( i1 == 2 && i2 == 1 ))
10249 reverse[ iSide ] = !reverse[ iSide ];
10251 if ( reverse[0] == reverse[1] ) {
10252 //nReplaceMap.insert( TNodeNodeMap::value_type
10253 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10254 //nReplaceMap.insert( TNodeNodeMap::value_type
10255 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
10256 for(int nn=0; nn<nbNodes-2; nn++) {
10257 nReplaceMap.insert( TNodeNodeMap::value_type
10258 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
10262 //nReplaceMap.insert( TNodeNodeMap::value_type
10263 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
10264 //nReplaceMap.insert( TNodeNodeMap::value_type
10265 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
10266 for(int nn=0; nn<nbNodes-2; nn++) {
10267 nReplaceMap.insert( TNodeNodeMap::value_type
10268 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
10273 // add other links of the faces to linkList
10274 // -----------------------------------------
10276 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
10277 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10278 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
10279 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
10280 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10281 if ( !iter_isnew.second ) { // already in a set: no need to process
10282 linkIdSet.erase( iter_isnew.first );
10284 else // new in set == encountered for the first time: add
10286 //const SMDS_MeshNode* n1 = nodes[ iNode ];
10287 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
10288 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
10289 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
10290 linkList[0].push_back ( NLink( n1, n2 ));
10291 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10295 } // loop on link lists
10297 if ( aResult == SEW_OK &&
10298 ( linkIt[0] != linkList[0].end() ||
10299 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10300 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10301 " " << (faceSetPtr[1]->empty()));
10302 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10305 // ====================================================================
10306 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10307 // ====================================================================
10309 // delete temporary faces: they are in reverseElements of actual nodes
10310 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10311 // while ( tmpFaceIt->more() )
10312 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10313 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10314 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10315 // aMesh->RemoveElement(*tmpFaceIt);
10317 if ( aResult != SEW_OK)
10320 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10321 // loop on nodes replacement map
10322 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10323 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10324 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10325 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10326 nodeIDsToRemove.push_back( nToRemove->GetID() );
10327 // loop on elements sharing nToRemove
10328 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10329 while ( invElemIt->more() ) {
10330 const SMDS_MeshElement* e = invElemIt->next();
10331 // get a new suite of nodes: make replacement
10332 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10333 vector< const SMDS_MeshNode*> nodes( nbNodes );
10334 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10335 while ( nIt->more() ) {
10336 const SMDS_MeshNode* n =
10337 static_cast<const SMDS_MeshNode*>( nIt->next() );
10338 nnIt = nReplaceMap.find( n );
10339 if ( nnIt != nReplaceMap.end() ) {
10341 n = (*nnIt).second;
10345 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10346 // elemIDsToRemove.push_back( e->GetID() );
10350 SMDSAbs_ElementType etyp = e->GetType();
10351 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10354 myLastCreatedElems.Append(newElem);
10355 AddToSameGroups(newElem, e, aMesh);
10356 int aShapeId = e->getshapeId();
10359 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10362 aMesh->RemoveElement(e);
10367 Remove( nodeIDsToRemove, true );
10372 //================================================================================
10374 * \brief Find corresponding nodes in two sets of faces
10375 * \param theSide1 - first face set
10376 * \param theSide2 - second first face
10377 * \param theFirstNode1 - a boundary node of set 1
10378 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10379 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10380 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10381 * \param nReplaceMap - output map of corresponding nodes
10382 * \return bool - is a success or not
10384 //================================================================================
10387 //#define DEBUG_MATCHING_NODES
10390 SMESH_MeshEditor::Sew_Error
10391 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10392 set<const SMDS_MeshElement*>& theSide2,
10393 const SMDS_MeshNode* theFirstNode1,
10394 const SMDS_MeshNode* theFirstNode2,
10395 const SMDS_MeshNode* theSecondNode1,
10396 const SMDS_MeshNode* theSecondNode2,
10397 TNodeNodeMap & nReplaceMap)
10399 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10401 nReplaceMap.clear();
10402 if ( theFirstNode1 != theFirstNode2 )
10403 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10404 if ( theSecondNode1 != theSecondNode2 )
10405 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10407 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10408 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10410 list< NLink > linkList[2];
10411 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10412 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10414 // loop on links in linkList; find faces by links and append links
10415 // of the found faces to linkList
10416 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10417 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10418 NLink link[] = { *linkIt[0], *linkIt[1] };
10419 if ( linkSet.find( link[0] ) == linkSet.end() )
10422 // by links, find faces in the face sets,
10423 // and find indices of link nodes in the found faces;
10424 // in a face set, there is only one or no face sharing a link
10425 // ---------------------------------------------------------------
10427 const SMDS_MeshElement* face[] = { 0, 0 };
10428 list<const SMDS_MeshNode*> notLinkNodes[2];
10429 //bool reverse[] = { false, false }; // order of notLinkNodes
10431 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10433 const SMDS_MeshNode* n1 = link[iSide].first;
10434 const SMDS_MeshNode* n2 = link[iSide].second;
10435 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10436 set< const SMDS_MeshElement* > facesOfNode1;
10437 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10439 // during a loop of the first node, we find all faces around n1,
10440 // during a loop of the second node, we find one face sharing both n1 and n2
10441 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10442 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10443 while ( fIt->more() ) { // loop on faces sharing a node
10444 const SMDS_MeshElement* f = fIt->next();
10445 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10446 ! facesOfNode1.insert( f ).second ) // f encounters twice
10448 if ( face[ iSide ] ) {
10449 MESSAGE( "2 faces per link " );
10450 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10453 faceSet->erase( f );
10455 // get not link nodes
10456 int nbN = f->NbNodes();
10457 if ( f->IsQuadratic() )
10459 nbNodes[ iSide ] = nbN;
10460 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10461 int i1 = f->GetNodeIndex( n1 );
10462 int i2 = f->GetNodeIndex( n2 );
10463 int iEnd = nbN, iBeg = -1, iDelta = 1;
10464 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10466 std::swap( iEnd, iBeg ); iDelta = -1;
10471 if ( i == iEnd ) i = iBeg + iDelta;
10472 if ( i == i1 ) break;
10473 nodes.push_back ( f->GetNode( i ) );
10479 // check similarity of elements of the sides
10480 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10481 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10482 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10483 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10486 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10490 // set nodes to merge
10491 // -------------------
10493 if ( face[0] && face[1] ) {
10494 if ( nbNodes[0] != nbNodes[1] ) {
10495 MESSAGE("Diff nb of face nodes");
10496 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10498 #ifdef DEBUG_MATCHING_NODES
10499 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10500 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10501 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10503 int nbN = nbNodes[0];
10505 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10506 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10507 for ( int i = 0 ; i < nbN - 2; ++i ) {
10508 #ifdef DEBUG_MATCHING_NODES
10509 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10511 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10515 // add other links of the face 1 to linkList
10516 // -----------------------------------------
10518 const SMDS_MeshElement* f0 = face[0];
10519 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10520 for ( int i = 0; i < nbN; i++ )
10522 const SMDS_MeshNode* n2 = f0->GetNode( i );
10523 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10524 linkSet.insert( SMESH_TLink( n1, n2 ));
10525 if ( !iter_isnew.second ) { // already in a set: no need to process
10526 linkSet.erase( iter_isnew.first );
10528 else // new in set == encountered for the first time: add
10530 #ifdef DEBUG_MATCHING_NODES
10531 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10532 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10534 linkList[0].push_back ( NLink( n1, n2 ));
10535 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10540 } // loop on link lists
10545 //================================================================================
10547 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10548 \param theElems - the list of elements (edges or faces) to be replicated
10549 The nodes for duplication could be found from these elements
10550 \param theNodesNot - list of nodes to NOT replicate
10551 \param theAffectedElems - the list of elements (cells and edges) to which the
10552 replicated nodes should be associated to.
10553 \return TRUE if operation has been completed successfully, FALSE otherwise
10555 //================================================================================
10557 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10558 const TIDSortedElemSet& theNodesNot,
10559 const TIDSortedElemSet& theAffectedElems )
10561 myLastCreatedElems.Clear();
10562 myLastCreatedNodes.Clear();
10564 if ( theElems.size() == 0 )
10567 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10572 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10573 // duplicate elements and nodes
10574 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10575 // replce nodes by duplications
10576 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10580 //================================================================================
10582 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10583 \param theMeshDS - mesh instance
10584 \param theElems - the elements replicated or modified (nodes should be changed)
10585 \param theNodesNot - nodes to NOT replicate
10586 \param theNodeNodeMap - relation of old node to new created node
10587 \param theIsDoubleElem - flag os to replicate element or modify
10588 \return TRUE if operation has been completed successfully, FALSE otherwise
10590 //================================================================================
10592 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10593 const TIDSortedElemSet& theElems,
10594 const TIDSortedElemSet& theNodesNot,
10595 std::map< const SMDS_MeshNode*,
10596 const SMDS_MeshNode* >& theNodeNodeMap,
10597 const bool theIsDoubleElem )
10599 MESSAGE("doubleNodes");
10600 // iterate on through element and duplicate them (by nodes duplication)
10602 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10603 for ( ; elemItr != theElems.end(); ++elemItr )
10605 const SMDS_MeshElement* anElem = *elemItr;
10609 bool isDuplicate = false;
10610 // duplicate nodes to duplicate element
10611 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10612 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10614 while ( anIter->more() )
10617 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10618 SMDS_MeshNode* aNewNode = aCurrNode;
10619 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10620 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10621 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10624 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10625 theNodeNodeMap[ aCurrNode ] = aNewNode;
10626 myLastCreatedNodes.Append( aNewNode );
10628 isDuplicate |= (aCurrNode != aNewNode);
10629 newNodes[ ind++ ] = aNewNode;
10631 if ( !isDuplicate )
10634 if ( theIsDoubleElem )
10635 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10638 MESSAGE("ChangeElementNodes");
10639 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10646 //================================================================================
10648 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10649 \param theNodes - identifiers of nodes to be doubled
10650 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10651 nodes. If list of element identifiers is empty then nodes are doubled but
10652 they not assigned to elements
10653 \return TRUE if operation has been completed successfully, FALSE otherwise
10655 //================================================================================
10657 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10658 const std::list< int >& theListOfModifiedElems )
10660 MESSAGE("DoubleNodes");
10661 myLastCreatedElems.Clear();
10662 myLastCreatedNodes.Clear();
10664 if ( theListOfNodes.size() == 0 )
10667 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10671 // iterate through nodes and duplicate them
10673 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10675 std::list< int >::const_iterator aNodeIter;
10676 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10678 int aCurr = *aNodeIter;
10679 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10685 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10688 anOldNodeToNewNode[ aNode ] = aNewNode;
10689 myLastCreatedNodes.Append( aNewNode );
10693 // Create map of new nodes for modified elements
10695 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10697 std::list< int >::const_iterator anElemIter;
10698 for ( anElemIter = theListOfModifiedElems.begin();
10699 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10701 int aCurr = *anElemIter;
10702 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10706 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10708 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10710 while ( anIter->more() )
10712 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10713 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10715 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10716 aNodeArr[ ind++ ] = aNewNode;
10719 aNodeArr[ ind++ ] = aCurrNode;
10721 anElemToNodes[ anElem ] = aNodeArr;
10724 // Change nodes of elements
10726 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10727 anElemToNodesIter = anElemToNodes.begin();
10728 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10730 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10731 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10734 MESSAGE("ChangeElementNodes");
10735 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10744 //================================================================================
10746 \brief Check if element located inside shape
10747 \return TRUE if IN or ON shape, FALSE otherwise
10749 //================================================================================
10751 template<class Classifier>
10752 bool isInside(const SMDS_MeshElement* theElem,
10753 Classifier& theClassifier,
10754 const double theTol)
10756 gp_XYZ centerXYZ (0, 0, 0);
10757 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10758 while (aNodeItr->more())
10759 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10761 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10762 theClassifier.Perform(aPnt, theTol);
10763 TopAbs_State aState = theClassifier.State();
10764 return (aState == TopAbs_IN || aState == TopAbs_ON );
10767 //================================================================================
10769 * \brief Classifier of the 3D point on the TopoDS_Face
10770 * with interaface suitable for isInside()
10772 //================================================================================
10774 struct _FaceClassifier
10776 Extrema_ExtPS _extremum;
10777 BRepAdaptor_Surface _surface;
10778 TopAbs_State _state;
10780 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10782 _extremum.Initialize( _surface,
10783 _surface.FirstUParameter(), _surface.LastUParameter(),
10784 _surface.FirstVParameter(), _surface.LastVParameter(),
10785 _surface.Tolerance(), _surface.Tolerance() );
10787 void Perform(const gp_Pnt& aPnt, double theTol)
10789 _state = TopAbs_OUT;
10790 _extremum.Perform(aPnt);
10791 if ( _extremum.IsDone() )
10792 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10793 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10795 TopAbs_State State() const
10802 //================================================================================
10804 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10805 \param theElems - group of of elements (edges or faces) to be replicated
10806 \param theNodesNot - group of nodes not to replicate
10807 \param theShape - shape to detect affected elements (element which geometric center
10808 located on or inside shape).
10809 The replicated nodes should be associated to affected elements.
10810 \return TRUE if operation has been completed successfully, FALSE otherwise
10812 //================================================================================
10814 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10815 const TIDSortedElemSet& theNodesNot,
10816 const TopoDS_Shape& theShape )
10818 if ( theShape.IsNull() )
10821 const double aTol = Precision::Confusion();
10822 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10823 auto_ptr<_FaceClassifier> aFaceClassifier;
10824 if ( theShape.ShapeType() == TopAbs_SOLID )
10826 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10827 bsc3d->PerformInfinitePoint(aTol);
10829 else if (theShape.ShapeType() == TopAbs_FACE )
10831 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10834 // iterates on indicated elements and get elements by back references from their nodes
10835 TIDSortedElemSet anAffected;
10836 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10837 for ( ; elemItr != theElems.end(); ++elemItr )
10839 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10843 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10844 while ( nodeItr->more() )
10846 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10847 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10849 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10850 while ( backElemItr->more() )
10852 const SMDS_MeshElement* curElem = backElemItr->next();
10853 if ( curElem && theElems.find(curElem) == theElems.end() &&
10855 isInside( curElem, *bsc3d, aTol ) :
10856 isInside( curElem, *aFaceClassifier, aTol )))
10857 anAffected.insert( curElem );
10861 return DoubleNodes( theElems, theNodesNot, anAffected );
10865 * \brief compute an oriented angle between two planes defined by four points.
10866 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10867 * @param p0 base of the rotation axe
10868 * @param p1 extremity of the rotation axe
10869 * @param g1 belongs to the first plane
10870 * @param g2 belongs to the second plane
10872 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10874 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10875 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10876 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10877 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10878 gp_Vec vref(p0, p1);
10881 gp_Vec n1 = vref.Crossed(v1);
10882 gp_Vec n2 = vref.Crossed(v2);
10883 return n2.AngleWithRef(n1, vref);
10887 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10888 * The list of groups must describe a partition of the mesh volumes.
10889 * The nodes of the internal faces at the boundaries of the groups are doubled.
10890 * In option, the internal faces are replaced by flat elements.
10891 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10892 * The flat elements are stored in groups of volumes.
10893 * @param theElems - list of groups of volumes, where a group of volume is a set of
10894 * SMDS_MeshElements sorted by Id.
10895 * @param createJointElems - if TRUE, create the elements
10896 * @return TRUE if operation has been completed successfully, FALSE otherwise
10898 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10899 bool createJointElems)
10901 MESSAGE("----------------------------------------------");
10902 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10903 MESSAGE("----------------------------------------------");
10905 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10906 meshDS->BuildDownWardConnectivity(true);
10908 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10910 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10911 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10912 // build the list of nodes shared by 2 or more domains, with their domain indexes
10914 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10915 std::map<int,int>celldom; // cell vtkId --> domain
10916 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10917 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10918 faceDomains.clear();
10920 cellDomains.clear();
10921 nodeDomains.clear();
10922 std::map<int,int> emptyMap;
10923 std::set<int> emptySet;
10926 for (int idom = 0; idom < theElems.size(); idom++)
10929 // --- build a map (face to duplicate --> volume to modify)
10930 // with all the faces shared by 2 domains (group of elements)
10931 // and corresponding volume of this domain, for each shared face.
10932 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10934 const TIDSortedElemSet& domain = theElems[idom];
10935 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10936 for (; elemItr != domain.end(); ++elemItr)
10938 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10941 int vtkId = anElem->getVtkId();
10942 int neighborsVtkIds[NBMAXNEIGHBORS];
10943 int downIds[NBMAXNEIGHBORS];
10944 unsigned char downTypes[NBMAXNEIGHBORS];
10945 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10946 for (int n = 0; n < nbNeighbors; n++)
10948 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10949 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10950 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10952 DownIdType face(downIds[n], downTypes[n]);
10953 if (!faceDomains.count(face))
10954 faceDomains[face] = emptyMap; // create an empty entry for face
10955 if (!faceDomains[face].count(idom))
10957 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10958 celldom[vtkId] = idom;
10965 //MESSAGE("Number of shared faces " << faceDomains.size());
10966 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10968 // --- explore the shared faces domain by domain,
10969 // explore the nodes of the face and see if they belong to a cell in the domain,
10970 // which has only a node or an edge on the border (not a shared face)
10972 for (int idomain = 0; idomain < theElems.size(); idomain++)
10974 const TIDSortedElemSet& domain = theElems[idomain];
10975 itface = faceDomains.begin();
10976 for (; itface != faceDomains.end(); ++itface)
10978 std::map<int, int> domvol = itface->second;
10979 if (!domvol.count(idomain))
10981 DownIdType face = itface->first;
10982 //MESSAGE(" --- face " << face.cellId);
10983 std::set<int> oldNodes;
10985 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10986 std::set<int>::iterator itn = oldNodes.begin();
10987 for (; itn != oldNodes.end(); ++itn)
10990 //MESSAGE(" node " << oldId);
10991 std::set<int> cells;
10993 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10994 for (int i=0; i<l.ncells; i++)
10996 int vtkId = l.cells[i];
10997 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10998 if (!domain.count(anElem))
11000 int vtkType = grid->GetCellType(vtkId);
11001 int downId = grid->CellIdToDownId(vtkId);
11004 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11005 continue; // not OK at this stage of the algorithm:
11006 //no cells created after BuildDownWardConnectivity
11008 DownIdType aCell(downId, vtkType);
11009 if (celldom.count(vtkId))
11011 cellDomains[aCell][idomain] = vtkId;
11012 celldom[vtkId] = idomain;
11018 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11019 // for each shared face, get the nodes
11020 // for each node, for each domain of the face, create a clone of the node
11022 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11023 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11024 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11026 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11027 std::map<int, std::vector<int> > mutipleNodes; // nodes muti domains with domain order
11029 for (int idomain = 0; idomain < theElems.size(); idomain++)
11031 itface = faceDomains.begin();
11032 for (; itface != faceDomains.end(); ++itface)
11034 std::map<int, int> domvol = itface->second;
11035 if (!domvol.count(idomain))
11037 DownIdType face = itface->first;
11038 //MESSAGE(" --- face " << face.cellId);
11039 std::set<int> oldNodes;
11041 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11042 bool isMultipleDetected = false;
11043 std::set<int>::iterator itn = oldNodes.begin();
11044 for (; itn != oldNodes.end(); ++itn)
11047 //MESSAGE(" node " << oldId);
11048 if (!nodeDomains.count(oldId))
11049 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11050 if (nodeDomains[oldId].empty())
11051 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11052 std::map<int, int>::iterator itdom = domvol.begin();
11053 for (; itdom != domvol.end(); ++itdom)
11055 int idom = itdom->first;
11056 //MESSAGE(" domain " << idom);
11057 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11059 if (nodeDomains[oldId].size() >= 2) // a multiple node
11061 vector<int> orderedDoms;
11062 //MESSAGE("multiple node " << oldId);
11063 isMultipleDetected =true;
11064 if (mutipleNodes.count(oldId))
11065 orderedDoms = mutipleNodes[oldId];
11068 map<int,int>::iterator it = nodeDomains[oldId].begin();
11069 for (; it != nodeDomains[oldId].end(); ++it)
11070 orderedDoms.push_back(it->first);
11072 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11073 //stringstream txt;
11074 //for (int i=0; i<orderedDoms.size(); i++)
11075 // txt << orderedDoms[i] << " ";
11076 //MESSAGE("orderedDoms " << txt.str());
11077 mutipleNodes[oldId] = orderedDoms;
11079 double *coords = grid->GetPoint(oldId);
11080 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11081 int newId = newNode->getVtkId();
11082 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11083 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
11085 if (nodeDomains[oldId].size() >= 3)
11087 //MESSAGE("confirm multiple node " << oldId);
11088 isMultipleDetected =true;
11092 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
11094 //MESSAGE("multiple Nodes detected on a shared face");
11095 int downId = itface->first.cellId;
11096 unsigned char cellType = itface->first.cellType;
11097 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11098 const int *downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11099 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11100 for (int ie =0; ie < nbEdges; ie++)
11103 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11104 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11106 vector<int> vn0 = mutipleNodes[nodes[0]];
11107 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11108 sort( vn0.begin(), vn0.end() );
11109 sort( vn1.begin(), vn1.end() );
11112 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11113 double *coords = grid->GetPoint(nodes[0]);
11114 gp_Pnt p0(coords[0], coords[1], coords[2]);
11115 coords = grid->GetPoint(nodes[nbNodes - 1]);
11116 gp_Pnt p1(coords[0], coords[1], coords[2]);
11118 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11119 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11120 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11121 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11122 for (int id=0; id < vn0.size(); id++)
11124 int idom = vn0[id];
11125 for (int ivol=0; ivol<nbvol; ivol++)
11127 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11128 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11129 if (theElems[idom].count(elem))
11131 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11132 domvol[idom] = svol;
11133 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11135 vtkIdType npts = 0;
11136 vtkIdType* pts = 0;
11137 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11138 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11141 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11142 angleDom[idom] = 0;
11146 gp_Pnt g(values[0], values[1], values[2]);
11147 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11148 //MESSAGE(" angle=" << angleDom[idom]);
11154 map<double, int> sortedDom; // sort domains by angle
11155 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11156 sortedDom[ia->second] = ia->first;
11157 vector<int> vnodes;
11159 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11161 vdom.push_back(ib->second);
11162 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11164 for (int ino = 0; ino < nbNodes; ino++)
11165 vnodes.push_back(nodes[ino]);
11166 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11174 // --- iterate on shared faces (volumes to modify, face to extrude)
11175 // get node id's of the face (id SMDS = id VTK)
11176 // create flat element with old and new nodes if requested
11178 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11179 // (domain1 X domain2) = domain1 + MAXINT*domain2
11181 std::map<int, std::map<long,int> > nodeQuadDomains;
11182 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11184 if (createJointElems)
11186 itface = faceDomains.begin();
11187 for (; itface != faceDomains.end(); ++itface)
11189 DownIdType face = itface->first;
11190 std::set<int> oldNodes;
11191 std::set<int>::iterator itn;
11193 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11195 std::map<int, int> domvol = itface->second;
11196 std::map<int, int>::iterator itdom = domvol.begin();
11197 int dom1 = itdom->first;
11198 int vtkVolId = itdom->second;
11200 int dom2 = itdom->first;
11201 SMDS_MeshVolume *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11203 stringstream grpname;
11206 grpname << dom1 << "_" << dom2;
11208 grpname << dom2 << "_" << dom1;
11210 string namegrp = grpname.str();
11211 if (!mapOfJunctionGroups.count(namegrp))
11212 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11213 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11215 sgrp->Add(vol->GetID());
11219 // --- create volumes on multiple domain intersection if requested
11220 // iterate on edgesMultiDomains
11222 if (createJointElems)
11224 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11225 for (; ite != edgesMultiDomains.end(); ++ite)
11227 vector<int> nodes = ite->first;
11228 vector<int> orderDom = ite->second;
11229 vector<vtkIdType> orderedNodes;
11230 if (nodes.size() == 2)
11232 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11233 for (int ino=0; ino < nodes.size(); ino++)
11234 if (orderDom.size() == 3)
11235 for (int idom = 0; idom <orderDom.size(); idom++)
11236 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11238 for (int idom = orderDom.size()-1; idom >=0; idom--)
11239 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11240 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11242 stringstream grpname;
11244 grpname << 0 << "_" << 0;
11246 string namegrp = grpname.str();
11247 if (!mapOfJunctionGroups.count(namegrp))
11248 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11249 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11251 sgrp->Add(vol->GetID());
11255 //MESSAGE("Quadratic multiple joints not implemented");
11256 // TODO quadratic nodes
11261 // --- list the explicit faces and edges of the mesh that need to be modified,
11262 // i.e. faces and edges built with one or more duplicated nodes.
11263 // associate these faces or edges to their corresponding domain.
11264 // only the first domain found is kept when a face or edge is shared
11266 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11267 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11268 faceOrEdgeDom.clear();
11271 for (int idomain = 0; idomain < theElems.size(); idomain++)
11273 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11274 for (; itnod != nodeDomains.end(); ++itnod)
11276 int oldId = itnod->first;
11277 //MESSAGE(" node " << oldId);
11278 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11279 for (int i = 0; i < l.ncells; i++)
11281 int vtkId = l.cells[i];
11282 int vtkType = grid->GetCellType(vtkId);
11283 int downId = grid->CellIdToDownId(vtkId);
11285 continue; // new cells: not to be modified
11286 DownIdType aCell(downId, vtkType);
11287 int volParents[1000];
11288 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11289 for (int j = 0; j < nbvol; j++)
11290 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11291 if (!feDom.count(vtkId))
11293 feDom[vtkId] = idomain;
11294 faceOrEdgeDom[aCell] = emptyMap;
11295 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11296 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11297 // << " type " << vtkType << " downId " << downId);
11303 // --- iterate on shared faces (volumes to modify, face to extrude)
11304 // get node id's of the face
11305 // replace old nodes by new nodes in volumes, and update inverse connectivity
11307 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11308 for (int m=0; m<3; m++)
11310 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11311 itface = (*amap).begin();
11312 for (; itface != (*amap).end(); ++itface)
11314 DownIdType face = itface->first;
11315 std::set<int> oldNodes;
11316 std::set<int>::iterator itn;
11318 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11319 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11320 std::map<int, int> localClonedNodeIds;
11322 std::map<int, int> domvol = itface->second;
11323 std::map<int, int>::iterator itdom = domvol.begin();
11324 for (; itdom != domvol.end(); ++itdom)
11326 int idom = itdom->first;
11327 int vtkVolId = itdom->second;
11328 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11329 localClonedNodeIds.clear();
11330 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11333 if (nodeDomains[oldId].count(idom))
11335 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11336 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11339 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11344 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11345 grid->BuildLinks();
11353 * \brief Double nodes on some external faces and create flat elements.
11354 * Flat elements are mainly used by some types of mechanic calculations.
11356 * Each group of the list must be constituted of faces.
11357 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11358 * @param theElems - list of groups of faces, where a group of faces is a set of
11359 * SMDS_MeshElements sorted by Id.
11360 * @return TRUE if operation has been completed successfully, FALSE otherwise
11362 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11364 MESSAGE("-------------------------------------------------");
11365 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11366 MESSAGE("-------------------------------------------------");
11368 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11370 // --- For each group of faces
11371 // duplicate the nodes, create a flat element based on the face
11372 // replace the nodes of the faces by their clones
11374 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11375 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11376 clonedNodes.clear();
11377 intermediateNodes.clear();
11378 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11379 mapOfJunctionGroups.clear();
11381 for (int idom = 0; idom < theElems.size(); idom++)
11383 const TIDSortedElemSet& domain = theElems[idom];
11384 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11385 for (; elemItr != domain.end(); ++elemItr)
11387 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11388 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11391 // MESSAGE("aFace=" << aFace->GetID());
11392 bool isQuad = aFace->IsQuadratic();
11393 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11395 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11397 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11398 while (nodeIt->more())
11400 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11401 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11403 ln2.push_back(node);
11405 ln0.push_back(node);
11407 const SMDS_MeshNode* clone = 0;
11408 if (!clonedNodes.count(node))
11410 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11411 clonedNodes[node] = clone;
11414 clone = clonedNodes[node];
11417 ln3.push_back(clone);
11419 ln1.push_back(clone);
11421 const SMDS_MeshNode* inter = 0;
11422 if (isQuad && (!isMedium))
11424 if (!intermediateNodes.count(node))
11426 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11427 intermediateNodes[node] = inter;
11430 inter = intermediateNodes[node];
11431 ln4.push_back(inter);
11435 // --- extrude the face
11437 vector<const SMDS_MeshNode*> ln;
11438 SMDS_MeshVolume* vol = 0;
11439 vtkIdType aType = aFace->GetVtkType();
11443 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11444 // MESSAGE("vol prism " << vol->GetID());
11445 ln.push_back(ln1[0]);
11446 ln.push_back(ln1[1]);
11447 ln.push_back(ln1[2]);
11450 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11451 // MESSAGE("vol hexa " << vol->GetID());
11452 ln.push_back(ln1[0]);
11453 ln.push_back(ln1[1]);
11454 ln.push_back(ln1[2]);
11455 ln.push_back(ln1[3]);
11457 case VTK_QUADRATIC_TRIANGLE:
11458 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11459 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11460 // MESSAGE("vol quad prism " << vol->GetID());
11461 ln.push_back(ln1[0]);
11462 ln.push_back(ln1[1]);
11463 ln.push_back(ln1[2]);
11464 ln.push_back(ln3[0]);
11465 ln.push_back(ln3[1]);
11466 ln.push_back(ln3[2]);
11468 case VTK_QUADRATIC_QUAD:
11469 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11470 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11471 // ln4[0], ln4[1], ln4[2], ln4[3]);
11472 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11473 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11474 ln4[0], ln4[1], ln4[2], ln4[3]);
11475 // MESSAGE("vol quad hexa " << vol->GetID());
11476 ln.push_back(ln1[0]);
11477 ln.push_back(ln1[1]);
11478 ln.push_back(ln1[2]);
11479 ln.push_back(ln1[3]);
11480 ln.push_back(ln3[0]);
11481 ln.push_back(ln3[1]);
11482 ln.push_back(ln3[2]);
11483 ln.push_back(ln3[3]);
11493 stringstream grpname;
11497 string namegrp = grpname.str();
11498 if (!mapOfJunctionGroups.count(namegrp))
11499 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11500 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11502 sgrp->Add(vol->GetID());
11505 // --- modify the face
11507 aFace->ChangeNodes(&ln[0], ln.size());
11513 //================================================================================
11515 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11516 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11517 * \return TRUE if operation has been completed successfully, FALSE otherwise
11519 //================================================================================
11521 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11523 // iterates on volume elements and detect all free faces on them
11524 SMESHDS_Mesh* aMesh = GetMeshDS();
11527 //bool res = false;
11528 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11529 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11532 const SMDS_MeshVolume* volume = vIt->next();
11533 SMDS_VolumeTool vTool( volume );
11534 vTool.SetExternalNormal();
11535 const bool isPoly = volume->IsPoly();
11536 const bool isQuad = volume->IsQuadratic();
11537 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11539 if (!vTool.IsFreeFace(iface))
11542 vector<const SMDS_MeshNode *> nodes;
11543 int nbFaceNodes = vTool.NbFaceNodes(iface);
11544 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11546 for ( ; inode < nbFaceNodes; inode += isQuad ? 2 : 1)
11547 nodes.push_back(faceNodes[inode]);
11549 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11550 nodes.push_back(faceNodes[inode]);
11552 // add new face based on volume nodes
11553 if (aMesh->FindFace( nodes ) ) {
11555 continue; // face already exsist
11557 AddElement(nodes, SMDSAbs_Face, isPoly && iface == 1);
11561 return ( nbFree==(nbExisted+nbCreated) );
11566 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11568 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11570 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11573 //================================================================================
11575 * \brief Creates missing boundary elements
11576 * \param elements - elements whose boundary is to be checked
11577 * \param dimension - defines type of boundary elements to create
11578 * \param group - a group to store created boundary elements in
11579 * \param targetMesh - a mesh to store created boundary elements in
11580 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11581 * \param toCopyExistingBondary - if true, not only new but also pre-existing
11582 * boundary elements will be copied into the targetMesh
11583 * \param toAddExistingBondary - if true, not only new but also pre-existing
11584 * boundary elements will be added into the new group
11585 * \param aroundElements - if true, elements will be created on boundary of given
11586 * elements else, on boundary of the whole mesh. This
11587 * option works for 2D elements only.
11588 * \return nb of added boundary elements
11590 //================================================================================
11592 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11593 Bnd_Dimension dimension,
11594 SMESH_Group* group/*=0*/,
11595 SMESH_Mesh* targetMesh/*=0*/,
11596 bool toCopyElements/*=false*/,
11597 bool toCopyExistingBondary/*=false*/,
11598 bool toAddExistingBondary/*= false*/,
11599 bool aroundElements/*= false*/)
11601 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11602 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11603 // hope that all elements are of the same type, do not check them all
11604 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11605 throw SALOME_Exception(LOCALIZED("wrong element type"));
11607 if ( aroundElements && elemType == SMDSAbs_Volume )
11608 throw SALOME_Exception(LOCALIZED("wrong element type for aroundElements==true"));
11611 toCopyElements = toCopyExistingBondary = false;
11613 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11614 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11615 int nbAddedBnd = 0;
11617 // editor adding present bnd elements and optionally holding elements to add to the group
11618 SMESH_MeshEditor* presentEditor;
11619 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11620 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11622 SMDS_VolumeTool vTool;
11623 TIDSortedElemSet avoidSet;
11624 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11627 typedef vector<const SMDS_MeshNode*> TConnectivity;
11629 SMDS_ElemIteratorPtr eIt;
11630 if (elements.empty())
11631 eIt = aMesh->elementsIterator(elemType);
11633 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11635 while (eIt->more())
11637 const SMDS_MeshElement* elem = eIt->next();
11638 const int iQuad = elem->IsQuadratic();
11640 // ------------------------------------------------------------------------------------
11641 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11642 // ------------------------------------------------------------------------------------
11643 vector<const SMDS_MeshElement*> presentBndElems;
11644 vector<TConnectivity> missingBndElems;
11645 TConnectivity nodes;
11646 if ( vTool.Set(elem) ) // elem is a volume ------------------------------------------
11648 vTool.SetExternalNormal();
11649 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11651 if (!vTool.IsFreeFace(iface))
11653 int nbFaceNodes = vTool.NbFaceNodes(iface);
11654 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11655 if ( missType == SMDSAbs_Edge ) // boundary edges
11657 nodes.resize( 2+iQuad );
11658 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11660 for ( int j = 0; j < nodes.size(); ++j )
11662 if ( const SMDS_MeshElement* edge =
11663 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/0))
11664 presentBndElems.push_back( edge );
11666 missingBndElems.push_back( nodes );
11669 else // boundary face
11672 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11673 nodes.push_back( nn[inode] );
11675 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11676 nodes.push_back( nn[inode] );
11678 if (const SMDS_MeshFace * f = aMesh->FindFace( nodes ) )
11679 presentBndElems.push_back( f );
11681 missingBndElems.push_back( nodes );
11685 else // elem is a face ------------------------------------------
11687 avoidSet.clear(), avoidSet.insert( elem );
11688 int nbNodes = elem->NbCornerNodes();
11689 nodes.resize( 2 /*+ iQuad*/);
11690 for ( int i = 0; i < nbNodes; i++ )
11692 nodes[0] = elem->GetNode(i);
11693 nodes[1] = elem->GetNode((i+1)%nbNodes);
11694 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11695 continue; // not free link
11698 //nodes[2] = elem->GetNode( i + nbNodes );
11699 if ( const SMDS_MeshElement* edge =
11700 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11701 presentBndElems.push_back( edge );
11703 missingBndElems.push_back( nodes );
11707 // ---------------------------------
11708 // 2. Add missing boundary elements
11709 // ---------------------------------
11710 if ( targetMesh != myMesh )
11711 // instead of making a map of nodes in this mesh and targetMesh,
11712 // we create nodes with same IDs. We can renumber them later, if needed
11713 for ( int i = 0; i < missingBndElems.size(); ++i )
11715 TConnectivity& srcNodes = missingBndElems[i];
11716 TConnectivity nodes( srcNodes.size() );
11717 for ( inode = 0; inode < nodes.size(); ++inode )
11718 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11719 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11721 /*noMedium=*/true))
11723 tgtEditor.AddElement(nodes, missType, elem->IsPoly() && nodes.size()/(iQuad+1)>4);
11727 for ( int i = 0; i < missingBndElems.size(); ++i )
11729 TConnectivity& nodes = missingBndElems[i];
11730 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11732 /*noMedium=*/true))
11734 tgtEditor.AddElement(nodes, missType, elem->IsPoly() && nodes.size()/(iQuad+1)>4);
11738 // ----------------------------------
11739 // 3. Copy present boundary elements
11740 // ----------------------------------
11741 if ( toCopyExistingBondary )
11742 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11744 const SMDS_MeshElement* e = presentBndElems[i];
11745 TConnectivity nodes( e->NbNodes() );
11746 for ( inode = 0; inode < nodes.size(); ++inode )
11747 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11748 presentEditor->AddElement(nodes, missType, e->IsPoly());
11750 else // store present elements to add them to a group
11751 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11753 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11756 } // loop on given elements
11758 // ---------------------------------------------
11759 // 4. Fill group with boundary elements
11760 // ---------------------------------------------
11763 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11764 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11765 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11767 tgtEditor.myLastCreatedElems.Clear();
11768 tgtEditor2.myLastCreatedElems.Clear();
11770 // -----------------------
11771 // 5. Copy given elements
11772 // -----------------------
11773 if ( toCopyElements && targetMesh != myMesh )
11775 if (elements.empty())
11776 eIt = aMesh->elementsIterator(elemType);
11778 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11779 while (eIt->more())
11781 const SMDS_MeshElement* elem = eIt->next();
11782 TConnectivity nodes( elem->NbNodes() );
11783 for ( inode = 0; inode < nodes.size(); ++inode )
11784 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11785 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11787 tgtEditor.myLastCreatedElems.Clear();