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
22 // SMESH SMESH : idl implementation based on 'SMESH' unit's classes
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
28 #include "SMESH_MeshEditor.hxx"
30 #include "SMDS_FaceOfNodes.hxx"
31 #include "SMDS_VolumeTool.hxx"
32 #include "SMDS_EdgePosition.hxx"
33 #include "SMDS_PolyhedralVolumeOfNodes.hxx"
34 #include "SMDS_FacePosition.hxx"
35 #include "SMDS_SpacePosition.hxx"
36 //#include "SMDS_QuadraticFaceOfNodes.hxx"
37 #include "SMDS_MeshGroup.hxx"
38 #include "SMDS_LinearEdge.hxx"
39 #include "SMDS_Downward.hxx"
40 #include "SMDS_SetIterator.hxx"
42 #include "SMESHDS_Group.hxx"
43 #include "SMESHDS_Mesh.hxx"
45 #include "SMESH_Algo.hxx"
46 #include "SMESH_ControlsDef.hxx"
47 #include "SMESH_Group.hxx"
48 #include "SMESH_MesherHelper.hxx"
49 #include "SMESH_OctreeNode.hxx"
50 #include "SMESH_subMesh.hxx"
52 #include <Basics_OCCTVersion.hxx>
54 #include "utilities.h"
56 #include <BRepAdaptor_Surface.hxx>
57 #include <BRepBuilderAPI_MakeEdge.hxx>
58 #include <BRepClass3d_SolidClassifier.hxx>
59 #include <BRep_Tool.hxx>
61 #include <Extrema_GenExtPS.hxx>
62 #include <Extrema_POnCurv.hxx>
63 #include <Extrema_POnSurf.hxx>
64 #include <GC_MakeSegment.hxx>
65 #include <Geom2d_Curve.hxx>
66 #include <GeomAPI_ExtremaCurveCurve.hxx>
67 #include <GeomAdaptor_Surface.hxx>
68 #include <Geom_Curve.hxx>
69 #include <Geom_Line.hxx>
70 #include <Geom_Surface.hxx>
71 #include <IntAna_IntConicQuad.hxx>
72 #include <IntAna_Quadric.hxx>
73 #include <Precision.hxx>
74 #include <TColStd_ListOfInteger.hxx>
75 #include <TopAbs_State.hxx>
77 #include <TopExp_Explorer.hxx>
78 #include <TopTools_ListIteratorOfListOfShape.hxx>
79 #include <TopTools_ListOfShape.hxx>
80 #include <TopTools_SequenceOfShape.hxx>
82 #include <TopoDS_Face.hxx>
88 #include <gp_Trsf.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
107 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
110 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
112 //=======================================================================
113 //function : SMESH_MeshEditor
115 //=======================================================================
117 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
118 :myMesh( theMesh ) // theMesh may be NULL
122 //=======================================================================
126 //=======================================================================
129 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
130 const SMDSAbs_ElementType type,
134 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
135 SMDS_MeshElement* e = 0;
136 int nbnode = node.size();
137 SMESHDS_Mesh* mesh = GetMeshDS();
142 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
143 else e = mesh->AddFace (node[0], node[1], node[2] );
145 else if (nbnode == 4) {
146 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
147 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
149 else if (nbnode == 6) {
150 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
151 node[4], node[5], ID);
152 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
155 else if (nbnode == 8) {
156 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
157 node[4], node[5], node[6], node[7], ID);
158 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
159 node[4], node[5], node[6], node[7] );
161 else if (nbnode == 9) {
162 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
163 node[4], node[5], node[6], node[7], node[8], ID);
164 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
165 node[4], node[5], node[6], node[7], node[8] );
168 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
169 else e = mesh->AddPolygonalFace (node );
176 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
177 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
179 else if (nbnode == 5) {
180 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
182 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
185 else if (nbnode == 6) {
186 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
187 node[4], node[5], ID);
188 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
191 else if (nbnode == 8) {
192 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
193 node[4], node[5], node[6], node[7], ID);
194 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
195 node[4], node[5], node[6], node[7] );
197 else if (nbnode == 10) {
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], ID);
201 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
202 node[4], node[5], node[6], node[7],
205 else if (nbnode == 12) {
206 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207 node[4], node[5], node[6], node[7],
208 node[8], node[9], node[10], node[11], ID);
209 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], node[7],
211 node[8], node[9], node[10], node[11] );
213 else if (nbnode == 13) {
214 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7],
216 node[8], node[9], node[10],node[11],
218 else e = mesh->AddVolume (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],
223 else if (nbnode == 15) {
224 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
226 node[8], node[9], node[10],node[11],
227 node[12],node[13],node[14],ID);
228 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
229 node[4], node[5], node[6], node[7],
230 node[8], node[9], node[10],node[11],
231 node[12],node[13],node[14] );
233 else if (nbnode == 20) {
234 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
235 node[4], node[5], node[6], node[7],
236 node[8], node[9], node[10],node[11],
237 node[12],node[13],node[14],node[15],
238 node[16],node[17],node[18],node[19],ID);
239 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
240 node[4], node[5], node[6], node[7],
241 node[8], node[9], node[10],node[11],
242 node[12],node[13],node[14],node[15],
243 node[16],node[17],node[18],node[19] );
245 else if (nbnode == 27) {
246 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 node[4], node[5], node[6], node[7],
248 node[8], node[9], node[10],node[11],
249 node[12],node[13],node[14],node[15],
250 node[16],node[17],node[18],node[19],
251 node[20],node[21],node[22],node[23],
252 node[24],node[25],node[26], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
254 node[4], node[5], node[6], node[7],
255 node[8], node[9], node[10],node[11],
256 node[12],node[13],node[14],node[15],
257 node[16],node[17],node[18],node[19],
258 node[20],node[21],node[22],node[23],
259 node[24],node[25],node[26] );
266 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
267 else e = mesh->AddEdge (node[0], node[1] );
269 else if ( nbnode == 3 ) {
270 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
271 else e = mesh->AddEdge (node[0], node[1], node[2] );
275 case SMDSAbs_0DElement:
277 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
278 else e = mesh->Add0DElement (node[0] );
283 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
284 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
289 if ( e ) myLastCreatedElems.Append( e );
293 //=======================================================================
297 //=======================================================================
299 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
300 const SMDSAbs_ElementType type,
304 vector<const SMDS_MeshNode*> nodes;
305 nodes.reserve( nodeIDs.size() );
306 vector<int>::const_iterator id = nodeIDs.begin();
307 while ( id != nodeIDs.end() ) {
308 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
309 nodes.push_back( node );
313 return AddElement( nodes, type, isPoly, ID );
316 //=======================================================================
318 //purpose : Remove a node or an element.
319 // Modify a compute state of sub-meshes which become empty
320 //=======================================================================
322 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
325 myLastCreatedElems.Clear();
326 myLastCreatedNodes.Clear();
328 SMESHDS_Mesh* aMesh = GetMeshDS();
329 set< SMESH_subMesh *> smmap;
332 list<int>::const_iterator it = theIDs.begin();
333 for ( ; it != theIDs.end(); it++ ) {
334 const SMDS_MeshElement * elem;
336 elem = aMesh->FindNode( *it );
338 elem = aMesh->FindElement( *it );
342 // Notify VERTEX sub-meshes about modification
344 const SMDS_MeshNode* node = cast2Node( elem );
345 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
346 if ( int aShapeID = node->getshapeId() )
347 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
350 // Find sub-meshes to notify about modification
351 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
352 // while ( nodeIt->more() ) {
353 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
354 // const SMDS_PositionPtr& aPosition = node->GetPosition();
355 // if ( aPosition.get() ) {
356 // if ( int aShapeID = aPosition->GetShapeId() ) {
357 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
358 // smmap.insert( sm );
365 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
367 aMesh->RemoveElement( elem );
371 // Notify sub-meshes about modification
372 if ( !smmap.empty() ) {
373 set< SMESH_subMesh *>::iterator smIt;
374 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
375 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
378 // // Check if the whole mesh becomes empty
379 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
380 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
385 //=======================================================================
386 //function : FindShape
387 //purpose : Return an index of the shape theElem is on
388 // or zero if a shape not found
389 //=======================================================================
391 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
393 myLastCreatedElems.Clear();
394 myLastCreatedNodes.Clear();
396 SMESHDS_Mesh * aMesh = GetMeshDS();
397 if ( aMesh->ShapeToMesh().IsNull() )
400 int aShapeID = theElem->getshapeId();
404 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
405 if ( sm->Contains( theElem ))
408 if ( theElem->GetType() == SMDSAbs_Node ) {
409 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
412 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
415 TopoDS_Shape aShape; // the shape a node of theElem is on
416 if ( theElem->GetType() != SMDSAbs_Node )
418 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
419 while ( nodeIt->more() ) {
420 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
421 if ((aShapeID = node->getshapeId()) > 0) {
422 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
423 if ( sm->Contains( theElem ))
425 if ( aShape.IsNull() )
426 aShape = aMesh->IndexToShape( aShapeID );
432 // None of nodes is on a proper shape,
433 // find the shape among ancestors of aShape on which a node is
434 if ( !aShape.IsNull() ) {
435 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
436 for ( ; ancIt.More(); ancIt.Next() ) {
437 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
438 if ( sm && sm->Contains( theElem ))
439 return aMesh->ShapeToIndex( ancIt.Value() );
444 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
445 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
446 for ( ; id_sm != id2sm.end(); ++id_sm )
447 if ( id_sm->second->Contains( theElem ))
451 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
455 //=======================================================================
456 //function : IsMedium
458 //=======================================================================
460 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
461 const SMDSAbs_ElementType typeToCheck)
463 bool isMedium = false;
464 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
465 while (it->more() && !isMedium ) {
466 const SMDS_MeshElement* elem = it->next();
467 isMedium = elem->IsMediumNode(node);
472 //=======================================================================
473 //function : ShiftNodesQuadTria
475 // Shift nodes in the array corresponded to quadratic triangle
476 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
477 //=======================================================================
478 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
480 const SMDS_MeshNode* nd1 = aNodes[0];
481 aNodes[0] = aNodes[1];
482 aNodes[1] = aNodes[2];
484 const SMDS_MeshNode* nd2 = aNodes[3];
485 aNodes[3] = aNodes[4];
486 aNodes[4] = aNodes[5];
490 //=======================================================================
491 //function : edgeConnectivity
493 // return number of the edges connected with the theNode.
494 // if theEdges has connections with the other type of the
495 // elements, return -1
496 //=======================================================================
497 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
499 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
501 while(elemIt->more()) {
509 //=======================================================================
510 //function : GetNodesFromTwoTria
512 // Shift nodes in the array corresponded to quadratic triangle
513 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
514 //=======================================================================
515 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
516 const SMDS_MeshElement * theTria2,
517 const SMDS_MeshNode* N1[],
518 const SMDS_MeshNode* N2[])
520 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
523 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
526 if(it->more()) return false;
527 it = theTria2->nodesIterator();
530 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
533 if(it->more()) return false;
535 int sames[3] = {-1,-1,-1};
547 if(nbsames!=2) return false;
549 ShiftNodesQuadTria(N1);
551 ShiftNodesQuadTria(N1);
554 i = sames[0] + sames[1] + sames[2];
556 ShiftNodesQuadTria(N2);
558 // now we receive following N1 and N2 (using numeration as above image)
559 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
560 // i.e. first nodes from both arrays determ new diagonal
564 //=======================================================================
565 //function : InverseDiag
566 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
567 // but having other common link.
568 // Return False if args are improper
569 //=======================================================================
571 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
572 const SMDS_MeshElement * theTria2 )
574 MESSAGE("InverseDiag");
575 myLastCreatedElems.Clear();
576 myLastCreatedNodes.Clear();
578 if (!theTria1 || !theTria2)
581 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
582 if (!F1) return false;
583 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
584 if (!F2) return false;
585 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
586 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
588 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
589 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
593 // put nodes in array and find out indices of the same ones
594 const SMDS_MeshNode* aNodes [6];
595 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
597 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
598 while ( it->more() ) {
599 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
601 if ( i > 2 ) // theTria2
602 // find same node of theTria1
603 for ( int j = 0; j < 3; j++ )
604 if ( aNodes[ i ] == aNodes[ j ]) {
613 return false; // theTria1 is not a triangle
614 it = theTria2->nodesIterator();
616 if ( i == 6 && it->more() )
617 return false; // theTria2 is not a triangle
620 // find indices of 1,2 and of A,B in theTria1
621 int iA = 0, iB = 0, i1 = 0, i2 = 0;
622 for ( i = 0; i < 6; i++ ) {
623 if ( sameInd [ i ] == 0 ) {
632 // nodes 1 and 2 should not be the same
633 if ( aNodes[ i1 ] == aNodes[ i2 ] )
637 aNodes[ iA ] = aNodes[ i2 ];
639 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
641 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
642 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
646 } // end if(F1 && F2)
648 // check case of quadratic faces
649 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
651 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
655 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
656 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
664 const SMDS_MeshNode* N1 [6];
665 const SMDS_MeshNode* N2 [6];
666 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
668 // now we receive following N1 and N2 (using numeration as above image)
669 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
670 // i.e. first nodes from both arrays determ new diagonal
672 const SMDS_MeshNode* N1new [6];
673 const SMDS_MeshNode* N2new [6];
686 // replaces nodes in faces
687 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
688 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
693 //=======================================================================
694 //function : findTriangles
695 //purpose : find triangles sharing theNode1-theNode2 link
696 //=======================================================================
698 static bool findTriangles(const SMDS_MeshNode * theNode1,
699 const SMDS_MeshNode * theNode2,
700 const SMDS_MeshElement*& theTria1,
701 const SMDS_MeshElement*& theTria2)
703 if ( !theNode1 || !theNode2 ) return false;
705 theTria1 = theTria2 = 0;
707 set< const SMDS_MeshElement* > emap;
708 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
710 const SMDS_MeshElement* elem = it->next();
711 if ( elem->NbNodes() == 3 )
714 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
716 const SMDS_MeshElement* elem = it->next();
717 if ( emap.find( elem ) != emap.end() ) {
719 // theTria1 must be element with minimum ID
720 if( theTria1->GetID() < elem->GetID() ) {
734 return ( theTria1 && theTria2 );
737 //=======================================================================
738 //function : InverseDiag
739 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
740 // with ones built on the same 4 nodes but having other common link.
741 // Return false if proper faces not found
742 //=======================================================================
744 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
745 const SMDS_MeshNode * theNode2)
747 myLastCreatedElems.Clear();
748 myLastCreatedNodes.Clear();
750 MESSAGE( "::InverseDiag()" );
752 const SMDS_MeshElement *tr1, *tr2;
753 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
756 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
757 if (!F1) return false;
758 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
759 if (!F2) return false;
760 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
761 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
763 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
764 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
768 // put nodes in array
769 // and find indices of 1,2 and of A in tr1 and of B in tr2
770 int i, iA1 = 0, i1 = 0;
771 const SMDS_MeshNode* aNodes1 [3];
772 SMDS_ElemIteratorPtr it;
773 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
774 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
775 if ( aNodes1[ i ] == theNode1 )
776 iA1 = i; // node A in tr1
777 else if ( aNodes1[ i ] != theNode2 )
781 const SMDS_MeshNode* aNodes2 [3];
782 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
783 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
784 if ( aNodes2[ i ] == theNode2 )
785 iB2 = i; // node B in tr2
786 else if ( aNodes2[ i ] != theNode1 )
790 // nodes 1 and 2 should not be the same
791 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
795 aNodes1[ iA1 ] = aNodes2[ i2 ];
797 aNodes2[ iB2 ] = aNodes1[ i1 ];
799 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
800 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
805 // check case of quadratic faces
806 return InverseDiag(tr1,tr2);
809 //=======================================================================
810 //function : getQuadrangleNodes
811 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
812 // fusion of triangles tr1 and tr2 having shared link on
813 // theNode1 and theNode2
814 //=======================================================================
816 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
817 const SMDS_MeshNode * theNode1,
818 const SMDS_MeshNode * theNode2,
819 const SMDS_MeshElement * tr1,
820 const SMDS_MeshElement * tr2 )
822 if( tr1->NbNodes() != tr2->NbNodes() )
824 // find the 4-th node to insert into tr1
825 const SMDS_MeshNode* n4 = 0;
826 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
828 while ( !n4 && i<3 ) {
829 const SMDS_MeshNode * n = cast2Node( it->next() );
831 bool isDiag = ( n == theNode1 || n == theNode2 );
835 // Make an array of nodes to be in a quadrangle
836 int iNode = 0, iFirstDiag = -1;
837 it = tr1->nodesIterator();
840 const SMDS_MeshNode * n = cast2Node( it->next() );
842 bool isDiag = ( n == theNode1 || n == theNode2 );
844 if ( iFirstDiag < 0 )
846 else if ( iNode - iFirstDiag == 1 )
847 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
849 else if ( n == n4 ) {
850 return false; // tr1 and tr2 should not have all the same nodes
852 theQuadNodes[ iNode++ ] = n;
854 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
855 theQuadNodes[ iNode ] = n4;
860 //=======================================================================
861 //function : DeleteDiag
862 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
863 // with a quadrangle built on the same 4 nodes.
864 // Return false if proper faces not found
865 //=======================================================================
867 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
868 const SMDS_MeshNode * theNode2)
870 myLastCreatedElems.Clear();
871 myLastCreatedNodes.Clear();
873 MESSAGE( "::DeleteDiag()" );
875 const SMDS_MeshElement *tr1, *tr2;
876 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
879 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
880 if (!F1) return false;
881 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
882 if (!F2) return false;
883 SMESHDS_Mesh * aMesh = GetMeshDS();
885 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
886 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
888 const SMDS_MeshNode* aNodes [ 4 ];
889 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
892 const SMDS_MeshElement* newElem = 0;
893 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
894 myLastCreatedElems.Append(newElem);
895 AddToSameGroups( newElem, tr1, aMesh );
896 int aShapeId = tr1->getshapeId();
899 aMesh->SetMeshElementOnShape( newElem, aShapeId );
901 aMesh->RemoveElement( tr1 );
902 aMesh->RemoveElement( tr2 );
907 // check case of quadratic faces
908 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
910 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
914 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
915 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
923 const SMDS_MeshNode* N1 [6];
924 const SMDS_MeshNode* N2 [6];
925 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
927 // now we receive following N1 and N2 (using numeration as above image)
928 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
929 // i.e. first nodes from both arrays determ new diagonal
931 const SMDS_MeshNode* aNodes[8];
941 const SMDS_MeshElement* newElem = 0;
942 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
943 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
944 myLastCreatedElems.Append(newElem);
945 AddToSameGroups( newElem, tr1, aMesh );
946 int aShapeId = tr1->getshapeId();
949 aMesh->SetMeshElementOnShape( newElem, aShapeId );
951 aMesh->RemoveElement( tr1 );
952 aMesh->RemoveElement( tr2 );
954 // remove middle node (9)
955 GetMeshDS()->RemoveNode( N1[4] );
960 //=======================================================================
961 //function : Reorient
962 //purpose : Reverse theElement orientation
963 //=======================================================================
965 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
968 myLastCreatedElems.Clear();
969 myLastCreatedNodes.Clear();
973 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
974 if ( !it || !it->more() )
977 switch ( theElem->GetType() ) {
981 if(!theElem->IsQuadratic()) {
982 int i = theElem->NbNodes();
983 vector<const SMDS_MeshNode*> aNodes( i );
985 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
986 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
989 // quadratic elements
990 if(theElem->GetType()==SMDSAbs_Edge) {
991 vector<const SMDS_MeshNode*> aNodes(3);
992 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
993 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
994 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
995 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
998 int nbn = theElem->NbNodes();
999 vector<const SMDS_MeshNode*> aNodes(nbn);
1000 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1002 for(; i<nbn/2; i++) {
1003 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1005 for(i=0; i<nbn/2; i++) {
1006 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1008 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1012 case SMDSAbs_Volume: {
1013 if (theElem->IsPoly()) {
1014 // TODO reorient vtk polyhedron
1015 MESSAGE("reorient vtk polyhedron ?");
1016 const SMDS_VtkVolume* aPolyedre =
1017 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1019 MESSAGE("Warning: bad volumic element");
1023 int nbFaces = aPolyedre->NbFaces();
1024 vector<const SMDS_MeshNode *> poly_nodes;
1025 vector<int> quantities (nbFaces);
1027 // reverse each face of the polyedre
1028 for (int iface = 1; iface <= nbFaces; iface++) {
1029 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1030 quantities[iface - 1] = nbFaceNodes;
1032 for (inode = nbFaceNodes; inode >= 1; inode--) {
1033 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1034 poly_nodes.push_back(curNode);
1038 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1042 SMDS_VolumeTool vTool;
1043 if ( !vTool.Set( theElem ))
1046 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1047 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1056 //=======================================================================
1057 //function : getBadRate
1059 //=======================================================================
1061 static double getBadRate (const SMDS_MeshElement* theElem,
1062 SMESH::Controls::NumericalFunctorPtr& theCrit)
1064 SMESH::Controls::TSequenceOfXYZ P;
1065 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1067 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1068 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1071 //=======================================================================
1072 //function : QuadToTri
1073 //purpose : Cut quadrangles into triangles.
1074 // theCrit is used to select a diagonal to cut
1075 //=======================================================================
1077 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1078 SMESH::Controls::NumericalFunctorPtr theCrit)
1080 myLastCreatedElems.Clear();
1081 myLastCreatedNodes.Clear();
1083 MESSAGE( "::QuadToTri()" );
1085 if ( !theCrit.get() )
1088 SMESHDS_Mesh * aMesh = GetMeshDS();
1090 Handle(Geom_Surface) surface;
1091 SMESH_MesherHelper helper( *GetMesh() );
1093 TIDSortedElemSet::iterator itElem;
1094 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1095 const SMDS_MeshElement* elem = *itElem;
1096 if ( !elem || elem->GetType() != SMDSAbs_Face )
1098 if ( elem->NbCornerNodes() != 4 )
1101 // retrieve element nodes
1102 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1104 // compare two sets of possible triangles
1105 double aBadRate1, aBadRate2; // to what extent a set is bad
1106 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1107 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1108 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1110 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1111 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1112 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1114 int aShapeId = FindShape( elem );
1115 const SMDS_MeshElement* newElem1 = 0;
1116 const SMDS_MeshElement* newElem2 = 0;
1118 if( !elem->IsQuadratic() ) {
1120 // split liner quadrangle
1122 if ( aBadRate1 <= aBadRate2 ) {
1123 // tr1 + tr2 is better
1124 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1125 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1128 // tr3 + tr4 is better
1129 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1130 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1135 // split quadratic quadrangle
1137 // get surface elem is on
1138 if ( aShapeId != helper.GetSubShapeID() ) {
1142 shape = aMesh->IndexToShape( aShapeId );
1143 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1144 TopoDS_Face face = TopoDS::Face( shape );
1145 surface = BRep_Tool::Surface( face );
1146 if ( !surface.IsNull() )
1147 helper.SetSubShape( shape );
1150 // find middle point for (0,1,2,3)
1151 // and create a node in this point;
1152 const SMDS_MeshNode* newN = 0;
1153 if ( aNodes.size() == 9 )
1155 // SMDSEntity_BiQuad_Quadrangle
1156 newN = aNodes.back();
1161 if ( surface.IsNull() )
1163 for ( int i = 0; i < 4; i++ )
1164 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1169 const SMDS_MeshNode* inFaceNode = 0;
1170 if ( helper.GetNodeUVneedInFaceNode() )
1171 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1172 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1173 inFaceNode = aNodes[ i ];
1175 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1177 for ( int i = 0; i < 4; i++ )
1178 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1180 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1182 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1183 myLastCreatedNodes.Append(newN);
1185 // create a new element
1186 if ( aBadRate1 <= aBadRate2 ) {
1187 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1188 aNodes[6], aNodes[7], newN );
1189 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1190 newN, aNodes[4], aNodes[5] );
1193 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1194 aNodes[7], aNodes[4], newN );
1195 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1196 newN, aNodes[5], aNodes[6] );
1200 // care of a new element
1202 myLastCreatedElems.Append(newElem1);
1203 myLastCreatedElems.Append(newElem2);
1204 AddToSameGroups( newElem1, elem, aMesh );
1205 AddToSameGroups( newElem2, elem, aMesh );
1207 // put a new triangle on the same shape
1210 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1211 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1213 aMesh->RemoveElement( elem );
1218 //=======================================================================
1219 //function : BestSplit
1220 //purpose : Find better diagonal for cutting.
1221 //=======================================================================
1223 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1224 SMESH::Controls::NumericalFunctorPtr theCrit)
1226 myLastCreatedElems.Clear();
1227 myLastCreatedNodes.Clear();
1232 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1235 if( theQuad->NbNodes()==4 ||
1236 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1238 // retrieve element nodes
1239 const SMDS_MeshNode* aNodes [4];
1240 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1242 //while (itN->more())
1244 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1246 // compare two sets of possible triangles
1247 double aBadRate1, aBadRate2; // to what extent a set is bad
1248 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1249 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1250 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1252 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1253 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1254 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1256 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1257 return 1; // diagonal 1-3
1259 return 2; // diagonal 2-4
1266 // Methods of splitting volumes into tetra
1268 const int theHexTo5_1[5*4+1] =
1270 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1272 const int theHexTo5_2[5*4+1] =
1274 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1276 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1278 const int theHexTo6_1[6*4+1] =
1280 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
1282 const int theHexTo6_2[6*4+1] =
1284 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
1286 const int theHexTo6_3[6*4+1] =
1288 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
1290 const int theHexTo6_4[6*4+1] =
1292 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
1294 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1296 const int thePyraTo2_1[2*4+1] =
1298 0, 1, 2, 4, 0, 2, 3, 4, -1
1300 const int thePyraTo2_2[2*4+1] =
1302 1, 2, 3, 4, 1, 3, 0, 4, -1
1304 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1306 const int thePentaTo3_1[3*4+1] =
1308 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1310 const int thePentaTo3_2[3*4+1] =
1312 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1314 const int thePentaTo3_3[3*4+1] =
1316 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1318 const int thePentaTo3_4[3*4+1] =
1320 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1322 const int thePentaTo3_5[3*4+1] =
1324 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1326 const int thePentaTo3_6[3*4+1] =
1328 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1330 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1331 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1333 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1336 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1337 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1338 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1343 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1344 bool _baryNode; //!< additional node is to be created at cell barycenter
1345 bool _ownConn; //!< to delete _connectivity in destructor
1346 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1348 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1349 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1350 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1351 bool hasFacet( const TTriangleFacet& facet ) const
1353 const int* tetConn = _connectivity;
1354 for ( ; tetConn[0] >= 0; tetConn += 4 )
1355 if (( facet.contains( tetConn[0] ) +
1356 facet.contains( tetConn[1] ) +
1357 facet.contains( tetConn[2] ) +
1358 facet.contains( tetConn[3] )) == 3 )
1364 //=======================================================================
1366 * \brief return TSplitMethod for the given element
1368 //=======================================================================
1370 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1372 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1374 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1375 // an edge and a face barycenter; tertaherdons are based on triangles and
1376 // a volume barycenter
1377 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1379 // Find out how adjacent volumes are split
1381 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1382 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1383 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1385 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1386 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1387 if ( nbNodes < 4 ) continue;
1389 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1390 const int* nInd = vol.GetFaceNodesIndices( iF );
1393 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1394 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1395 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1396 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1400 int iCom = 0; // common node of triangle faces to split into
1401 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1403 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1404 nInd[ iQ * ( (iCom+1)%nbNodes )],
1405 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1406 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1407 nInd[ iQ * ( (iCom+2)%nbNodes )],
1408 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1409 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1411 triaSplits.push_back( t012 );
1412 triaSplits.push_back( t023 );
1417 if ( !triaSplits.empty() )
1418 hasAdjacentSplits = true;
1421 // Among variants of split method select one compliant with adjacent volumes
1423 TSplitMethod method;
1424 if ( !vol.Element()->IsPoly() && !is24TetMode )
1426 int nbVariants = 2, nbTet = 0;
1427 const int** connVariants = 0;
1428 switch ( vol.Element()->GetEntityType() )
1430 case SMDSEntity_Hexa:
1431 case SMDSEntity_Quad_Hexa:
1432 case SMDSEntity_TriQuad_Hexa:
1433 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1434 connVariants = theHexTo5, nbTet = 5;
1436 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1438 case SMDSEntity_Pyramid:
1439 case SMDSEntity_Quad_Pyramid:
1440 connVariants = thePyraTo2; nbTet = 2;
1442 case SMDSEntity_Penta:
1443 case SMDSEntity_Quad_Penta:
1444 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1449 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1451 // check method compliancy with adjacent tetras,
1452 // all found splits must be among facets of tetras described by this method
1453 method = TSplitMethod( nbTet, connVariants[variant] );
1454 if ( hasAdjacentSplits && method._nbTetra > 0 )
1456 bool facetCreated = true;
1457 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1459 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1460 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1461 facetCreated = method.hasFacet( *facet );
1463 if ( !facetCreated )
1464 method = TSplitMethod(0); // incompatible method
1468 if ( method._nbTetra < 1 )
1470 // No standard method is applicable, use a generic solution:
1471 // each facet of a volume is split into triangles and
1472 // each of triangles and a volume barycenter form a tetrahedron.
1474 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1476 int* connectivity = new int[ maxTetConnSize + 1 ];
1477 method._connectivity = connectivity;
1478 method._ownConn = true;
1479 method._baryNode = !isHex27; // to create central node or not
1482 int baryCenInd = vol.NbNodes() - int( isHex27 );
1483 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1485 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1486 const int* nInd = vol.GetFaceNodesIndices( iF );
1487 // find common node of triangle facets of tetra to create
1488 int iCommon = 0; // index in linear numeration
1489 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1490 if ( !triaSplits.empty() )
1493 const TTriangleFacet* facet = &triaSplits.front();
1494 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1495 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1496 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1499 else if ( nbNodes > 3 && !is24TetMode )
1501 // find the best method of splitting into triangles by aspect ratio
1502 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1503 map< double, int > badness2iCommon;
1504 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1505 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1506 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1509 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1511 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1512 nodes[ iQ*((iLast-1)%nbNodes)],
1513 nodes[ iQ*((iLast )%nbNodes)]);
1514 badness += getBadRate( &tria, aspectRatio );
1516 badness2iCommon.insert( make_pair( badness, iCommon ));
1518 // use iCommon with lowest badness
1519 iCommon = badness2iCommon.begin()->second;
1521 if ( iCommon >= nbNodes )
1522 iCommon = 0; // something wrong
1524 // fill connectivity of tetrahedra based on a current face
1525 int nbTet = nbNodes - 2;
1526 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1531 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1532 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1536 method._faceBaryNode[ iF ] = 0;
1537 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1540 for ( int i = 0; i < nbTet; ++i )
1542 int i1 = i, i2 = (i+1) % nbNodes;
1543 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1544 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1545 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1546 connectivity[ connSize++ ] = faceBaryCenInd;
1547 connectivity[ connSize++ ] = baryCenInd;
1552 for ( int i = 0; i < nbTet; ++i )
1554 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1555 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1556 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1557 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1558 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1559 connectivity[ connSize++ ] = baryCenInd;
1562 method._nbTetra += nbTet;
1564 } // loop on volume faces
1566 connectivity[ connSize++ ] = -1;
1568 } // end of generic solution
1572 //================================================================================
1574 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1576 //================================================================================
1578 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1580 // find the tetrahedron including the three nodes of facet
1581 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1582 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1583 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1584 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1585 while ( volIt1->more() )
1587 const SMDS_MeshElement* v = volIt1->next();
1588 SMDSAbs_EntityType type = v->GetEntityType();
1589 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1591 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1592 continue; // medium node not allowed
1593 const int ind2 = v->GetNodeIndex( n2 );
1594 if ( ind2 < 0 || 3 < ind2 )
1596 const int ind3 = v->GetNodeIndex( n3 );
1597 if ( ind3 < 0 || 3 < ind3 )
1604 //=======================================================================
1606 * \brief A key of a face of volume
1608 //=======================================================================
1610 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1612 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1614 TIDSortedNodeSet sortedNodes;
1615 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1616 int nbNodes = vol.NbFaceNodes( iF );
1617 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1618 for ( int i = 0; i < nbNodes; i += iQ )
1619 sortedNodes.insert( fNodes[i] );
1620 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1621 first.first = (*(n++))->GetID();
1622 first.second = (*(n++))->GetID();
1623 second.first = (*(n++))->GetID();
1624 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1629 //=======================================================================
1630 //function : SplitVolumesIntoTetra
1631 //purpose : Split volume elements into tetrahedra.
1632 //=======================================================================
1634 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1635 const int theMethodFlags)
1637 // std-like iterator on coordinates of nodes of mesh element
1638 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1639 NXyzIterator xyzEnd;
1641 SMDS_VolumeTool volTool;
1642 SMESH_MesherHelper helper( *GetMesh());
1644 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1645 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1647 SMESH_SequenceOfElemPtr newNodes, newElems;
1649 // map face of volume to it's baricenrtic node
1650 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1653 TIDSortedElemSet::const_iterator elem = theElems.begin();
1654 for ( ; elem != theElems.end(); ++elem )
1656 if ( (*elem)->GetType() != SMDSAbs_Volume )
1658 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1659 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1662 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1664 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1665 if ( splitMethod._nbTetra < 1 ) continue;
1667 // find submesh to add new tetras to
1668 if ( !subMesh || !subMesh->Contains( *elem ))
1670 int shapeID = FindShape( *elem );
1671 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1672 subMesh = GetMeshDS()->MeshElements( shapeID );
1675 if ( (*elem)->IsQuadratic() )
1678 // add quadratic links to the helper
1679 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1681 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1682 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1683 for ( int iN = 0; iN < nbN; iN += iQ )
1684 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1686 helper.SetIsQuadratic( true );
1691 helper.SetIsQuadratic( false );
1693 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1694 helper.SetElementsOnShape( true );
1695 if ( splitMethod._baryNode )
1697 // make a node at barycenter
1698 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1699 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1700 nodes.push_back( gcNode );
1701 newNodes.Append( gcNode );
1703 if ( !splitMethod._faceBaryNode.empty() )
1705 // make or find baricentric nodes of faces
1706 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1707 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1709 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1710 volFace2BaryNode.insert
1711 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1714 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1715 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1717 nodes.push_back( iF_n->second = f_n->second );
1722 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1723 const int* tetConn = splitMethod._connectivity;
1724 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1725 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1726 nodes[ tetConn[1] ],
1727 nodes[ tetConn[2] ],
1728 nodes[ tetConn[3] ]));
1730 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1732 // Split faces on sides of the split volume
1734 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1735 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1737 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1738 if ( nbNodes < 4 ) continue;
1740 // find an existing face
1741 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1742 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1743 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1744 /*noMedium=*/false))
1747 helper.SetElementsOnShape( false );
1748 vector< const SMDS_MeshElement* > triangles;
1750 // find submesh to add new triangles in
1751 if ( !fSubMesh || !fSubMesh->Contains( face ))
1753 int shapeID = FindShape( face );
1754 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1756 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1757 if ( iF_n != splitMethod._faceBaryNode.end() )
1759 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1761 const SMDS_MeshNode* n1 = fNodes[iN];
1762 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1763 const SMDS_MeshNode *n3 = iF_n->second;
1764 if ( !volTool.IsFaceExternal( iF ))
1766 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1768 if ( fSubMesh && n3->getshapeId() < 1 )
1769 fSubMesh->AddNode( n3 );
1774 // among possible triangles create ones discribed by split method
1775 const int* nInd = volTool.GetFaceNodesIndices( iF );
1776 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1777 int iCom = 0; // common node of triangle faces to split into
1778 list< TTriangleFacet > facets;
1779 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1781 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1782 nInd[ iQ * ( (iCom+1)%nbNodes )],
1783 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1784 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1785 nInd[ iQ * ( (iCom+2)%nbNodes )],
1786 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1787 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1789 facets.push_back( t012 );
1790 facets.push_back( t023 );
1791 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1792 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1793 nInd[ iQ * ((iLast-1)%nbNodes )],
1794 nInd[ iQ * ((iLast )%nbNodes )]));
1798 list< TTriangleFacet >::iterator facet = facets.begin();
1799 for ( ; facet != facets.end(); ++facet )
1801 if ( !volTool.IsFaceExternal( iF ))
1802 swap( facet->_n2, facet->_n3 );
1803 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1804 volNodes[ facet->_n2 ],
1805 volNodes[ facet->_n3 ]));
1808 for ( int i = 0; i < triangles.size(); ++i )
1810 if ( !triangles[i] ) continue;
1812 fSubMesh->AddElement( triangles[i]);
1813 newElems.Append( triangles[i] );
1815 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1816 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1819 } // loop on volume faces to split them into triangles
1821 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1823 if ( geomType == SMDSEntity_TriQuad_Hexa )
1825 // remove medium nodes that could become free
1826 for ( int i = 20; i < volTool.NbNodes(); ++i )
1827 if ( volNodes[i]->NbInverseElements() == 0 )
1828 GetMeshDS()->RemoveNode( volNodes[i] );
1830 } // loop on volumes to split
1832 myLastCreatedNodes = newNodes;
1833 myLastCreatedElems = newElems;
1836 //=======================================================================
1837 //function : AddToSameGroups
1838 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1839 //=======================================================================
1841 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1842 const SMDS_MeshElement* elemInGroups,
1843 SMESHDS_Mesh * aMesh)
1845 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1846 if (!groups.empty()) {
1847 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1848 for ( ; grIt != groups.end(); grIt++ ) {
1849 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1850 if ( group && group->Contains( elemInGroups ))
1851 group->SMDSGroup().Add( elemToAdd );
1857 //=======================================================================
1858 //function : RemoveElemFromGroups
1859 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1860 //=======================================================================
1861 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1862 SMESHDS_Mesh * aMesh)
1864 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1865 if (!groups.empty())
1867 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1868 for (; GrIt != groups.end(); GrIt++)
1870 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1871 if (!grp || grp->IsEmpty()) continue;
1872 grp->SMDSGroup().Remove(removeelem);
1877 //================================================================================
1879 * \brief Replace elemToRm by elemToAdd in the all groups
1881 //================================================================================
1883 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1884 const SMDS_MeshElement* elemToAdd,
1885 SMESHDS_Mesh * aMesh)
1887 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1888 if (!groups.empty()) {
1889 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1890 for ( ; grIt != groups.end(); grIt++ ) {
1891 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1892 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1893 group->SMDSGroup().Add( elemToAdd );
1898 //================================================================================
1900 * \brief Replace elemToRm by elemToAdd in the all groups
1902 //================================================================================
1904 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1905 const vector<const SMDS_MeshElement*>& elemToAdd,
1906 SMESHDS_Mesh * aMesh)
1908 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1909 if (!groups.empty())
1911 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1912 for ( ; grIt != groups.end(); grIt++ ) {
1913 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1914 if ( group && group->SMDSGroup().Remove( elemToRm ) )
1915 for ( int i = 0; i < elemToAdd.size(); ++i )
1916 group->SMDSGroup().Add( elemToAdd[ i ] );
1921 //=======================================================================
1922 //function : QuadToTri
1923 //purpose : Cut quadrangles into triangles.
1924 // theCrit is used to select a diagonal to cut
1925 //=======================================================================
1927 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1928 const bool the13Diag)
1930 myLastCreatedElems.Clear();
1931 myLastCreatedNodes.Clear();
1933 MESSAGE( "::QuadToTri()" );
1935 SMESHDS_Mesh * aMesh = GetMeshDS();
1937 Handle(Geom_Surface) surface;
1938 SMESH_MesherHelper helper( *GetMesh() );
1940 TIDSortedElemSet::iterator itElem;
1941 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1942 const SMDS_MeshElement* elem = *itElem;
1943 if ( !elem || elem->GetType() != SMDSAbs_Face )
1945 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1946 if(!isquad) continue;
1948 if(elem->NbNodes()==4) {
1949 // retrieve element nodes
1950 const SMDS_MeshNode* aNodes [4];
1951 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1953 while ( itN->more() )
1954 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1956 int aShapeId = FindShape( elem );
1957 const SMDS_MeshElement* newElem1 = 0;
1958 const SMDS_MeshElement* newElem2 = 0;
1960 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1961 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1964 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1965 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1967 myLastCreatedElems.Append(newElem1);
1968 myLastCreatedElems.Append(newElem2);
1969 // put a new triangle on the same shape and add to the same groups
1972 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1973 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1975 AddToSameGroups( newElem1, elem, aMesh );
1976 AddToSameGroups( newElem2, elem, aMesh );
1977 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
1978 aMesh->RemoveElement( elem );
1981 // Quadratic quadrangle
1983 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1985 // get surface elem is on
1986 int aShapeId = FindShape( elem );
1987 if ( aShapeId != helper.GetSubShapeID() ) {
1991 shape = aMesh->IndexToShape( aShapeId );
1992 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1993 TopoDS_Face face = TopoDS::Face( shape );
1994 surface = BRep_Tool::Surface( face );
1995 if ( !surface.IsNull() )
1996 helper.SetSubShape( shape );
2000 const SMDS_MeshNode* aNodes [8];
2001 const SMDS_MeshNode* inFaceNode = 0;
2002 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2004 while ( itN->more() ) {
2005 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2006 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2007 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2009 inFaceNode = aNodes[ i-1 ];
2013 // find middle point for (0,1,2,3)
2014 // and create a node in this point;
2016 if ( surface.IsNull() ) {
2018 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2022 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2025 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2027 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2029 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2030 myLastCreatedNodes.Append(newN);
2032 // create a new element
2033 const SMDS_MeshElement* newElem1 = 0;
2034 const SMDS_MeshElement* newElem2 = 0;
2036 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2037 aNodes[6], aNodes[7], newN );
2038 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2039 newN, aNodes[4], aNodes[5] );
2042 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2043 aNodes[7], aNodes[4], newN );
2044 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2045 newN, aNodes[5], aNodes[6] );
2047 myLastCreatedElems.Append(newElem1);
2048 myLastCreatedElems.Append(newElem2);
2049 // put a new triangle on the same shape and add to the same groups
2052 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2053 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2055 AddToSameGroups( newElem1, elem, aMesh );
2056 AddToSameGroups( newElem2, elem, aMesh );
2057 aMesh->RemoveElement( elem );
2064 //=======================================================================
2065 //function : getAngle
2067 //=======================================================================
2069 double getAngle(const SMDS_MeshElement * tr1,
2070 const SMDS_MeshElement * tr2,
2071 const SMDS_MeshNode * n1,
2072 const SMDS_MeshNode * n2)
2074 double angle = 2. * M_PI; // bad angle
2077 SMESH::Controls::TSequenceOfXYZ P1, P2;
2078 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2079 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2082 if(!tr1->IsQuadratic())
2083 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2085 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2086 if ( N1.SquareMagnitude() <= gp::Resolution() )
2088 if(!tr2->IsQuadratic())
2089 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2091 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2092 if ( N2.SquareMagnitude() <= gp::Resolution() )
2095 // find the first diagonal node n1 in the triangles:
2096 // take in account a diagonal link orientation
2097 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2098 for ( int t = 0; t < 2; t++ ) {
2099 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2100 int i = 0, iDiag = -1;
2101 while ( it->more()) {
2102 const SMDS_MeshElement *n = it->next();
2103 if ( n == n1 || n == n2 ) {
2107 if ( i - iDiag == 1 )
2108 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2117 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2120 angle = N1.Angle( N2 );
2125 // =================================================
2126 // class generating a unique ID for a pair of nodes
2127 // and able to return nodes by that ID
2128 // =================================================
2132 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2133 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2136 long GetLinkID (const SMDS_MeshNode * n1,
2137 const SMDS_MeshNode * n2) const
2139 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2142 bool GetNodes (const long theLinkID,
2143 const SMDS_MeshNode* & theNode1,
2144 const SMDS_MeshNode* & theNode2) const
2146 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2147 if ( !theNode1 ) return false;
2148 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2149 if ( !theNode2 ) return false;
2155 const SMESHDS_Mesh* myMesh;
2160 //=======================================================================
2161 //function : TriToQuad
2162 //purpose : Fuse neighbour triangles into quadrangles.
2163 // theCrit is used to select a neighbour to fuse with.
2164 // theMaxAngle is a max angle between element normals at which
2165 // fusion is still performed.
2166 //=======================================================================
2168 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2169 SMESH::Controls::NumericalFunctorPtr theCrit,
2170 const double theMaxAngle)
2172 myLastCreatedElems.Clear();
2173 myLastCreatedNodes.Clear();
2175 MESSAGE( "::TriToQuad()" );
2177 if ( !theCrit.get() )
2180 SMESHDS_Mesh * aMesh = GetMeshDS();
2182 // Prepare data for algo: build
2183 // 1. map of elements with their linkIDs
2184 // 2. map of linkIDs with their elements
2186 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2187 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2188 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2189 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2191 TIDSortedElemSet::iterator itElem;
2192 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2193 const SMDS_MeshElement* elem = *itElem;
2194 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2195 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2196 if(!IsTria) continue;
2198 // retrieve element nodes
2199 const SMDS_MeshNode* aNodes [4];
2200 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2203 aNodes[ i++ ] = cast2Node( itN->next() );
2204 aNodes[ 3 ] = aNodes[ 0 ];
2207 for ( i = 0; i < 3; i++ ) {
2208 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2209 // check if elements sharing a link can be fused
2210 itLE = mapLi_listEl.find( link );
2211 if ( itLE != mapLi_listEl.end() ) {
2212 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2214 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2215 //if ( FindShape( elem ) != FindShape( elem2 ))
2216 // continue; // do not fuse triangles laying on different shapes
2217 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2218 continue; // avoid making badly shaped quads
2219 (*itLE).second.push_back( elem );
2222 mapLi_listEl[ link ].push_back( elem );
2224 mapEl_setLi [ elem ].insert( link );
2227 // Clean the maps from the links shared by a sole element, ie
2228 // links to which only one element is bound in mapLi_listEl
2230 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2231 int nbElems = (*itLE).second.size();
2232 if ( nbElems < 2 ) {
2233 const SMDS_MeshElement* elem = (*itLE).second.front();
2234 SMESH_TLink link = (*itLE).first;
2235 mapEl_setLi[ elem ].erase( link );
2236 if ( mapEl_setLi[ elem ].empty() )
2237 mapEl_setLi.erase( elem );
2241 // Algo: fuse triangles into quadrangles
2243 while ( ! mapEl_setLi.empty() ) {
2244 // Look for the start element:
2245 // the element having the least nb of shared links
2246 const SMDS_MeshElement* startElem = 0;
2248 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2249 int nbLinks = (*itEL).second.size();
2250 if ( nbLinks < minNbLinks ) {
2251 startElem = (*itEL).first;
2252 minNbLinks = nbLinks;
2253 if ( minNbLinks == 1 )
2258 // search elements to fuse starting from startElem or links of elements
2259 // fused earlyer - startLinks
2260 list< SMESH_TLink > startLinks;
2261 while ( startElem || !startLinks.empty() ) {
2262 while ( !startElem && !startLinks.empty() ) {
2263 // Get an element to start, by a link
2264 SMESH_TLink linkId = startLinks.front();
2265 startLinks.pop_front();
2266 itLE = mapLi_listEl.find( linkId );
2267 if ( itLE != mapLi_listEl.end() ) {
2268 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2269 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2270 for ( ; itE != listElem.end() ; itE++ )
2271 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2273 mapLi_listEl.erase( itLE );
2278 // Get candidates to be fused
2279 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2280 const SMESH_TLink *link12, *link13;
2282 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2283 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2284 ASSERT( !setLi.empty() );
2285 set< SMESH_TLink >::iterator itLi;
2286 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2288 const SMESH_TLink & link = (*itLi);
2289 itLE = mapLi_listEl.find( link );
2290 if ( itLE == mapLi_listEl.end() )
2293 const SMDS_MeshElement* elem = (*itLE).second.front();
2295 elem = (*itLE).second.back();
2296 mapLi_listEl.erase( itLE );
2297 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2308 // add other links of elem to list of links to re-start from
2309 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2310 set< SMESH_TLink >::iterator it;
2311 for ( it = links.begin(); it != links.end(); it++ ) {
2312 const SMESH_TLink& link2 = (*it);
2313 if ( link2 != link )
2314 startLinks.push_back( link2 );
2318 // Get nodes of possible quadrangles
2319 const SMDS_MeshNode *n12 [4], *n13 [4];
2320 bool Ok12 = false, Ok13 = false;
2321 const SMDS_MeshNode *linkNode1, *linkNode2;
2323 linkNode1 = link12->first;
2324 linkNode2 = link12->second;
2325 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2329 linkNode1 = link13->first;
2330 linkNode2 = link13->second;
2331 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2335 // Choose a pair to fuse
2336 if ( Ok12 && Ok13 ) {
2337 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2338 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2339 double aBadRate12 = getBadRate( &quad12, theCrit );
2340 double aBadRate13 = getBadRate( &quad13, theCrit );
2341 if ( aBadRate13 < aBadRate12 )
2348 // and remove fused elems and removed links from the maps
2349 mapEl_setLi.erase( tr1 );
2351 mapEl_setLi.erase( tr2 );
2352 mapLi_listEl.erase( *link12 );
2353 if(tr1->NbNodes()==3) {
2354 const SMDS_MeshElement* newElem = 0;
2355 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2356 myLastCreatedElems.Append(newElem);
2357 AddToSameGroups( newElem, tr1, aMesh );
2358 int aShapeId = tr1->getshapeId();
2361 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2363 aMesh->RemoveElement( tr1 );
2364 aMesh->RemoveElement( tr2 );
2367 const SMDS_MeshNode* N1 [6];
2368 const SMDS_MeshNode* N2 [6];
2369 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2370 // now we receive following N1 and N2 (using numeration as above image)
2371 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2372 // i.e. first nodes from both arrays determ new diagonal
2373 const SMDS_MeshNode* aNodes[8];
2382 const SMDS_MeshElement* newElem = 0;
2383 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2384 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2385 myLastCreatedElems.Append(newElem);
2386 AddToSameGroups( newElem, tr1, aMesh );
2387 int aShapeId = tr1->getshapeId();
2390 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2392 aMesh->RemoveElement( tr1 );
2393 aMesh->RemoveElement( tr2 );
2394 // remove middle node (9)
2395 GetMeshDS()->RemoveNode( N1[4] );
2399 mapEl_setLi.erase( tr3 );
2400 mapLi_listEl.erase( *link13 );
2401 if(tr1->NbNodes()==3) {
2402 const SMDS_MeshElement* newElem = 0;
2403 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2404 myLastCreatedElems.Append(newElem);
2405 AddToSameGroups( newElem, tr1, aMesh );
2406 int aShapeId = tr1->getshapeId();
2409 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2411 aMesh->RemoveElement( tr1 );
2412 aMesh->RemoveElement( tr3 );
2415 const SMDS_MeshNode* N1 [6];
2416 const SMDS_MeshNode* N2 [6];
2417 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2418 // now we receive following N1 and N2 (using numeration as above image)
2419 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2420 // i.e. first nodes from both arrays determ new diagonal
2421 const SMDS_MeshNode* aNodes[8];
2430 const SMDS_MeshElement* newElem = 0;
2431 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2432 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2433 myLastCreatedElems.Append(newElem);
2434 AddToSameGroups( newElem, tr1, aMesh );
2435 int aShapeId = tr1->getshapeId();
2438 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2440 aMesh->RemoveElement( tr1 );
2441 aMesh->RemoveElement( tr3 );
2442 // remove middle node (9)
2443 GetMeshDS()->RemoveNode( N1[4] );
2447 // Next element to fuse: the rejected one
2449 startElem = Ok12 ? tr3 : tr2;
2451 } // if ( startElem )
2452 } // while ( startElem || !startLinks.empty() )
2453 } // while ( ! mapEl_setLi.empty() )
2459 /*#define DUMPSO(txt) \
2460 // cout << txt << endl;
2461 //=============================================================================
2465 //=============================================================================
2466 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2470 int tmp = idNodes[ i1 ];
2471 idNodes[ i1 ] = idNodes[ i2 ];
2472 idNodes[ i2 ] = tmp;
2473 gp_Pnt Ptmp = P[ i1 ];
2476 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2479 //=======================================================================
2480 //function : SortQuadNodes
2481 //purpose : Set 4 nodes of a quadrangle face in a good order.
2482 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2484 //=======================================================================
2486 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2491 for ( i = 0; i < 4; i++ ) {
2492 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2494 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2497 gp_Vec V1(P[0], P[1]);
2498 gp_Vec V2(P[0], P[2]);
2499 gp_Vec V3(P[0], P[3]);
2501 gp_Vec Cross1 = V1 ^ V2;
2502 gp_Vec Cross2 = V2 ^ V3;
2505 if (Cross1.Dot(Cross2) < 0)
2510 if (Cross1.Dot(Cross2) < 0)
2514 swap ( i, i + 1, idNodes, P );
2516 // for ( int ii = 0; ii < 4; ii++ ) {
2517 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2518 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2524 //=======================================================================
2525 //function : SortHexaNodes
2526 //purpose : Set 8 nodes of a hexahedron in a good order.
2527 // Return success status
2528 //=======================================================================
2530 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2535 DUMPSO( "INPUT: ========================================");
2536 for ( i = 0; i < 8; i++ ) {
2537 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2538 if ( !n ) return false;
2539 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2540 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2542 DUMPSO( "========================================");
2545 set<int> faceNodes; // ids of bottom face nodes, to be found
2546 set<int> checkedId1; // ids of tried 2-nd nodes
2547 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2548 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2549 int iMin, iLoop1 = 0;
2551 // Loop to try the 2-nd nodes
2553 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2555 // Find not checked 2-nd node
2556 for ( i = 1; i < 8; i++ )
2557 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2558 int id1 = idNodes[i];
2559 swap ( 1, i, idNodes, P );
2560 checkedId1.insert ( id1 );
2564 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2565 // ie that all but meybe one (id3 which is on the same face) nodes
2566 // lay on the same side from the triangle plane.
2568 bool manyInPlane = false; // more than 4 nodes lay in plane
2570 while ( ++iLoop2 < 6 ) {
2572 // get 1-2-3 plane coeffs
2573 Standard_Real A, B, C, D;
2574 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2575 if ( N.SquareMagnitude() > gp::Resolution() )
2577 gp_Pln pln ( P[0], N );
2578 pln.Coefficients( A, B, C, D );
2580 // find the node (iMin) closest to pln
2581 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2583 for ( i = 3; i < 8; i++ ) {
2584 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2585 if ( fabs( dist[i] ) < minDist ) {
2586 minDist = fabs( dist[i] );
2589 if ( fabs( dist[i] ) <= tol )
2590 idInPln.insert( idNodes[i] );
2593 // there should not be more than 4 nodes in bottom plane
2594 if ( idInPln.size() > 1 )
2596 DUMPSO( "### idInPln.size() = " << idInPln.size());
2597 // idInPlane does not contain the first 3 nodes
2598 if ( manyInPlane || idInPln.size() == 5)
2599 return false; // all nodes in one plane
2602 // set the 1-st node to be not in plane
2603 for ( i = 3; i < 8; i++ ) {
2604 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2605 DUMPSO( "### Reset 0-th node");
2606 swap( 0, i, idNodes, P );
2611 // reset to re-check second nodes
2612 leastDist = DBL_MAX;
2616 break; // from iLoop2;
2619 // check that the other 4 nodes are on the same side
2620 bool sameSide = true;
2621 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2622 for ( i = 3; sameSide && i < 8; i++ ) {
2624 sameSide = ( isNeg == dist[i] <= 0.);
2627 // keep best solution
2628 if ( sameSide && minDist < leastDist ) {
2629 leastDist = minDist;
2631 faceNodes.insert( idNodes[ 1 ] );
2632 faceNodes.insert( idNodes[ 2 ] );
2633 faceNodes.insert( idNodes[ iMin ] );
2634 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2635 << " leastDist = " << leastDist);
2636 if ( leastDist <= DBL_MIN )
2641 // set next 3-d node to check
2642 int iNext = 2 + iLoop2;
2644 DUMPSO( "Try 2-nd");
2645 swap ( 2, iNext, idNodes, P );
2647 } // while ( iLoop2 < 6 )
2650 if ( faceNodes.empty() ) return false;
2652 // Put the faceNodes in proper places
2653 for ( i = 4; i < 8; i++ ) {
2654 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2655 // find a place to put
2657 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2659 DUMPSO( "Set faceNodes");
2660 swap ( iTo, i, idNodes, P );
2665 // Set nodes of the found bottom face in good order
2666 DUMPSO( " Found bottom face: ");
2667 i = SortQuadNodes( theMesh, idNodes );
2669 gp_Pnt Ptmp = P[ i ];
2674 // for ( int ii = 0; ii < 4; ii++ ) {
2675 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2676 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2679 // Gravity center of the top and bottom faces
2680 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2681 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2683 // Get direction from the bottom to the top face
2684 gp_Vec upDir ( aGCb, aGCt );
2685 Standard_Real upDirSize = upDir.Magnitude();
2686 if ( upDirSize <= gp::Resolution() ) return false;
2689 // Assure that the bottom face normal points up
2690 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2691 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2692 if ( Nb.Dot( upDir ) < 0 ) {
2693 DUMPSO( "Reverse bottom face");
2694 swap( 1, 3, idNodes, P );
2697 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2698 Standard_Real minDist = DBL_MAX;
2699 for ( i = 4; i < 8; i++ ) {
2700 // projection of P[i] to the plane defined by P[0] and upDir
2701 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2702 Standard_Real sqDist = P[0].SquareDistance( Pp );
2703 if ( sqDist < minDist ) {
2708 DUMPSO( "Set 4-th");
2709 swap ( 4, iMin, idNodes, P );
2711 // Set nodes of the top face in good order
2712 DUMPSO( "Sort top face");
2713 i = SortQuadNodes( theMesh, &idNodes[4] );
2716 gp_Pnt Ptmp = P[ i ];
2721 // Assure that direction of the top face normal is from the bottom face
2722 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2723 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2724 if ( Nt.Dot( upDir ) < 0 ) {
2725 DUMPSO( "Reverse top face");
2726 swap( 5, 7, idNodes, P );
2729 // DUMPSO( "OUTPUT: ========================================");
2730 // for ( i = 0; i < 8; i++ ) {
2731 // float *p = ugrid->GetPoint(idNodes[i]);
2732 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2738 //================================================================================
2740 * \brief Return nodes linked to the given one
2741 * \param theNode - the node
2742 * \param linkedNodes - the found nodes
2743 * \param type - the type of elements to check
2745 * Medium nodes are ignored
2747 //================================================================================
2749 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2750 TIDSortedElemSet & linkedNodes,
2751 SMDSAbs_ElementType type )
2753 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2754 while ( elemIt->more() )
2756 const SMDS_MeshElement* elem = elemIt->next();
2757 if(elem->GetType() == SMDSAbs_0DElement)
2760 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2761 if ( elem->GetType() == SMDSAbs_Volume )
2763 SMDS_VolumeTool vol( elem );
2764 while ( nodeIt->more() ) {
2765 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2766 if ( theNode != n && vol.IsLinked( theNode, n ))
2767 linkedNodes.insert( n );
2772 for ( int i = 0; nodeIt->more(); ++i ) {
2773 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2774 if ( n == theNode ) {
2775 int iBefore = i - 1;
2777 if ( elem->IsQuadratic() ) {
2778 int nb = elem->NbNodes() / 2;
2779 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2780 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2782 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2783 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2790 //=======================================================================
2791 //function : laplacianSmooth
2792 //purpose : pulls theNode toward the center of surrounding nodes directly
2793 // connected to that node along an element edge
2794 //=======================================================================
2796 void laplacianSmooth(const SMDS_MeshNode* theNode,
2797 const Handle(Geom_Surface)& theSurface,
2798 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2800 // find surrounding nodes
2802 TIDSortedElemSet nodeSet;
2803 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2805 // compute new coodrs
2807 double coord[] = { 0., 0., 0. };
2808 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2809 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2810 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2811 if ( theSurface.IsNull() ) { // smooth in 3D
2812 coord[0] += node->X();
2813 coord[1] += node->Y();
2814 coord[2] += node->Z();
2816 else { // smooth in 2D
2817 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2818 gp_XY* uv = theUVMap[ node ];
2819 coord[0] += uv->X();
2820 coord[1] += uv->Y();
2823 int nbNodes = nodeSet.size();
2826 coord[0] /= nbNodes;
2827 coord[1] /= nbNodes;
2829 if ( !theSurface.IsNull() ) {
2830 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2831 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2832 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2838 coord[2] /= nbNodes;
2842 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2845 //=======================================================================
2846 //function : centroidalSmooth
2847 //purpose : pulls theNode toward the element-area-weighted centroid of the
2848 // surrounding elements
2849 //=======================================================================
2851 void centroidalSmooth(const SMDS_MeshNode* theNode,
2852 const Handle(Geom_Surface)& theSurface,
2853 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2855 gp_XYZ aNewXYZ(0.,0.,0.);
2856 SMESH::Controls::Area anAreaFunc;
2857 double totalArea = 0.;
2862 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2863 while ( elemIt->more() )
2865 const SMDS_MeshElement* elem = elemIt->next();
2868 gp_XYZ elemCenter(0.,0.,0.);
2869 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2870 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2871 int nn = elem->NbNodes();
2872 if(elem->IsQuadratic()) nn = nn/2;
2874 //while ( itN->more() ) {
2876 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2878 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2879 aNodePoints.push_back( aP );
2880 if ( !theSurface.IsNull() ) { // smooth in 2D
2881 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2882 gp_XY* uv = theUVMap[ aNode ];
2883 aP.SetCoord( uv->X(), uv->Y(), 0. );
2887 double elemArea = anAreaFunc.GetValue( aNodePoints );
2888 totalArea += elemArea;
2890 aNewXYZ += elemCenter * elemArea;
2892 aNewXYZ /= totalArea;
2893 if ( !theSurface.IsNull() ) {
2894 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2895 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2900 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2903 //=======================================================================
2904 //function : getClosestUV
2905 //purpose : return UV of closest projection
2906 //=======================================================================
2908 static bool getClosestUV (Extrema_GenExtPS& projector,
2909 const gp_Pnt& point,
2912 projector.Perform( point );
2913 if ( projector.IsDone() ) {
2914 double u, v, minVal = DBL_MAX;
2915 for ( int i = projector.NbExt(); i > 0; i-- )
2916 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
2917 if ( projector.SquareDistance( i ) < minVal ) {
2918 minVal = projector.SquareDistance( i );
2920 if ( projector.Value( i ) < minVal ) {
2921 minVal = projector.Value( i );
2923 projector.Point( i ).Parameter( u, v );
2925 result.SetCoord( u, v );
2931 //=======================================================================
2933 //purpose : Smooth theElements during theNbIterations or until a worst
2934 // element has aspect ratio <= theTgtAspectRatio.
2935 // Aspect Ratio varies in range [1.0, inf].
2936 // If theElements is empty, the whole mesh is smoothed.
2937 // theFixedNodes contains additionally fixed nodes. Nodes built
2938 // on edges and boundary nodes are always fixed.
2939 //=======================================================================
2941 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
2942 set<const SMDS_MeshNode*> & theFixedNodes,
2943 const SmoothMethod theSmoothMethod,
2944 const int theNbIterations,
2945 double theTgtAspectRatio,
2948 myLastCreatedElems.Clear();
2949 myLastCreatedNodes.Clear();
2951 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2953 if ( theTgtAspectRatio < 1.0 )
2954 theTgtAspectRatio = 1.0;
2956 const double disttol = 1.e-16;
2958 SMESH::Controls::AspectRatio aQualityFunc;
2960 SMESHDS_Mesh* aMesh = GetMeshDS();
2962 if ( theElems.empty() ) {
2963 // add all faces to theElems
2964 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2965 while ( fIt->more() ) {
2966 const SMDS_MeshElement* face = fIt->next();
2967 theElems.insert( face );
2970 // get all face ids theElems are on
2971 set< int > faceIdSet;
2972 TIDSortedElemSet::iterator itElem;
2974 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2975 int fId = FindShape( *itElem );
2976 // check that corresponding submesh exists and a shape is face
2978 faceIdSet.find( fId ) == faceIdSet.end() &&
2979 aMesh->MeshElements( fId )) {
2980 TopoDS_Shape F = aMesh->IndexToShape( fId );
2981 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2982 faceIdSet.insert( fId );
2985 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2987 // ===============================================
2988 // smooth elements on each TopoDS_Face separately
2989 // ===============================================
2991 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2992 for ( ; fId != faceIdSet.rend(); ++fId ) {
2993 // get face surface and submesh
2994 Handle(Geom_Surface) surface;
2995 SMESHDS_SubMesh* faceSubMesh = 0;
2997 double fToler2 = 0, f,l;
2998 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2999 bool isUPeriodic = false, isVPeriodic = false;
3001 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3002 surface = BRep_Tool::Surface( face );
3003 faceSubMesh = aMesh->MeshElements( *fId );
3004 fToler2 = BRep_Tool::Tolerance( face );
3005 fToler2 *= fToler2 * 10.;
3006 isUPeriodic = surface->IsUPeriodic();
3009 isVPeriodic = surface->IsVPeriodic();
3012 surface->Bounds( u1, u2, v1, v2 );
3014 // ---------------------------------------------------------
3015 // for elements on a face, find movable and fixed nodes and
3016 // compute UV for them
3017 // ---------------------------------------------------------
3018 bool checkBoundaryNodes = false;
3019 bool isQuadratic = false;
3020 set<const SMDS_MeshNode*> setMovableNodes;
3021 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3022 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3023 list< const SMDS_MeshElement* > elemsOnFace;
3025 Extrema_GenExtPS projector;
3026 GeomAdaptor_Surface surfAdaptor;
3027 if ( !surface.IsNull() ) {
3028 surfAdaptor.Load( surface );
3029 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3031 int nbElemOnFace = 0;
3032 itElem = theElems.begin();
3033 // loop on not yet smoothed elements: look for elems on a face
3034 while ( itElem != theElems.end() ) {
3035 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3036 break; // all elements found
3038 const SMDS_MeshElement* elem = *itElem;
3039 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3040 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3044 elemsOnFace.push_back( elem );
3045 theElems.erase( itElem++ );
3049 isQuadratic = elem->IsQuadratic();
3051 // get movable nodes of elem
3052 const SMDS_MeshNode* node;
3053 SMDS_TypeOfPosition posType;
3054 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3055 int nn = 0, nbn = elem->NbNodes();
3056 if(elem->IsQuadratic())
3058 while ( nn++ < nbn ) {
3059 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3060 const SMDS_PositionPtr& pos = node->GetPosition();
3061 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3062 if (posType != SMDS_TOP_EDGE &&
3063 posType != SMDS_TOP_VERTEX &&
3064 theFixedNodes.find( node ) == theFixedNodes.end())
3066 // check if all faces around the node are on faceSubMesh
3067 // because a node on edge may be bound to face
3068 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3070 if ( faceSubMesh ) {
3071 while ( eIt->more() && all ) {
3072 const SMDS_MeshElement* e = eIt->next();
3073 all = faceSubMesh->Contains( e );
3077 setMovableNodes.insert( node );
3079 checkBoundaryNodes = true;
3081 if ( posType == SMDS_TOP_3DSPACE )
3082 checkBoundaryNodes = true;
3085 if ( surface.IsNull() )
3088 // get nodes to check UV
3089 list< const SMDS_MeshNode* > uvCheckNodes;
3090 itN = elem->nodesIterator();
3091 nn = 0; nbn = elem->NbNodes();
3092 if(elem->IsQuadratic())
3094 while ( nn++ < nbn ) {
3095 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3096 if ( uvMap.find( node ) == uvMap.end() )
3097 uvCheckNodes.push_back( node );
3098 // add nodes of elems sharing node
3099 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3100 // while ( eIt->more() ) {
3101 // const SMDS_MeshElement* e = eIt->next();
3102 // if ( e != elem ) {
3103 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3104 // while ( nIt->more() ) {
3105 // const SMDS_MeshNode* n =
3106 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3107 // if ( uvMap.find( n ) == uvMap.end() )
3108 // uvCheckNodes.push_back( n );
3114 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3115 for ( ; n != uvCheckNodes.end(); ++n ) {
3118 const SMDS_PositionPtr& pos = node->GetPosition();
3119 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3121 switch ( posType ) {
3122 case SMDS_TOP_FACE: {
3123 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3124 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3127 case SMDS_TOP_EDGE: {
3128 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3129 Handle(Geom2d_Curve) pcurve;
3130 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3131 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3132 if ( !pcurve.IsNull() ) {
3133 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3134 uv = pcurve->Value( u ).XY();
3138 case SMDS_TOP_VERTEX: {
3139 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3140 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3141 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3146 // check existing UV
3147 bool project = true;
3148 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3149 double dist1 = DBL_MAX, dist2 = 0;
3150 if ( posType != SMDS_TOP_3DSPACE ) {
3151 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3152 project = dist1 > fToler2;
3154 if ( project ) { // compute new UV
3156 if ( !getClosestUV( projector, pNode, newUV )) {
3157 MESSAGE("Node Projection Failed " << node);
3161 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3163 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3165 if ( posType != SMDS_TOP_3DSPACE )
3166 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3167 if ( dist2 < dist1 )
3171 // store UV in the map
3172 listUV.push_back( uv );
3173 uvMap.insert( make_pair( node, &listUV.back() ));
3175 } // loop on not yet smoothed elements
3177 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3178 checkBoundaryNodes = true;
3180 // fix nodes on mesh boundary
3182 if ( checkBoundaryNodes ) {
3183 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3184 map< SMESH_TLink, int >::iterator link_nb;
3185 // put all elements links to linkNbMap
3186 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3187 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3188 const SMDS_MeshElement* elem = (*elemIt);
3189 int nbn = elem->NbCornerNodes();
3190 // loop on elem links: insert them in linkNbMap
3191 for ( int iN = 0; iN < nbn; ++iN ) {
3192 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3193 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3194 SMESH_TLink link( n1, n2 );
3195 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3199 // remove nodes that are in links encountered only once from setMovableNodes
3200 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3201 if ( link_nb->second == 1 ) {
3202 setMovableNodes.erase( link_nb->first.node1() );
3203 setMovableNodes.erase( link_nb->first.node2() );
3208 // -----------------------------------------------------
3209 // for nodes on seam edge, compute one more UV ( uvMap2 );
3210 // find movable nodes linked to nodes on seam and which
3211 // are to be smoothed using the second UV ( uvMap2 )
3212 // -----------------------------------------------------
3214 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3215 if ( !surface.IsNull() ) {
3216 TopExp_Explorer eExp( face, TopAbs_EDGE );
3217 for ( ; eExp.More(); eExp.Next() ) {
3218 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3219 if ( !BRep_Tool::IsClosed( edge, face ))
3221 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3222 if ( !sm ) continue;
3223 // find out which parameter varies for a node on seam
3226 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3227 if ( pcurve.IsNull() ) continue;
3228 uv1 = pcurve->Value( f );
3230 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3231 if ( pcurve.IsNull() ) continue;
3232 uv2 = pcurve->Value( f );
3233 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3235 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3236 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3238 // get nodes on seam and its vertices
3239 list< const SMDS_MeshNode* > seamNodes;
3240 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3241 while ( nSeamIt->more() ) {
3242 const SMDS_MeshNode* node = nSeamIt->next();
3243 if ( !isQuadratic || !IsMedium( node ))
3244 seamNodes.push_back( node );
3246 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3247 for ( ; vExp.More(); vExp.Next() ) {
3248 sm = aMesh->MeshElements( vExp.Current() );
3250 nSeamIt = sm->GetNodes();
3251 while ( nSeamIt->more() )
3252 seamNodes.push_back( nSeamIt->next() );
3255 // loop on nodes on seam
3256 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3257 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3258 const SMDS_MeshNode* nSeam = *noSeIt;
3259 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3260 if ( n_uv == uvMap.end() )
3263 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3264 // set the second UV
3265 listUV.push_back( *n_uv->second );
3266 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3267 if ( uvMap2.empty() )
3268 uvMap2 = uvMap; // copy the uvMap contents
3269 uvMap2[ nSeam ] = &listUV.back();
3271 // collect movable nodes linked to ones on seam in nodesNearSeam
3272 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3273 while ( eIt->more() ) {
3274 const SMDS_MeshElement* e = eIt->next();
3275 int nbUseMap1 = 0, nbUseMap2 = 0;
3276 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3277 int nn = 0, nbn = e->NbNodes();
3278 if(e->IsQuadratic()) nbn = nbn/2;
3279 while ( nn++ < nbn )
3281 const SMDS_MeshNode* n =
3282 static_cast<const SMDS_MeshNode*>( nIt->next() );
3284 setMovableNodes.find( n ) == setMovableNodes.end() )
3286 // add only nodes being closer to uv2 than to uv1
3287 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3288 0.5 * ( n->Y() + nSeam->Y() ),
3289 0.5 * ( n->Z() + nSeam->Z() ));
3291 getClosestUV( projector, pMid, uv );
3292 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3293 nodesNearSeam.insert( n );
3299 // for centroidalSmooth all element nodes must
3300 // be on one side of a seam
3301 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3302 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3304 while ( nn++ < nbn ) {
3305 const SMDS_MeshNode* n =
3306 static_cast<const SMDS_MeshNode*>( nIt->next() );
3307 setMovableNodes.erase( n );
3311 } // loop on nodes on seam
3312 } // loop on edge of a face
3313 } // if ( !face.IsNull() )
3315 if ( setMovableNodes.empty() ) {
3316 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3317 continue; // goto next face
3325 double maxRatio = -1., maxDisplacement = -1.;
3326 set<const SMDS_MeshNode*>::iterator nodeToMove;
3327 for ( it = 0; it < theNbIterations; it++ ) {
3328 maxDisplacement = 0.;
3329 nodeToMove = setMovableNodes.begin();
3330 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3331 const SMDS_MeshNode* node = (*nodeToMove);
3332 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3335 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3336 if ( theSmoothMethod == LAPLACIAN )
3337 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3339 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3341 // node displacement
3342 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3343 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3344 if ( aDispl > maxDisplacement )
3345 maxDisplacement = aDispl;
3347 // no node movement => exit
3348 //if ( maxDisplacement < 1.e-16 ) {
3349 if ( maxDisplacement < disttol ) {
3350 MESSAGE("-- no node movement --");
3354 // check elements quality
3356 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3357 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3358 const SMDS_MeshElement* elem = (*elemIt);
3359 if ( !elem || elem->GetType() != SMDSAbs_Face )
3361 SMESH::Controls::TSequenceOfXYZ aPoints;
3362 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3363 double aValue = aQualityFunc.GetValue( aPoints );
3364 if ( aValue > maxRatio )
3368 if ( maxRatio <= theTgtAspectRatio ) {
3369 MESSAGE("-- quality achived --");
3372 if (it+1 == theNbIterations) {
3373 MESSAGE("-- Iteration limit exceeded --");
3375 } // smoothing iterations
3377 MESSAGE(" Face id: " << *fId <<
3378 " Nb iterstions: " << it <<
3379 " Displacement: " << maxDisplacement <<
3380 " Aspect Ratio " << maxRatio);
3382 // ---------------------------------------
3383 // new nodes positions are computed,
3384 // record movement in DS and set new UV
3385 // ---------------------------------------
3386 nodeToMove = setMovableNodes.begin();
3387 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3388 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3389 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3390 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3391 if ( node_uv != uvMap.end() ) {
3392 gp_XY* uv = node_uv->second;
3394 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3398 // move medium nodes of quadratic elements
3401 SMESH_MesherHelper helper( *GetMesh() );
3402 if ( !face.IsNull() )
3403 helper.SetSubShape( face );
3404 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3405 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3406 const SMDS_VtkFace* QF =
3407 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3408 if(QF && QF->IsQuadratic()) {
3409 vector<const SMDS_MeshNode*> Ns;
3410 Ns.reserve(QF->NbNodes()+1);
3411 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3412 while ( anIter->more() )
3413 Ns.push_back( cast2Node(anIter->next()) );
3414 Ns.push_back( Ns[0] );
3416 for(int i=0; i<QF->NbNodes(); i=i+2) {
3417 if ( !surface.IsNull() ) {
3418 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3419 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3420 gp_XY uv = ( uv1 + uv2 ) / 2.;
3421 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3422 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3425 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3426 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3427 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3429 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3430 fabs( Ns[i+1]->Y() - y ) > disttol ||
3431 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3432 // we have to move i+1 node
3433 aMesh->MoveNode( Ns[i+1], x, y, z );
3440 } // loop on face ids
3444 //=======================================================================
3445 //function : isReverse
3446 //purpose : Return true if normal of prevNodes is not co-directied with
3447 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3448 // iNotSame is where prevNodes and nextNodes are different.
3449 // If result is true then future volume orientation is OK
3450 //=======================================================================
3452 static bool isReverse(const SMDS_MeshElement* face,
3453 const vector<const SMDS_MeshNode*>& prevNodes,
3454 const vector<const SMDS_MeshNode*>& nextNodes,
3458 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3459 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3460 gp_XYZ extrDir( pN - pP ), faceNorm;
3461 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3463 return faceNorm * extrDir < 0.0;
3466 //=======================================================================
3468 * \brief Create elements by sweeping an element
3469 * \param elem - element to sweep
3470 * \param newNodesItVec - nodes generated from each node of the element
3471 * \param newElems - generated elements
3472 * \param nbSteps - number of sweeping steps
3473 * \param srcElements - to append elem for each generated element
3475 //=======================================================================
3477 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3478 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3479 list<const SMDS_MeshElement*>& newElems,
3481 SMESH_SequenceOfElemPtr& srcElements)
3483 //MESSAGE("sweepElement " << nbSteps);
3484 SMESHDS_Mesh* aMesh = GetMeshDS();
3486 const int nbNodes = elem->NbNodes();
3487 const int nbCorners = elem->NbCornerNodes();
3488 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3489 polyhedron creation !!! */
3490 // Loop on elem nodes:
3491 // find new nodes and detect same nodes indices
3492 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3493 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3494 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3495 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3497 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3498 vector<int> sames(nbNodes);
3499 vector<bool> isSingleNode(nbNodes);
3501 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3502 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3503 const SMDS_MeshNode* node = nnIt->first;
3504 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3505 if ( listNewNodes.empty() )
3508 itNN [ iNode ] = listNewNodes.begin();
3509 prevNod[ iNode ] = node;
3510 nextNod[ iNode ] = listNewNodes.front();
3512 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3513 corner node of linear */
3514 if ( prevNod[ iNode ] != nextNod [ iNode ])
3515 nbDouble += !isSingleNode[iNode];
3517 if( iNode < nbCorners ) { // check corners only
3518 if ( prevNod[ iNode ] == nextNod [ iNode ])
3519 sames[nbSame++] = iNode;
3521 iNotSameNode = iNode;
3525 if ( nbSame == nbNodes || nbSame > 2) {
3526 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3530 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3532 // fix nodes order to have bottom normal external
3533 if ( baseType == SMDSEntity_Polygon )
3535 std::reverse( itNN.begin(), itNN.end() );
3536 std::reverse( prevNod.begin(), prevNod.end() );
3537 std::reverse( midlNod.begin(), midlNod.end() );
3538 std::reverse( nextNod.begin(), nextNod.end() );
3539 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3543 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3544 SMDS_MeshCell::applyInterlace( ind, itNN );
3545 SMDS_MeshCell::applyInterlace( ind, prevNod );
3546 SMDS_MeshCell::applyInterlace( ind, nextNod );
3547 SMDS_MeshCell::applyInterlace( ind, midlNod );
3548 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3551 sames[nbSame] = iNotSameNode;
3552 for ( int j = 0; j <= nbSame; ++j )
3553 for ( size_t i = 0; i < ind.size(); ++i )
3554 if ( ind[i] == sames[j] )
3559 iNotSameNode = sames[nbSame];
3564 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3566 iSameNode = sames[ nbSame-1 ];
3567 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3568 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3569 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3572 // make new elements
3573 for (int iStep = 0; iStep < nbSteps; iStep++ )
3576 for ( iNode = 0; iNode < nbNodes; iNode++ )
3578 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3579 nextNod[ iNode ] = *itNN[ iNode ]++;
3582 SMDS_MeshElement* aNewElem = 0;
3583 /*if(!elem->IsPoly())*/ {
3584 switch ( baseType ) {
3586 case SMDSEntity_Node: { // sweep NODE
3587 if ( nbSame == 0 ) {
3588 if ( isSingleNode[0] )
3589 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3591 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3597 case SMDSEntity_Edge: { // sweep EDGE
3598 if ( nbDouble == 0 )
3600 if ( nbSame == 0 ) // ---> quadrangle
3601 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3602 nextNod[ 1 ], nextNod[ 0 ] );
3603 else // ---> triangle
3604 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3605 nextNod[ iNotSameNode ] );
3607 else // ---> polygon
3609 vector<const SMDS_MeshNode*> poly_nodes;
3610 poly_nodes.push_back( prevNod[0] );
3611 poly_nodes.push_back( prevNod[1] );
3612 if ( prevNod[1] != nextNod[1] )
3614 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3615 poly_nodes.push_back( nextNod[1] );
3617 if ( prevNod[0] != nextNod[0] )
3619 poly_nodes.push_back( nextNod[0] );
3620 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3622 switch ( poly_nodes.size() ) {
3624 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3627 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3628 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3631 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3636 case SMDSEntity_Triangle: // TRIANGLE --->
3638 if ( nbDouble > 0 ) break;
3639 if ( nbSame == 0 ) // ---> pentahedron
3640 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3641 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3643 else if ( nbSame == 1 ) // ---> pyramid
3644 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3645 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3646 nextNod[ iSameNode ]);
3648 else // 2 same nodes: ---> tetrahedron
3649 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3650 nextNod[ iNotSameNode ]);
3653 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3657 if ( nbDouble+nbSame == 2 )
3659 if(nbSame==0) { // ---> quadratic quadrangle
3660 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3661 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3663 else { //(nbSame==1) // ---> quadratic triangle
3665 return; // medium node on axis
3667 else if(sames[0]==0)
3668 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3669 nextNod[2], midlNod[1], prevNod[2]);
3671 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3672 midlNod[0], nextNod[2], prevNod[2]);
3675 else if ( nbDouble == 3 )
3677 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3678 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3679 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3686 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3687 if ( nbDouble > 0 ) break;
3689 if ( nbSame == 0 ) // ---> hexahedron
3690 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3691 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3693 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3694 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3695 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3696 nextNod[ iSameNode ]);
3697 newElems.push_back( aNewElem );
3698 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3699 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3700 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3702 else if ( nbSame == 2 ) { // ---> pentahedron
3703 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3704 // iBeforeSame is same too
3705 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3706 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3707 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3709 // iAfterSame is same too
3710 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3711 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3712 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3716 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3717 if ( nbDouble+nbSame != 3 ) break;
3719 // ---> pentahedron with 15 nodes
3720 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3721 nextNod[0], nextNod[1], nextNod[2],
3722 prevNod[3], prevNod[4], prevNod[5],
3723 nextNod[3], nextNod[4], nextNod[5],
3724 midlNod[0], midlNod[1], midlNod[2]);
3726 else if(nbSame==1) {
3727 // ---> 2d order pyramid of 13 nodes
3728 int apex = iSameNode;
3729 int i0 = ( apex + 1 ) % nbCorners;
3730 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3734 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3735 nextNod[i0], nextNod[i1], prevNod[apex],
3736 prevNod[i01], midlNod[i0],
3737 nextNod[i01], midlNod[i1],
3738 prevNod[i1a], prevNod[i0a],
3739 nextNod[i0a], nextNod[i1a]);
3741 else if(nbSame==2) {
3742 // ---> 2d order tetrahedron of 10 nodes
3743 int n1 = iNotSameNode;
3744 int n2 = ( n1 + 1 ) % nbCorners;
3745 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3749 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3750 prevNod[n12], prevNod[n23], prevNod[n31],
3751 midlNod[n1], nextNod[n12], nextNod[n31]);
3755 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3756 if ( nbDouble != 4 ) break;
3758 // ---> hexahedron with 20 nodes
3759 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3760 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3761 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3762 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3763 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3765 else if(nbSame==1) {
3766 // ---> pyramid + pentahedron - can not be created since it is needed
3767 // additional middle node at the center of face
3768 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3771 else if( nbSame == 2 ) {
3772 // ---> 2d order Pentahedron with 15 nodes
3774 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3775 // iBeforeSame is same too
3782 // iAfterSame is same too
3792 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3793 prevNod[n4], prevNod[n5], nextNod[n5],
3794 prevNod[n12], midlNod[n2], nextNod[n12],
3795 prevNod[n45], midlNod[n5], nextNod[n45],
3796 prevNod[n14], prevNod[n25], nextNod[n25]);
3800 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3802 if( nbSame == 0 && nbDouble == 9 ) {
3803 // ---> tri-quadratic hexahedron with 27 nodes
3804 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3805 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3806 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3807 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3808 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3809 prevNod[8], // bottom center
3810 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3811 nextNod[8], // top center
3812 midlNod[8]);// elem center
3820 case SMDSEntity_Polygon: { // sweep POLYGON
3822 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3823 // ---> hexagonal prism
3824 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3825 prevNod[3], prevNod[4], prevNod[5],
3826 nextNod[0], nextNod[1], nextNod[2],
3827 nextNod[3], nextNod[4], nextNod[5]);
3836 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3838 if ( baseType != SMDSEntity_Polygon )
3840 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3841 SMDS_MeshCell::applyInterlace( ind, prevNod );
3842 SMDS_MeshCell::applyInterlace( ind, nextNod );
3843 SMDS_MeshCell::applyInterlace( ind, midlNod );
3844 SMDS_MeshCell::applyInterlace( ind, itNN );
3845 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3846 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3848 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3849 vector<int> quantities (nbNodes + 2);
3850 polyedre_nodes.clear();
3854 for (int inode = 0; inode < nbNodes; inode++)
3855 polyedre_nodes.push_back( prevNod[inode] );
3856 quantities.push_back( nbNodes );
3859 polyedre_nodes.push_back( nextNod[0] );
3860 for (int inode = nbNodes; inode-1; --inode )
3861 polyedre_nodes.push_back( nextNod[inode-1] );
3862 quantities.push_back( nbNodes );
3865 for (int iface = 0; iface < nbNodes; iface++)
3867 const int prevNbNodes = polyedre_nodes.size();
3868 int inextface = (iface+1) % nbNodes;
3869 polyedre_nodes.push_back( prevNod[inextface] );
3870 polyedre_nodes.push_back( prevNod[iface] );
3871 if ( prevNod[iface] != nextNod[iface] )
3873 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
3874 polyedre_nodes.push_back( nextNod[iface] );
3876 if ( prevNod[inextface] != nextNod[inextface] )
3878 polyedre_nodes.push_back( nextNod[inextface] );
3879 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
3881 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
3882 if ( nbFaceNodes > 2 )
3883 quantities.push_back( nbFaceNodes );
3884 else // degenerated face
3885 polyedre_nodes.resize( prevNbNodes );
3887 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3891 newElems.push_back( aNewElem );
3892 myLastCreatedElems.Append(aNewElem);
3893 srcElements.Append( elem );
3896 // set new prev nodes
3897 for ( iNode = 0; iNode < nbNodes; iNode++ )
3898 prevNod[ iNode ] = nextNod[ iNode ];
3903 //=======================================================================
3905 * \brief Create 1D and 2D elements around swept elements
3906 * \param mapNewNodes - source nodes and ones generated from them
3907 * \param newElemsMap - source elements and ones generated from them
3908 * \param elemNewNodesMap - nodes generated from each node of each element
3909 * \param elemSet - all swept elements
3910 * \param nbSteps - number of sweeping steps
3911 * \param srcElements - to append elem for each generated element
3913 //=======================================================================
3915 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3916 TElemOfElemListMap & newElemsMap,
3917 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3918 TIDSortedElemSet& elemSet,
3920 SMESH_SequenceOfElemPtr& srcElements)
3922 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3923 SMESHDS_Mesh* aMesh = GetMeshDS();
3925 // Find nodes belonging to only one initial element - sweep them to get edges.
3927 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3928 for ( ; nList != mapNewNodes.end(); nList++ )
3930 const SMDS_MeshNode* node =
3931 static_cast<const SMDS_MeshNode*>( nList->first );
3932 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3933 int nbInitElems = 0;
3934 const SMDS_MeshElement* el = 0;
3935 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3936 while ( eIt->more() && nbInitElems < 2 ) {
3938 SMDSAbs_ElementType type = el->GetType();
3939 if ( type == SMDSAbs_Volume || type < highType ) continue;
3940 if ( type > highType ) {
3944 nbInitElems += elemSet.count(el);
3946 if ( nbInitElems < 2 ) {
3947 bool NotCreateEdge = el && el->IsMediumNode(node);
3948 if(!NotCreateEdge) {
3949 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3950 list<const SMDS_MeshElement*> newEdges;
3951 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3956 // Make a ceiling for each element ie an equal element of last new nodes.
3957 // Find free links of faces - make edges and sweep them into faces.
3959 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3960 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3961 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
3963 const SMDS_MeshElement* elem = itElem->first;
3964 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3966 if(itElem->second.size()==0) continue;
3968 const bool isQuadratic = elem->IsQuadratic();
3970 if ( elem->GetType() == SMDSAbs_Edge ) {
3971 // create a ceiling edge
3972 if ( !isQuadratic ) {
3973 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3974 vecNewNodes[ 1 ]->second.back())) {
3975 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3976 vecNewNodes[ 1 ]->second.back()));
3977 srcElements.Append( myLastCreatedElems.Last() );
3981 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3982 vecNewNodes[ 1 ]->second.back(),
3983 vecNewNodes[ 2 ]->second.back())) {
3984 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3985 vecNewNodes[ 1 ]->second.back(),
3986 vecNewNodes[ 2 ]->second.back()));
3987 srcElements.Append( myLastCreatedElems.Last() );
3991 if ( elem->GetType() != SMDSAbs_Face )
3994 bool hasFreeLinks = false;
3996 TIDSortedElemSet avoidSet;
3997 avoidSet.insert( elem );
3999 set<const SMDS_MeshNode*> aFaceLastNodes;
4000 int iNode, nbNodes = vecNewNodes.size();
4001 if ( !isQuadratic ) {
4002 // loop on the face nodes
4003 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4004 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4005 // look for free links of the face
4006 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4007 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4008 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4009 // check if a link is free
4010 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4011 hasFreeLinks = true;
4012 // make an edge and a ceiling for a new edge
4013 if ( !aMesh->FindEdge( n1, n2 )) {
4014 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4015 srcElements.Append( myLastCreatedElems.Last() );
4017 n1 = vecNewNodes[ iNode ]->second.back();
4018 n2 = vecNewNodes[ iNext ]->second.back();
4019 if ( !aMesh->FindEdge( n1, n2 )) {
4020 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4021 srcElements.Append( myLastCreatedElems.Last() );
4026 else { // elem is quadratic face
4027 int nbn = nbNodes/2;
4028 for ( iNode = 0; iNode < nbn; iNode++ ) {
4029 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4030 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4031 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4032 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4033 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4034 // check if a link is free
4035 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4036 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4037 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4038 hasFreeLinks = true;
4039 // make an edge and a ceiling for a new edge
4041 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4042 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4043 srcElements.Append( myLastCreatedElems.Last() );
4045 n1 = vecNewNodes[ iNode ]->second.back();
4046 n2 = vecNewNodes[ iNext ]->second.back();
4047 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4048 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4049 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4050 srcElements.Append( myLastCreatedElems.Last() );
4054 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4055 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4059 // sweep free links into faces
4061 if ( hasFreeLinks ) {
4062 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4063 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4065 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4066 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4067 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4068 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4070 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4071 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4072 std::advance( v, volNb );
4073 // find indices of free faces of a volume and their source edges
4074 list< int > freeInd;
4075 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4076 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4077 int iF, nbF = vTool.NbFaces();
4078 for ( iF = 0; iF < nbF; iF ++ ) {
4079 if (vTool.IsFreeFace( iF ) &&
4080 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4081 initNodeSet != faceNodeSet) // except an initial face
4083 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4085 freeInd.push_back( iF );
4086 // find source edge of a free face iF
4087 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4088 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4089 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4090 initNodeSet.begin(), initNodeSet.end(),
4091 commonNodes.begin());
4092 if ( (*v)->IsQuadratic() )
4093 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4095 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4097 if ( !srcEdges.back() )
4099 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4100 << iF << " of volume #" << vTool.ID() << endl;
4105 if ( freeInd.empty() )
4108 // create faces for all steps;
4109 // if such a face has been already created by sweep of edge,
4110 // assure that its orientation is OK
4111 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4112 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4113 vTool.SetExternalNormal();
4114 const int nextShift = vTool.IsForward() ? +1 : -1;
4115 list< int >::iterator ind = freeInd.begin();
4116 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4117 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4119 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4120 int nbn = vTool.NbFaceNodes( *ind );
4121 const SMDS_MeshElement * f = 0;
4122 if ( nbn == 3 ) ///// triangle
4124 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4126 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4128 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4130 nodes[ 1 + nextShift ] };
4132 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4134 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4138 else if ( nbn == 4 ) ///// quadrangle
4140 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4142 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4144 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4145 nodes[ 2 ], nodes[ 2+nextShift ] };
4147 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4149 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4150 newOrder[ 2 ], newOrder[ 3 ]));
4153 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4155 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4157 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4159 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4161 nodes[2 + 2*nextShift],
4162 nodes[3 - 2*nextShift],
4164 nodes[3 + 2*nextShift]};
4166 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4168 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4176 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4178 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4179 nodes[1], nodes[3], nodes[5], nodes[7] );
4181 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4183 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4184 nodes[4 - 2*nextShift],
4186 nodes[4 + 2*nextShift],
4188 nodes[5 - 2*nextShift],
4190 nodes[5 + 2*nextShift] };
4192 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4194 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4195 newOrder[ 2 ], newOrder[ 3 ],
4196 newOrder[ 4 ], newOrder[ 5 ],
4197 newOrder[ 6 ], newOrder[ 7 ]));
4200 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4202 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4203 SMDSAbs_Face, /*noMedium=*/false);
4205 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4207 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4208 nodes[4 - 2*nextShift],
4210 nodes[4 + 2*nextShift],
4212 nodes[5 - 2*nextShift],
4214 nodes[5 + 2*nextShift],
4217 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4219 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4220 newOrder[ 2 ], newOrder[ 3 ],
4221 newOrder[ 4 ], newOrder[ 5 ],
4222 newOrder[ 6 ], newOrder[ 7 ],
4226 else //////// polygon
4228 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4229 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4231 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4233 if ( !vTool.IsForward() )
4234 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4236 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4238 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4242 while ( srcElements.Length() < myLastCreatedElems.Length() )
4243 srcElements.Append( *srcEdge );
4245 } // loop on free faces
4247 // go to the next volume
4249 while ( iVol++ < nbVolumesByStep ) v++;
4252 } // loop on volumes of one step
4253 } // sweep free links into faces
4255 // Make a ceiling face with a normal external to a volume
4257 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4259 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4261 lastVol.SetExternalNormal();
4262 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4263 int nbn = lastVol.NbFaceNodes( iF );
4265 if (!hasFreeLinks ||
4266 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4267 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4269 else if ( nbn == 4 )
4271 if (!hasFreeLinks ||
4272 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4273 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4275 else if ( nbn == 6 && isQuadratic )
4277 if (!hasFreeLinks ||
4278 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4279 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4280 nodes[1], nodes[3], nodes[5]));
4282 else if ( nbn == 8 && isQuadratic )
4284 if (!hasFreeLinks ||
4285 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4286 nodes[1], nodes[3], nodes[5], nodes[7]) )
4287 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4288 nodes[1], nodes[3], nodes[5], nodes[7]));
4290 else if ( nbn == 9 && isQuadratic )
4292 if (!hasFreeLinks ||
4293 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4294 SMDSAbs_Face, /*noMedium=*/false) )
4295 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4296 nodes[1], nodes[3], nodes[5], nodes[7],
4300 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4301 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4302 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4305 while ( srcElements.Length() < myLastCreatedElems.Length() )
4306 srcElements.Append( myLastCreatedElems.Last() );
4308 } // loop on swept elements
4311 //=======================================================================
4312 //function : RotationSweep
4314 //=======================================================================
4316 SMESH_MeshEditor::PGroupIDs
4317 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4318 const gp_Ax1& theAxis,
4319 const double theAngle,
4320 const int theNbSteps,
4321 const double theTol,
4322 const bool theMakeGroups,
4323 const bool theMakeWalls)
4325 myLastCreatedElems.Clear();
4326 myLastCreatedNodes.Clear();
4328 // source elements for each generated one
4329 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4331 MESSAGE( "RotationSweep()");
4333 aTrsf.SetRotation( theAxis, theAngle );
4335 aTrsf2.SetRotation( theAxis, theAngle/2. );
4337 gp_Lin aLine( theAxis );
4338 double aSqTol = theTol * theTol;
4340 SMESHDS_Mesh* aMesh = GetMeshDS();
4342 TNodeOfNodeListMap mapNewNodes;
4343 TElemOfVecOfNnlmiMap mapElemNewNodes;
4344 TElemOfElemListMap newElemsMap;
4346 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4347 myMesh->NbFaces(ORDER_QUADRATIC) +
4348 myMesh->NbVolumes(ORDER_QUADRATIC) );
4350 TIDSortedElemSet::iterator itElem;
4351 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4352 const SMDS_MeshElement* elem = *itElem;
4353 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4355 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4356 newNodesItVec.reserve( elem->NbNodes() );
4358 // loop on elem nodes
4359 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4360 while ( itN->more() )
4362 // check if a node has been already sweeped
4363 const SMDS_MeshNode* node = cast2Node( itN->next() );
4365 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4367 aXYZ.Coord( coord[0], coord[1], coord[2] );
4368 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4370 TNodeOfNodeListMapItr nIt =
4371 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4372 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4373 if ( listNewNodes.empty() )
4375 // check if we are to create medium nodes between corner ones
4376 bool needMediumNodes = false;
4377 if ( isQuadraticMesh )
4379 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4380 while (it->more() && !needMediumNodes )
4382 const SMDS_MeshElement* invElem = it->next();
4383 if ( invElem != elem && !theElems.count( invElem )) continue;
4384 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4385 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4386 needMediumNodes = true;
4391 const SMDS_MeshNode * newNode = node;
4392 for ( int i = 0; i < theNbSteps; i++ ) {
4394 if ( needMediumNodes ) // create a medium node
4396 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4397 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4398 myLastCreatedNodes.Append(newNode);
4399 srcNodes.Append( node );
4400 listNewNodes.push_back( newNode );
4401 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4404 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4406 // create a corner node
4407 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4408 myLastCreatedNodes.Append(newNode);
4409 srcNodes.Append( node );
4410 listNewNodes.push_back( newNode );
4413 listNewNodes.push_back( newNode );
4414 // if ( needMediumNodes )
4415 // listNewNodes.push_back( newNode );
4419 newNodesItVec.push_back( nIt );
4421 // make new elements
4422 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4426 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4428 PGroupIDs newGroupIDs;
4429 if ( theMakeGroups )
4430 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4436 //=======================================================================
4437 //function : CreateNode
4439 //=======================================================================
4440 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4443 const double tolnode,
4444 SMESH_SequenceOfNode& aNodes)
4446 // myLastCreatedElems.Clear();
4447 // myLastCreatedNodes.Clear();
4450 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4452 // try to search in sequence of existing nodes
4453 // if aNodes.Length()>0 we 'nave to use given sequence
4454 // else - use all nodes of mesh
4455 if(aNodes.Length()>0) {
4457 for(i=1; i<=aNodes.Length(); i++) {
4458 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4459 if(P1.Distance(P2)<tolnode)
4460 return aNodes.Value(i);
4464 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4465 while(itn->more()) {
4466 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4467 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4468 if(P1.Distance(P2)<tolnode)
4473 // create new node and return it
4474 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4475 //myLastCreatedNodes.Append(NewNode);
4480 //=======================================================================
4481 //function : ExtrusionSweep
4483 //=======================================================================
4485 SMESH_MeshEditor::PGroupIDs
4486 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4487 const gp_Vec& theStep,
4488 const int theNbSteps,
4489 TElemOfElemListMap& newElemsMap,
4490 const bool theMakeGroups,
4492 const double theTolerance)
4494 ExtrusParam aParams;
4495 aParams.myDir = gp_Dir(theStep);
4496 aParams.myNodes.Clear();
4497 aParams.mySteps = new TColStd_HSequenceOfReal;
4499 for(i=1; i<=theNbSteps; i++)
4500 aParams.mySteps->Append(theStep.Magnitude());
4503 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4507 //=======================================================================
4508 //function : ExtrusionSweep
4510 //=======================================================================
4512 SMESH_MeshEditor::PGroupIDs
4513 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4514 ExtrusParam& theParams,
4515 TElemOfElemListMap& newElemsMap,
4516 const bool theMakeGroups,
4518 const double theTolerance)
4520 myLastCreatedElems.Clear();
4521 myLastCreatedNodes.Clear();
4523 // source elements for each generated one
4524 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4526 SMESHDS_Mesh* aMesh = GetMeshDS();
4528 int nbsteps = theParams.mySteps->Length();
4530 TNodeOfNodeListMap mapNewNodes;
4531 //TNodeOfNodeVecMap mapNewNodes;
4532 TElemOfVecOfNnlmiMap mapElemNewNodes;
4533 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4535 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4536 myMesh->NbFaces(ORDER_QUADRATIC) +
4537 myMesh->NbVolumes(ORDER_QUADRATIC) );
4539 TIDSortedElemSet::iterator itElem;
4540 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4541 // check element type
4542 const SMDS_MeshElement* elem = *itElem;
4543 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4546 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4547 newNodesItVec.reserve( elem->NbNodes() );
4549 // loop on elem nodes
4550 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4551 while ( itN->more() )
4553 // check if a node has been already sweeped
4554 const SMDS_MeshNode* node = cast2Node( itN->next() );
4555 TNodeOfNodeListMap::iterator nIt =
4556 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4557 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4558 if ( listNewNodes.empty() )
4562 // check if we are to create medium nodes between corner ones
4563 bool needMediumNodes = false;
4564 if ( isQuadraticMesh )
4566 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4567 while (it->more() && !needMediumNodes )
4569 const SMDS_MeshElement* invElem = it->next();
4570 if ( invElem != elem && !theElems.count( invElem )) continue;
4571 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4572 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4573 needMediumNodes = true;
4577 double coord[] = { node->X(), node->Y(), node->Z() };
4578 for ( int i = 0; i < nbsteps; i++ )
4580 if ( needMediumNodes ) // create a medium node
4582 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4583 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4584 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4585 if( theFlags & EXTRUSION_FLAG_SEW ) {
4586 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4587 theTolerance, theParams.myNodes);
4588 listNewNodes.push_back( newNode );
4591 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4592 myLastCreatedNodes.Append(newNode);
4593 srcNodes.Append( node );
4594 listNewNodes.push_back( newNode );
4597 // create a corner node
4598 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4599 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4600 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4601 if( theFlags & EXTRUSION_FLAG_SEW ) {
4602 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4603 theTolerance, theParams.myNodes);
4604 listNewNodes.push_back( newNode );
4607 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4608 myLastCreatedNodes.Append(newNode);
4609 srcNodes.Append( node );
4610 listNewNodes.push_back( newNode );
4614 newNodesItVec.push_back( nIt );
4616 // make new elements
4617 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4620 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4621 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4623 PGroupIDs newGroupIDs;
4624 if ( theMakeGroups )
4625 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4630 //=======================================================================
4631 //function : ExtrusionAlongTrack
4633 //=======================================================================
4634 SMESH_MeshEditor::Extrusion_Error
4635 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4636 SMESH_subMesh* theTrack,
4637 const SMDS_MeshNode* theN1,
4638 const bool theHasAngles,
4639 list<double>& theAngles,
4640 const bool theLinearVariation,
4641 const bool theHasRefPoint,
4642 const gp_Pnt& theRefPoint,
4643 const bool theMakeGroups)
4645 MESSAGE("ExtrusionAlongTrack");
4646 myLastCreatedElems.Clear();
4647 myLastCreatedNodes.Clear();
4650 std::list<double> aPrms;
4651 TIDSortedElemSet::iterator itElem;
4654 TopoDS_Edge aTrackEdge;
4655 TopoDS_Vertex aV1, aV2;
4657 SMDS_ElemIteratorPtr aItE;
4658 SMDS_NodeIteratorPtr aItN;
4659 SMDSAbs_ElementType aTypeE;
4661 TNodeOfNodeListMap mapNewNodes;
4664 aNbE = theElements.size();
4667 return EXTR_NO_ELEMENTS;
4669 // 1.1 Track Pattern
4672 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4674 aItE = pSubMeshDS->GetElements();
4675 while ( aItE->more() ) {
4676 const SMDS_MeshElement* pE = aItE->next();
4677 aTypeE = pE->GetType();
4678 // Pattern must contain links only
4679 if ( aTypeE != SMDSAbs_Edge )
4680 return EXTR_PATH_NOT_EDGE;
4683 list<SMESH_MeshEditor_PathPoint> fullList;
4685 const TopoDS_Shape& aS = theTrack->GetSubShape();
4686 // Sub shape for the Pattern must be an Edge or Wire
4687 if( aS.ShapeType() == TopAbs_EDGE ) {
4688 aTrackEdge = TopoDS::Edge( aS );
4689 // the Edge must not be degenerated
4690 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4691 return EXTR_BAD_PATH_SHAPE;
4692 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4693 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4694 const SMDS_MeshNode* aN1 = aItN->next();
4695 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4696 const SMDS_MeshNode* aN2 = aItN->next();
4697 // starting node must be aN1 or aN2
4698 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4699 return EXTR_BAD_STARTING_NODE;
4700 aItN = pSubMeshDS->GetNodes();
4701 while ( aItN->more() ) {
4702 const SMDS_MeshNode* pNode = aItN->next();
4703 const SMDS_EdgePosition* pEPos =
4704 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4705 double aT = pEPos->GetUParameter();
4706 aPrms.push_back( aT );
4708 //Extrusion_Error err =
4709 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4710 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4711 list< SMESH_subMesh* > LSM;
4712 TopTools_SequenceOfShape Edges;
4713 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4714 while(itSM->more()) {
4715 SMESH_subMesh* SM = itSM->next();
4717 const TopoDS_Shape& aS = SM->GetSubShape();
4720 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4721 int startNid = theN1->GetID();
4722 TColStd_MapOfInteger UsedNums;
4724 int NbEdges = Edges.Length();
4726 for(; i<=NbEdges; i++) {
4728 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4729 for(; itLSM!=LSM.end(); itLSM++) {
4731 if(UsedNums.Contains(k)) continue;
4732 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4733 SMESH_subMesh* locTrack = *itLSM;
4734 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4735 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4736 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4737 const SMDS_MeshNode* aN1 = aItN->next();
4738 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4739 const SMDS_MeshNode* aN2 = aItN->next();
4740 // starting node must be aN1 or aN2
4741 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4742 // 2. Collect parameters on the track edge
4744 aItN = locMeshDS->GetNodes();
4745 while ( aItN->more() ) {
4746 const SMDS_MeshNode* pNode = aItN->next();
4747 const SMDS_EdgePosition* pEPos =
4748 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4749 double aT = pEPos->GetUParameter();
4750 aPrms.push_back( aT );
4752 list<SMESH_MeshEditor_PathPoint> LPP;
4753 //Extrusion_Error err =
4754 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4755 LLPPs.push_back(LPP);
4757 // update startN for search following egde
4758 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4759 else startNid = aN1->GetID();
4763 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4764 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4765 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4766 for(; itPP!=firstList.end(); itPP++) {
4767 fullList.push_back( *itPP );
4769 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4770 fullList.pop_back();
4772 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4773 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4774 itPP = currList.begin();
4775 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4776 gp_Dir D1 = PP1.Tangent();
4777 gp_Dir D2 = PP2.Tangent();
4778 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4779 (D1.Z()+D2.Z())/2 ) );
4780 PP1.SetTangent(Dnew);
4781 fullList.push_back(PP1);
4783 for(; itPP!=firstList.end(); itPP++) {
4784 fullList.push_back( *itPP );
4786 PP1 = fullList.back();
4787 fullList.pop_back();
4789 // if wire not closed
4790 fullList.push_back(PP1);
4794 return EXTR_BAD_PATH_SHAPE;
4797 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4798 theHasRefPoint, theRefPoint, theMakeGroups);
4802 //=======================================================================
4803 //function : ExtrusionAlongTrack
4805 //=======================================================================
4806 SMESH_MeshEditor::Extrusion_Error
4807 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4808 SMESH_Mesh* theTrack,
4809 const SMDS_MeshNode* theN1,
4810 const bool theHasAngles,
4811 list<double>& theAngles,
4812 const bool theLinearVariation,
4813 const bool theHasRefPoint,
4814 const gp_Pnt& theRefPoint,
4815 const bool theMakeGroups)
4817 myLastCreatedElems.Clear();
4818 myLastCreatedNodes.Clear();
4821 std::list<double> aPrms;
4822 TIDSortedElemSet::iterator itElem;
4825 TopoDS_Edge aTrackEdge;
4826 TopoDS_Vertex aV1, aV2;
4828 SMDS_ElemIteratorPtr aItE;
4829 SMDS_NodeIteratorPtr aItN;
4830 SMDSAbs_ElementType aTypeE;
4832 TNodeOfNodeListMap mapNewNodes;
4835 aNbE = theElements.size();
4838 return EXTR_NO_ELEMENTS;
4840 // 1.1 Track Pattern
4843 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4845 aItE = pMeshDS->elementsIterator();
4846 while ( aItE->more() ) {
4847 const SMDS_MeshElement* pE = aItE->next();
4848 aTypeE = pE->GetType();
4849 // Pattern must contain links only
4850 if ( aTypeE != SMDSAbs_Edge )
4851 return EXTR_PATH_NOT_EDGE;
4854 list<SMESH_MeshEditor_PathPoint> fullList;
4856 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4858 if( aS == SMESH_Mesh::PseudoShape() ) {
4859 //Mesh without shape
4860 const SMDS_MeshNode* currentNode = NULL;
4861 const SMDS_MeshNode* prevNode = theN1;
4862 std::vector<const SMDS_MeshNode*> aNodesList;
4863 aNodesList.push_back(theN1);
4864 int nbEdges = 0, conn=0;
4865 const SMDS_MeshElement* prevElem = NULL;
4866 const SMDS_MeshElement* currentElem = NULL;
4867 int totalNbEdges = theTrack->NbEdges();
4868 SMDS_ElemIteratorPtr nIt;
4869 bool isClosed = false;
4872 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4873 return EXTR_BAD_STARTING_NODE;
4876 conn = nbEdgeConnectivity(theN1);
4878 return EXTR_PATH_NOT_EDGE;
4880 aItE = theN1->GetInverseElementIterator();
4881 prevElem = aItE->next();
4882 currentElem = prevElem;
4884 if(totalNbEdges == 1 ) {
4885 nIt = currentElem->nodesIterator();
4886 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4887 if(currentNode == prevNode)
4888 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4889 aNodesList.push_back(currentNode);
4891 nIt = currentElem->nodesIterator();
4892 while( nIt->more() ) {
4893 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4894 if(currentNode == prevNode)
4895 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4896 aNodesList.push_back(currentNode);
4898 //case of the closed mesh
4899 if(currentNode == theN1) {
4905 conn = nbEdgeConnectivity(currentNode);
4907 return EXTR_PATH_NOT_EDGE;
4908 }else if( conn == 1 && nbEdges > 0 ) {
4913 prevNode = currentNode;
4914 aItE = currentNode->GetInverseElementIterator();
4915 currentElem = aItE->next();
4916 if( currentElem == prevElem)
4917 currentElem = aItE->next();
4918 nIt = currentElem->nodesIterator();
4919 prevElem = currentElem;
4925 if(nbEdges != totalNbEdges)
4926 return EXTR_PATH_NOT_EDGE;
4928 TopTools_SequenceOfShape Edges;
4929 double x1,x2,y1,y2,z1,z2;
4930 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4931 int startNid = theN1->GetID();
4932 for(int i = 1; i < aNodesList.size(); i++) {
4933 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
4934 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
4935 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
4936 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
4937 list<SMESH_MeshEditor_PathPoint> LPP;
4939 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
4940 LLPPs.push_back(LPP);
4941 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
4942 else startNid = aNodesList[i-1]->GetID();
4946 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4947 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4948 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4949 for(; itPP!=firstList.end(); itPP++) {
4950 fullList.push_back( *itPP );
4953 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4954 SMESH_MeshEditor_PathPoint PP2;
4955 fullList.pop_back();
4957 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4958 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4959 itPP = currList.begin();
4960 PP2 = currList.front();
4961 gp_Dir D1 = PP1.Tangent();
4962 gp_Dir D2 = PP2.Tangent();
4963 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4964 (D1.Z()+D2.Z())/2 ) );
4965 PP1.SetTangent(Dnew);
4966 fullList.push_back(PP1);
4968 for(; itPP!=currList.end(); itPP++) {
4969 fullList.push_back( *itPP );
4971 PP1 = fullList.back();
4972 fullList.pop_back();
4974 fullList.push_back(PP1);
4976 } // Sub shape for the Pattern must be an Edge or Wire
4977 else if( aS.ShapeType() == TopAbs_EDGE ) {
4978 aTrackEdge = TopoDS::Edge( aS );
4979 // the Edge must not be degenerated
4980 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4981 return EXTR_BAD_PATH_SHAPE;
4982 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4983 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4984 const SMDS_MeshNode* aN1 = aItN->next();
4985 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4986 const SMDS_MeshNode* aN2 = aItN->next();
4987 // starting node must be aN1 or aN2
4988 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4989 return EXTR_BAD_STARTING_NODE;
4990 aItN = pMeshDS->nodesIterator();
4991 while ( aItN->more() ) {
4992 const SMDS_MeshNode* pNode = aItN->next();
4993 if( pNode==aN1 || pNode==aN2 ) continue;
4994 const SMDS_EdgePosition* pEPos =
4995 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4996 double aT = pEPos->GetUParameter();
4997 aPrms.push_back( aT );
4999 //Extrusion_Error err =
5000 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5002 else if( aS.ShapeType() == TopAbs_WIRE ) {
5003 list< SMESH_subMesh* > LSM;
5004 TopTools_SequenceOfShape Edges;
5005 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5006 for(; eExp.More(); eExp.Next()) {
5007 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5008 if( BRep_Tool::Degenerated(E) ) continue;
5009 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5015 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5016 int startNid = theN1->GetID();
5017 TColStd_MapOfInteger UsedNums;
5018 int NbEdges = Edges.Length();
5020 for(; i<=NbEdges; i++) {
5022 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5023 for(; itLSM!=LSM.end(); itLSM++) {
5025 if(UsedNums.Contains(k)) continue;
5026 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5027 SMESH_subMesh* locTrack = *itLSM;
5028 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5029 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5030 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5031 const SMDS_MeshNode* aN1 = aItN->next();
5032 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5033 const SMDS_MeshNode* aN2 = aItN->next();
5034 // starting node must be aN1 or aN2
5035 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5036 // 2. Collect parameters on the track edge
5038 aItN = locMeshDS->GetNodes();
5039 while ( aItN->more() ) {
5040 const SMDS_MeshNode* pNode = aItN->next();
5041 const SMDS_EdgePosition* pEPos =
5042 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5043 double aT = pEPos->GetUParameter();
5044 aPrms.push_back( aT );
5046 list<SMESH_MeshEditor_PathPoint> LPP;
5047 //Extrusion_Error err =
5048 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5049 LLPPs.push_back(LPP);
5051 // update startN for search following egde
5052 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5053 else startNid = aN1->GetID();
5057 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5058 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5059 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5060 for(; itPP!=firstList.end(); itPP++) {
5061 fullList.push_back( *itPP );
5063 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5064 fullList.pop_back();
5066 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5067 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5068 itPP = currList.begin();
5069 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5070 gp_Dir D1 = PP1.Tangent();
5071 gp_Dir D2 = PP2.Tangent();
5072 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5073 (D1.Z()+D2.Z())/2 ) );
5074 PP1.SetTangent(Dnew);
5075 fullList.push_back(PP1);
5077 for(; itPP!=currList.end(); itPP++) {
5078 fullList.push_back( *itPP );
5080 PP1 = fullList.back();
5081 fullList.pop_back();
5083 // if wire not closed
5084 fullList.push_back(PP1);
5088 return EXTR_BAD_PATH_SHAPE;
5091 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5092 theHasRefPoint, theRefPoint, theMakeGroups);
5096 //=======================================================================
5097 //function : MakeEdgePathPoints
5098 //purpose : auxilary for ExtrusionAlongTrack
5099 //=======================================================================
5100 SMESH_MeshEditor::Extrusion_Error
5101 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5102 const TopoDS_Edge& aTrackEdge,
5104 list<SMESH_MeshEditor_PathPoint>& LPP)
5106 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5108 aTolVec2=aTolVec*aTolVec;
5110 TopoDS_Vertex aV1, aV2;
5111 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5112 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5113 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5114 // 2. Collect parameters on the track edge
5115 aPrms.push_front( aT1 );
5116 aPrms.push_back( aT2 );
5119 if( FirstIsStart ) {
5130 SMESH_MeshEditor_PathPoint aPP;
5131 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5132 std::list<double>::iterator aItD = aPrms.begin();
5133 for(; aItD != aPrms.end(); ++aItD) {
5137 aC3D->D1( aT, aP3D, aVec );
5138 aL2 = aVec.SquareMagnitude();
5139 if ( aL2 < aTolVec2 )
5140 return EXTR_CANT_GET_TANGENT;
5141 gp_Dir aTgt( aVec );
5143 aPP.SetTangent( aTgt );
5144 aPP.SetParameter( aT );
5151 //=======================================================================
5152 //function : MakeExtrElements
5153 //purpose : auxilary for ExtrusionAlongTrack
5154 //=======================================================================
5155 SMESH_MeshEditor::Extrusion_Error
5156 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5157 list<SMESH_MeshEditor_PathPoint>& fullList,
5158 const bool theHasAngles,
5159 list<double>& theAngles,
5160 const bool theLinearVariation,
5161 const bool theHasRefPoint,
5162 const gp_Pnt& theRefPoint,
5163 const bool theMakeGroups)
5165 MESSAGE("MakeExtrElements");
5166 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5167 int aNbTP = fullList.size();
5168 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5170 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5171 LinearAngleVariation(aNbTP-1, theAngles);
5173 vector<double> aAngles( aNbTP );
5175 for(; j<aNbTP; ++j) {
5178 if ( theHasAngles ) {
5180 std::list<double>::iterator aItD = theAngles.begin();
5181 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5183 aAngles[j] = anAngle;
5186 // fill vector of path points with angles
5187 //aPPs.resize(fullList.size());
5189 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5190 for(; itPP!=fullList.end(); itPP++) {
5192 SMESH_MeshEditor_PathPoint PP = *itPP;
5193 PP.SetAngle(aAngles[j]);
5197 TNodeOfNodeListMap mapNewNodes;
5198 TElemOfVecOfNnlmiMap mapElemNewNodes;
5199 TElemOfElemListMap newElemsMap;
5200 TIDSortedElemSet::iterator itElem;
5203 SMDSAbs_ElementType aTypeE;
5204 // source elements for each generated one
5205 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5207 // 3. Center of rotation aV0
5208 gp_Pnt aV0 = theRefPoint;
5210 if ( !theHasRefPoint ) {
5212 aGC.SetCoord( 0.,0.,0. );
5214 itElem = theElements.begin();
5215 for ( ; itElem != theElements.end(); itElem++ ) {
5216 const SMDS_MeshElement* elem = *itElem;
5218 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5219 while ( itN->more() ) {
5220 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5225 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5226 list<const SMDS_MeshNode*> aLNx;
5227 mapNewNodes[node] = aLNx;
5229 gp_XYZ aXYZ( aX, aY, aZ );
5237 } // if (!theHasRefPoint) {
5238 mapNewNodes.clear();
5240 // 4. Processing the elements
5241 SMESHDS_Mesh* aMesh = GetMeshDS();
5243 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5244 // check element type
5245 const SMDS_MeshElement* elem = *itElem;
5246 aTypeE = elem->GetType();
5247 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5250 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5251 newNodesItVec.reserve( elem->NbNodes() );
5253 // loop on elem nodes
5255 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5256 while ( itN->more() )
5259 // check if a node has been already processed
5260 const SMDS_MeshNode* node =
5261 static_cast<const SMDS_MeshNode*>( itN->next() );
5262 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5263 if ( nIt == mapNewNodes.end() ) {
5264 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5265 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5268 aX = node->X(); aY = node->Y(); aZ = node->Z();
5270 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5271 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5272 gp_Ax1 anAx1, anAxT1T0;
5273 gp_Dir aDT1x, aDT0x, aDT1T0;
5278 aPN0.SetCoord(aX, aY, aZ);
5280 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5282 aDT0x= aPP0.Tangent();
5283 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5285 for ( j = 1; j < aNbTP; ++j ) {
5286 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5288 aDT1x = aPP1.Tangent();
5289 aAngle1x = aPP1.Angle();
5291 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5293 gp_Vec aV01x( aP0x, aP1x );
5294 aTrsf.SetTranslation( aV01x );
5297 aV1x = aV0x.Transformed( aTrsf );
5298 aPN1 = aPN0.Transformed( aTrsf );
5300 // rotation 1 [ T1,T0 ]
5301 aAngleT1T0=-aDT1x.Angle( aDT0x );
5302 if (fabs(aAngleT1T0) > aTolAng) {
5304 anAxT1T0.SetLocation( aV1x );
5305 anAxT1T0.SetDirection( aDT1T0 );
5306 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5308 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5312 if ( theHasAngles ) {
5313 anAx1.SetLocation( aV1x );
5314 anAx1.SetDirection( aDT1x );
5315 aTrsfRot.SetRotation( anAx1, aAngle1x );
5317 aPN1 = aPN1.Transformed( aTrsfRot );
5321 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5322 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5323 // create additional node
5324 double x = ( aPN1.X() + aPN0.X() )/2.;
5325 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5326 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5327 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5328 myLastCreatedNodes.Append(newNode);
5329 srcNodes.Append( node );
5330 listNewNodes.push_back( newNode );
5335 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5336 myLastCreatedNodes.Append(newNode);
5337 srcNodes.Append( node );
5338 listNewNodes.push_back( newNode );
5348 // if current elem is quadratic and current node is not medium
5349 // we have to check - may be it is needed to insert additional nodes
5350 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5351 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5352 if(listNewNodes.size()==aNbTP-1) {
5353 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5354 gp_XYZ P(node->X(), node->Y(), node->Z());
5355 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5357 for(i=0; i<aNbTP-1; i++) {
5358 const SMDS_MeshNode* N = *it;
5359 double x = ( N->X() + P.X() )/2.;
5360 double y = ( N->Y() + P.Y() )/2.;
5361 double z = ( N->Z() + P.Z() )/2.;
5362 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5363 srcNodes.Append( node );
5364 myLastCreatedNodes.Append(newN);
5367 P = gp_XYZ(N->X(),N->Y(),N->Z());
5369 listNewNodes.clear();
5370 for(i=0; i<2*(aNbTP-1); i++) {
5371 listNewNodes.push_back(aNodes[i]);
5377 newNodesItVec.push_back( nIt );
5379 // make new elements
5380 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5381 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5382 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5385 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5387 if ( theMakeGroups )
5388 generateGroups( srcNodes, srcElems, "extruded");
5394 //=======================================================================
5395 //function : LinearAngleVariation
5396 //purpose : auxilary for ExtrusionAlongTrack
5397 //=======================================================================
5398 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5399 list<double>& Angles)
5401 int nbAngles = Angles.size();
5402 if( nbSteps > nbAngles ) {
5403 vector<double> theAngles(nbAngles);
5404 list<double>::iterator it = Angles.begin();
5406 for(; it!=Angles.end(); it++) {
5408 theAngles[i] = (*it);
5411 double rAn2St = double( nbAngles ) / double( nbSteps );
5412 double angPrev = 0, angle;
5413 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5414 double angCur = rAn2St * ( iSt+1 );
5415 double angCurFloor = floor( angCur );
5416 double angPrevFloor = floor( angPrev );
5417 if ( angPrevFloor == angCurFloor )
5418 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5420 int iP = int( angPrevFloor );
5421 double angPrevCeil = ceil(angPrev);
5422 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5424 int iC = int( angCurFloor );
5425 if ( iC < nbAngles )
5426 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5428 iP = int( angPrevCeil );
5430 angle += theAngles[ iC ];
5432 res.push_back(angle);
5437 for(; it!=res.end(); it++)
5438 Angles.push_back( *it );
5443 //================================================================================
5445 * \brief Move or copy theElements applying theTrsf to their nodes
5446 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5447 * \param theTrsf - transformation to apply
5448 * \param theCopy - if true, create translated copies of theElems
5449 * \param theMakeGroups - if true and theCopy, create translated groups
5450 * \param theTargetMesh - mesh to copy translated elements into
5451 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5453 //================================================================================
5455 SMESH_MeshEditor::PGroupIDs
5456 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5457 const gp_Trsf& theTrsf,
5459 const bool theMakeGroups,
5460 SMESH_Mesh* theTargetMesh)
5462 myLastCreatedElems.Clear();
5463 myLastCreatedNodes.Clear();
5465 bool needReverse = false;
5466 string groupPostfix;
5467 switch ( theTrsf.Form() ) {
5469 MESSAGE("gp_PntMirror");
5471 groupPostfix = "mirrored";
5474 MESSAGE("gp_Ax1Mirror");
5475 groupPostfix = "mirrored";
5478 MESSAGE("gp_Ax2Mirror");
5480 groupPostfix = "mirrored";
5483 MESSAGE("gp_Rotation");
5484 groupPostfix = "rotated";
5486 case gp_Translation:
5487 MESSAGE("gp_Translation");
5488 groupPostfix = "translated";
5491 MESSAGE("gp_Scale");
5492 groupPostfix = "scaled";
5494 case gp_CompoundTrsf: // different scale by axis
5495 MESSAGE("gp_CompoundTrsf");
5496 groupPostfix = "scaled";
5500 needReverse = false;
5501 groupPostfix = "transformed";
5504 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5505 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5506 SMESHDS_Mesh* aMesh = GetMeshDS();
5509 // map old node to new one
5510 TNodeNodeMap nodeMap;
5512 // elements sharing moved nodes; those of them which have all
5513 // nodes mirrored but are not in theElems are to be reversed
5514 TIDSortedElemSet inverseElemSet;
5516 // source elements for each generated one
5517 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5519 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5520 TIDSortedElemSet orphanNode;
5522 if ( theElems.empty() ) // transform the whole mesh
5525 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5526 while ( eIt->more() ) theElems.insert( eIt->next() );
5528 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5529 while ( nIt->more() )
5531 const SMDS_MeshNode* node = nIt->next();
5532 if ( node->NbInverseElements() == 0)
5533 orphanNode.insert( node );
5537 // loop on elements to transform nodes : first orphan nodes then elems
5538 TIDSortedElemSet::iterator itElem;
5539 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5540 for (int i=0; i<2; i++)
5541 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5542 const SMDS_MeshElement* elem = *itElem;
5546 // loop on elem nodes
5547 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5548 while ( itN->more() ) {
5550 const SMDS_MeshNode* node = cast2Node( itN->next() );
5551 // check if a node has been already transformed
5552 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5553 nodeMap.insert( make_pair ( node, node ));
5554 if ( !n2n_isnew.second )
5558 coord[0] = node->X();
5559 coord[1] = node->Y();
5560 coord[2] = node->Z();
5561 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5562 if ( theTargetMesh ) {
5563 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5564 n2n_isnew.first->second = newNode;
5565 myLastCreatedNodes.Append(newNode);
5566 srcNodes.Append( node );
5568 else if ( theCopy ) {
5569 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5570 n2n_isnew.first->second = newNode;
5571 myLastCreatedNodes.Append(newNode);
5572 srcNodes.Append( node );
5575 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5576 // node position on shape becomes invalid
5577 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5578 ( SMDS_SpacePosition::originSpacePosition() );
5581 // keep inverse elements
5582 if ( !theCopy && !theTargetMesh && needReverse ) {
5583 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5584 while ( invElemIt->more() ) {
5585 const SMDS_MeshElement* iel = invElemIt->next();
5586 inverseElemSet.insert( iel );
5592 // either create new elements or reverse mirrored ones
5593 if ( !theCopy && !needReverse && !theTargetMesh )
5596 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5597 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5598 theElems.insert( *invElemIt );
5600 // Replicate or reverse elements
5602 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5604 const SMDS_MeshElement* elem = *itElem;
5605 if ( !elem || elem->GetType() == SMDSAbs_Node )
5608 int nbNodes = elem->NbNodes();
5609 int elemType = elem->GetType();
5611 if (elem->IsPoly()) {
5613 // polygon or polyhedral volume
5614 switch ( elemType ) {
5617 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5619 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5620 while (itN->more()) {
5621 const SMDS_MeshNode* node =
5622 static_cast<const SMDS_MeshNode*>(itN->next());
5623 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5624 if (nodeMapIt == nodeMap.end())
5625 break; // not all nodes transformed
5627 // reverse mirrored faces and volumes
5628 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5630 poly_nodes[iNode] = (*nodeMapIt).second;
5634 if ( iNode != nbNodes )
5635 continue; // not all nodes transformed
5637 if ( theTargetMesh ) {
5638 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5639 srcElems.Append( elem );
5641 else if ( theCopy ) {
5642 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5643 srcElems.Append( elem );
5646 aMesh->ChangePolygonNodes(elem, poly_nodes);
5650 case SMDSAbs_Volume:
5652 const SMDS_VtkVolume* aPolyedre =
5653 dynamic_cast<const SMDS_VtkVolume*>( elem );
5655 MESSAGE("Warning: bad volumic element");
5659 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5660 vector<int> quantities;
5662 bool allTransformed = true;
5663 int nbFaces = aPolyedre->NbFaces();
5664 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5665 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5666 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5667 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5668 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5669 if (nodeMapIt == nodeMap.end()) {
5670 allTransformed = false; // not all nodes transformed
5672 poly_nodes.push_back((*nodeMapIt).second);
5674 if ( needReverse && allTransformed )
5675 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5677 quantities.push_back(nbFaceNodes);
5679 if ( !allTransformed )
5680 continue; // not all nodes transformed
5682 if ( theTargetMesh ) {
5683 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5684 srcElems.Append( elem );
5686 else if ( theCopy ) {
5687 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5688 srcElems.Append( elem );
5691 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5703 const std::vector<int>& i = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5705 // find transformed nodes
5706 vector<const SMDS_MeshNode*> nodes(nbNodes);
5708 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5709 while ( itN->more() ) {
5710 const SMDS_MeshNode* node =
5711 static_cast<const SMDS_MeshNode*>( itN->next() );
5712 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5713 if ( nodeMapIt == nodeMap.end() )
5714 break; // not all nodes transformed
5715 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5717 if ( iNode != nbNodes )
5718 continue; // not all nodes transformed
5720 if ( theTargetMesh ) {
5721 if ( SMDS_MeshElement* copy =
5722 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5723 myLastCreatedElems.Append( copy );
5724 srcElems.Append( elem );
5727 else if ( theCopy ) {
5728 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5729 srcElems.Append( elem );
5732 // reverse element as it was reversed by transformation
5734 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5737 } // loop on elements
5739 PGroupIDs newGroupIDs;
5741 if ( ( theMakeGroups && theCopy ) ||
5742 ( theMakeGroups && theTargetMesh ) )
5743 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5748 //=======================================================================
5750 * \brief Create groups of elements made during transformation
5751 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5752 * \param elemGens - elements making corresponding myLastCreatedElems
5753 * \param postfix - to append to names of new groups
5755 //=======================================================================
5757 SMESH_MeshEditor::PGroupIDs
5758 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5759 const SMESH_SequenceOfElemPtr& elemGens,
5760 const std::string& postfix,
5761 SMESH_Mesh* targetMesh)
5763 PGroupIDs newGroupIDs( new list<int> );
5764 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5766 // Sort existing groups by types and collect their names
5768 // to store an old group and a generated new one
5769 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5770 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5772 set< string > groupNames;
5774 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5775 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5776 while ( groupIt->more() ) {
5777 SMESH_Group * group = groupIt->next();
5778 if ( !group ) continue;
5779 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5780 if ( !groupDS || groupDS->IsEmpty() ) continue;
5781 groupNames.insert( group->GetName() );
5782 groupDS->SetStoreName( group->GetName() );
5783 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5788 // loop on nodes and elements
5789 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5791 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5792 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5793 if ( gens.Length() != elems.Length() )
5794 throw SALOME_Exception(LOCALIZED("invalid args"));
5796 // loop on created elements
5797 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5799 const SMDS_MeshElement* sourceElem = gens( iElem );
5800 if ( !sourceElem ) {
5801 MESSAGE("generateGroups(): NULL source element");
5804 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5805 if ( groupsOldNew.empty() ) {
5806 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5807 ++iElem; // skip all elements made by sourceElem
5810 // collect all elements made by sourceElem
5811 list< const SMDS_MeshElement* > resultElems;
5812 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5813 if ( resElem != sourceElem )
5814 resultElems.push_back( resElem );
5815 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5816 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5817 if ( resElem != sourceElem )
5818 resultElems.push_back( resElem );
5819 // do not generate element groups from node ones
5820 if ( sourceElem->GetType() == SMDSAbs_Node &&
5821 elems( iElem )->GetType() != SMDSAbs_Node )
5824 // add resultElems to groups made by ones the sourceElem belongs to
5825 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5826 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5828 SMESHDS_GroupBase* oldGroup = gOldNew->first;
5829 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5831 SMDS_MeshGroup* & newGroup = gOldNew->second;
5832 if ( !newGroup )// create a new group
5835 string name = oldGroup->GetStoreName();
5836 if ( !targetMesh ) {
5840 while ( !groupNames.insert( name ).second ) // name exists
5846 TCollection_AsciiString nbStr(nb+1);
5847 name.resize( name.rfind('_')+1 );
5848 name += nbStr.ToCString();
5855 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5857 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5858 newGroup = & groupDS->SMDSGroup();
5859 newGroupIDs->push_back( id );
5862 // fill in a new group
5863 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5864 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5865 newGroup->Add( *resElemIt );
5868 } // loop on created elements
5869 }// loop on nodes and elements
5874 //================================================================================
5876 * \brief Return list of group of nodes close to each other within theTolerance
5877 * Search among theNodes or in the whole mesh if theNodes is empty using
5878 * an Octree algorithm
5880 //================================================================================
5882 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
5883 const double theTolerance,
5884 TListOfListOfNodes & theGroupsOfNodes)
5886 myLastCreatedElems.Clear();
5887 myLastCreatedNodes.Clear();
5889 if ( theNodes.empty() )
5890 { // get all nodes in the mesh
5891 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
5892 while ( nIt->more() )
5893 theNodes.insert( theNodes.end(),nIt->next());
5896 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
5900 //=======================================================================
5902 * \brief Implementation of search for the node closest to point
5904 //=======================================================================
5906 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
5908 //---------------------------------------------------------------------
5910 * \brief Constructor
5912 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
5914 myMesh = ( SMESHDS_Mesh* ) theMesh;
5916 TIDSortedNodeSet nodes;
5918 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
5919 while ( nIt->more() )
5920 nodes.insert( nodes.end(), nIt->next() );
5922 myOctreeNode = new SMESH_OctreeNode(nodes) ;
5924 // get max size of a leaf box
5925 SMESH_OctreeNode* tree = myOctreeNode;
5926 while ( !tree->isLeaf() )
5928 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5932 myHalfLeafSize = tree->maxSize() / 2.;
5935 //---------------------------------------------------------------------
5937 * \brief Move node and update myOctreeNode accordingly
5939 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
5941 myOctreeNode->UpdateByMoveNode( node, toPnt );
5942 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
5945 //---------------------------------------------------------------------
5947 * \brief Do it's job
5949 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
5951 map<double, const SMDS_MeshNode*> dist2Nodes;
5952 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
5953 if ( !dist2Nodes.empty() )
5954 return dist2Nodes.begin()->second;
5955 list<const SMDS_MeshNode*> nodes;
5956 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
5958 double minSqDist = DBL_MAX;
5959 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
5961 // sort leafs by their distance from thePnt
5962 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
5963 TDistTreeMap treeMap;
5964 list< SMESH_OctreeNode* > treeList;
5965 list< SMESH_OctreeNode* >::iterator trIt;
5966 treeList.push_back( myOctreeNode );
5968 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5969 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
5970 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
5972 SMESH_OctreeNode* tree = *trIt;
5973 if ( !tree->isLeaf() ) // put children to the queue
5975 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
5976 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5977 while ( cIt->more() )
5978 treeList.push_back( cIt->next() );
5980 else if ( tree->NbNodes() ) // put a tree to the treeMap
5982 const Bnd_B3d& box = tree->getBox();
5983 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
5984 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
5985 if ( !it_in.second ) // not unique distance to box center
5986 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
5989 // find distance after which there is no sense to check tree's
5990 double sqLimit = DBL_MAX;
5991 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
5992 if ( treeMap.size() > 5 ) {
5993 SMESH_OctreeNode* closestTree = sqDist_tree->second;
5994 const Bnd_B3d& box = closestTree->getBox();
5995 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
5996 sqLimit = limit * limit;
5998 // get all nodes from trees
5999 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6000 if ( sqDist_tree->first > sqLimit )
6002 SMESH_OctreeNode* tree = sqDist_tree->second;
6003 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6006 // find closest among nodes
6007 minSqDist = DBL_MAX;
6008 const SMDS_MeshNode* closestNode = 0;
6009 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6010 for ( ; nIt != nodes.end(); ++nIt ) {
6011 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6012 if ( minSqDist > sqDist ) {
6020 //---------------------------------------------------------------------
6024 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6026 //---------------------------------------------------------------------
6028 * \brief Return the node tree
6030 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6033 SMESH_OctreeNode* myOctreeNode;
6034 SMESHDS_Mesh* myMesh;
6035 double myHalfLeafSize; // max size of a leaf box
6038 //=======================================================================
6040 * \brief Return SMESH_NodeSearcher
6042 //=======================================================================
6044 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6046 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6049 // ========================================================================
6050 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6052 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6053 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6054 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6056 //=======================================================================
6058 * \brief Octal tree of bounding boxes of elements
6060 //=======================================================================
6062 class ElementBndBoxTree : public SMESH_Octree
6066 ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(), double tolerance = NodeRadius );
6067 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
6068 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6069 ~ElementBndBoxTree();
6072 ElementBndBoxTree() {}
6073 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6074 void buildChildrenData();
6075 Bnd_B3d* buildRootBox();
6077 //!< Bounding box of element
6078 struct ElementBox : public Bnd_B3d
6080 const SMDS_MeshElement* _element;
6081 int _refCount; // an ElementBox can be included in several tree branches
6082 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6084 vector< ElementBox* > _elements;
6087 //================================================================================
6089 * \brief ElementBndBoxTree creation
6091 //================================================================================
6093 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6094 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6096 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6097 _elements.reserve( nbElems );
6099 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6100 while ( elemIt->more() )
6101 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6103 if ( _elements.size() > MaxNbElemsInLeaf )
6109 //================================================================================
6113 //================================================================================
6115 ElementBndBoxTree::~ElementBndBoxTree()
6117 for ( int i = 0; i < _elements.size(); ++i )
6118 if ( --_elements[i]->_refCount <= 0 )
6119 delete _elements[i];
6122 //================================================================================
6124 * \brief Return the maximal box
6126 //================================================================================
6128 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6130 Bnd_B3d* box = new Bnd_B3d;
6131 for ( int i = 0; i < _elements.size(); ++i )
6132 box->Add( *_elements[i] );
6136 //================================================================================
6138 * \brief Redistrubute element boxes among children
6140 //================================================================================
6142 void ElementBndBoxTree::buildChildrenData()
6144 for ( int i = 0; i < _elements.size(); ++i )
6146 for (int j = 0; j < 8; j++)
6148 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6150 _elements[i]->_refCount++;
6151 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6154 _elements[i]->_refCount--;
6158 for (int j = 0; j < 8; j++)
6160 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6161 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6162 child->myIsLeaf = true;
6164 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6165 child->_elements.resize( child->_elements.size() ); // compact
6169 //================================================================================
6171 * \brief Return elements which can include the point
6173 //================================================================================
6175 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6176 TIDSortedElemSet& foundElems)
6178 if ( level() && getBox().IsOut( point.XYZ() ))
6183 for ( int i = 0; i < _elements.size(); ++i )
6184 if ( !_elements[i]->IsOut( point.XYZ() ))
6185 foundElems.insert( _elements[i]->_element );
6189 for (int i = 0; i < 8; i++)
6190 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6194 //================================================================================
6196 * \brief Return elements which can be intersected by the line
6198 //================================================================================
6200 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6201 TIDSortedElemSet& foundElems)
6203 if ( level() && getBox().IsOut( line ))
6208 for ( int i = 0; i < _elements.size(); ++i )
6209 if ( !_elements[i]->IsOut( line ))
6210 foundElems.insert( _elements[i]->_element );
6214 for (int i = 0; i < 8; i++)
6215 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6219 //================================================================================
6221 * \brief Construct the element box
6223 //================================================================================
6225 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6229 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6230 while ( nIt->more() )
6231 Add( SMESH_TNodeXYZ( cast2Node( nIt->next() )));
6232 Enlarge( tolerance );
6237 //=======================================================================
6239 * \brief Implementation of search for the elements by point and
6240 * of classification of point in 2D mesh
6242 //=======================================================================
6244 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6246 SMESHDS_Mesh* _mesh;
6247 SMDS_ElemIteratorPtr _meshPartIt;
6248 ElementBndBoxTree* _ebbTree;
6249 SMESH_NodeSearcherImpl* _nodeSearcher;
6250 SMDSAbs_ElementType _elementType;
6252 bool _outerFacesFound;
6253 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6255 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6256 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6257 ~SMESH_ElementSearcherImpl()
6259 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6260 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6262 virtual int FindElementsByPoint(const gp_Pnt& point,
6263 SMDSAbs_ElementType type,
6264 vector< const SMDS_MeshElement* >& foundElements);
6265 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6267 void GetElementsNearLine( const gp_Ax1& line,
6268 SMDSAbs_ElementType type,
6269 vector< const SMDS_MeshElement* >& foundElems);
6270 double getTolerance();
6271 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6272 const double tolerance, double & param);
6273 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6274 bool isOuterBoundary(const SMDS_MeshElement* face) const
6276 return _outerFaces.empty() || _outerFaces.count(face);
6278 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6280 const SMDS_MeshElement* _face;
6282 bool _coincides; //!< the line lays in face plane
6283 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6284 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6286 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6289 TIDSortedElemSet _faces;
6290 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6291 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6295 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6297 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6298 << ", _coincides="<<i._coincides << ")";
6301 //=======================================================================
6303 * \brief define tolerance for search
6305 //=======================================================================
6307 double SMESH_ElementSearcherImpl::getTolerance()
6309 if ( _tolerance < 0 )
6311 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6314 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6316 double boxSize = _nodeSearcher->getTree()->maxSize();
6317 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6319 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6321 double boxSize = _ebbTree->maxSize();
6322 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6324 if ( _tolerance == 0 )
6326 // define tolerance by size of a most complex element
6327 int complexType = SMDSAbs_Volume;
6328 while ( complexType > SMDSAbs_All &&
6329 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6331 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6333 if ( complexType == int( SMDSAbs_Node ))
6335 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6337 if ( meshInfo.NbNodes() > 2 )
6338 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6342 SMDS_ElemIteratorPtr elemIt =
6343 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6344 const SMDS_MeshElement* elem = elemIt->next();
6345 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6346 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6348 while ( nodeIt->more() )
6350 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6351 elemSize = max( dist, elemSize );
6354 _tolerance = 1e-4 * elemSize;
6360 //================================================================================
6362 * \brief Find intersection of the line and an edge of face and return parameter on line
6364 //================================================================================
6366 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6367 const SMDS_MeshElement* face,
6374 GeomAPI_ExtremaCurveCurve anExtCC;
6375 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6377 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6378 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6380 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6381 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6382 anExtCC.Init( lineCurve, edge);
6383 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6385 Quantity_Parameter pl, pe;
6386 anExtCC.LowerDistanceParameters( pl, pe );
6388 if ( ++nbInts == 2 )
6392 if ( nbInts > 0 ) param /= nbInts;
6395 //================================================================================
6397 * \brief Find all faces belonging to the outer boundary of mesh
6399 //================================================================================
6401 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6403 if ( _outerFacesFound ) return;
6405 // Collect all outer faces by passing from one outer face to another via their links
6406 // and BTW find out if there are internal faces at all.
6408 // checked links and links where outer boundary meets internal one
6409 set< SMESH_TLink > visitedLinks, seamLinks;
6411 // links to treat with already visited faces sharing them
6412 list < TFaceLink > startLinks;
6414 // load startLinks with the first outerFace
6415 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6416 _outerFaces.insert( outerFace );
6418 TIDSortedElemSet emptySet;
6419 while ( !startLinks.empty() )
6421 const SMESH_TLink& link = startLinks.front()._link;
6422 TIDSortedElemSet& faces = startLinks.front()._faces;
6424 outerFace = *faces.begin();
6425 // find other faces sharing the link
6426 const SMDS_MeshElement* f;
6427 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6430 // select another outer face among the found
6431 const SMDS_MeshElement* outerFace2 = 0;
6432 if ( faces.size() == 2 )
6434 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6436 else if ( faces.size() > 2 )
6438 seamLinks.insert( link );
6440 // link direction within the outerFace
6441 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6442 SMESH_TNodeXYZ( link.node2()));
6443 int i1 = outerFace->GetNodeIndex( link.node1() );
6444 int i2 = outerFace->GetNodeIndex( link.node2() );
6445 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6446 if ( rev ) n1n2.Reverse();
6448 gp_XYZ ofNorm, fNorm;
6449 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6451 // direction from the link inside outerFace
6452 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6453 // sort all other faces by angle with the dirInOF
6454 map< double, const SMDS_MeshElement* > angle2Face;
6455 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6456 for ( ; face != faces.end(); ++face )
6458 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6460 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6461 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6462 if ( angle < 0 ) angle += 2. * M_PI;
6463 angle2Face.insert( make_pair( angle, *face ));
6465 if ( !angle2Face.empty() )
6466 outerFace2 = angle2Face.begin()->second;
6469 // store the found outer face and add its links to continue seaching from
6472 _outerFaces.insert( outerFace );
6473 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6474 for ( int i = 0; i < nbNodes; ++i )
6476 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6477 if ( visitedLinks.insert( link2 ).second )
6478 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6481 startLinks.pop_front();
6483 _outerFacesFound = true;
6485 if ( !seamLinks.empty() )
6487 // There are internal boundaries touching the outher one,
6488 // find all faces of internal boundaries in order to find
6489 // faces of boundaries of holes, if any.
6494 _outerFaces.clear();
6498 //=======================================================================
6500 * \brief Find elements of given type where the given point is IN or ON.
6501 * Returns nb of found elements and elements them-selves.
6503 * 'ALL' type means elements of any type excluding nodes and 0D elements
6505 //=======================================================================
6507 int SMESH_ElementSearcherImpl::
6508 FindElementsByPoint(const gp_Pnt& point,
6509 SMDSAbs_ElementType type,
6510 vector< const SMDS_MeshElement* >& foundElements)
6512 foundElements.clear();
6514 double tolerance = getTolerance();
6516 // =================================================================================
6517 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6519 if ( !_nodeSearcher )
6520 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6522 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6523 if ( !closeNode ) return foundElements.size();
6525 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6526 return foundElements.size(); // to far from any node
6528 if ( type == SMDSAbs_Node )
6530 foundElements.push_back( closeNode );
6534 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6535 while ( elemIt->more() )
6536 foundElements.push_back( elemIt->next() );
6539 // =================================================================================
6540 else // elements more complex than 0D
6542 if ( !_ebbTree || _elementType != type )
6544 if ( _ebbTree ) delete _ebbTree;
6545 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6547 TIDSortedElemSet suspectElems;
6548 _ebbTree->getElementsNearPoint( point, suspectElems );
6549 TIDSortedElemSet::iterator elem = suspectElems.begin();
6550 for ( ; elem != suspectElems.end(); ++elem )
6551 if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6552 foundElements.push_back( *elem );
6554 return foundElements.size();
6557 //================================================================================
6559 * \brief Classify the given point in the closed 2D mesh
6561 //================================================================================
6563 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6565 double tolerance = getTolerance();
6566 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6568 if ( _ebbTree ) delete _ebbTree;
6569 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6571 // Algo: analyse transition of a line starting at the point through mesh boundary;
6572 // try three lines parallel to axis of the coordinate system and perform rough
6573 // analysis. If solution is not clear perform thorough analysis.
6575 const int nbAxes = 3;
6576 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6577 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6578 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6579 multimap< int, int > nbInt2Axis; // to find the simplest case
6580 for ( int axis = 0; axis < nbAxes; ++axis )
6582 gp_Ax1 lineAxis( point, axisDir[axis]);
6583 gp_Lin line ( lineAxis );
6585 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6586 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6588 // Intersect faces with the line
6590 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6591 TIDSortedElemSet::iterator face = suspectFaces.begin();
6592 for ( ; face != suspectFaces.end(); ++face )
6596 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6597 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6599 // perform intersection
6600 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6601 if ( !intersection.IsDone() )
6603 if ( intersection.IsInQuadric() )
6605 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6607 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6609 gp_Pnt intersectionPoint = intersection.Point(1);
6610 if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6611 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6614 // Analyse intersections roughly
6616 int nbInter = u2inters.size();
6620 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6621 if ( nbInter == 1 ) // not closed mesh
6622 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6624 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6627 if ( (f<0) == (l<0) )
6630 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6631 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6632 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6635 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6637 if ( _outerFacesFound ) break; // pass to thorough analysis
6639 } // three attempts - loop on CS axes
6641 // Analyse intersections thoroughly.
6642 // We make two loops maximum, on the first one we only exclude touching intersections,
6643 // on the second, if situation is still unclear, we gather and use information on
6644 // position of faces (internal or outer). If faces position is already gathered,
6645 // we make the second loop right away.
6647 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6649 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6650 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6652 int axis = nb_axis->second;
6653 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6655 gp_Ax1 lineAxis( point, axisDir[axis]);
6656 gp_Lin line ( lineAxis );
6658 // add tangent intersections to u2inters
6660 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6661 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6662 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6663 u2inters.insert(make_pair( param, *tgtInt ));
6664 tangentInters[ axis ].clear();
6666 // Count intersections before and after the point excluding touching ones.
6667 // If hasPositionInfo we count intersections of outer boundary only
6669 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6670 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6671 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6672 bool ok = ! u_int1->second._coincides;
6673 while ( ok && u_int1 != u2inters.end() )
6675 double u = u_int1->first;
6676 bool touchingInt = false;
6677 if ( ++u_int2 != u2inters.end() )
6679 // skip intersections at the same point (if the line passes through edge or node)
6681 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6687 // skip tangent intersections
6689 const SMDS_MeshElement* prevFace = u_int1->second._face;
6690 while ( ok && u_int2->second._coincides )
6692 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6698 ok = ( u_int2 != u2inters.end() );
6703 // skip intersections at the same point after tangent intersections
6706 double u2 = u_int2->first;
6708 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6714 // decide if we skipped a touching intersection
6715 if ( nbSamePnt + nbTgt > 0 )
6717 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6718 map< double, TInters >::iterator u_int = u_int1;
6719 for ( ; u_int != u_int2; ++u_int )
6721 if ( u_int->second._coincides ) continue;
6722 double dot = u_int->second._faceNorm * line.Direction();
6723 if ( dot > maxDot ) maxDot = dot;
6724 if ( dot < minDot ) minDot = dot;
6726 touchingInt = ( minDot*maxDot < 0 );
6731 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6742 u_int1 = u_int2; // to next intersection
6744 } // loop on intersections with one line
6748 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6751 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6754 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6755 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6757 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6760 if ( (f<0) == (l<0) )
6763 if ( hasPositionInfo )
6764 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
6766 } // loop on intersections of the tree lines - thorough analysis
6768 if ( !hasPositionInfo )
6770 // gather info on faces position - is face in the outer boundary or not
6771 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
6772 findOuterBoundary( u2inters.begin()->second._face );
6775 } // two attempts - with and w/o faces position info in the mesh
6777 return TopAbs_UNKNOWN;
6780 //=======================================================================
6782 * \brief Return elements possibly intersecting the line
6784 //=======================================================================
6786 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
6787 SMDSAbs_ElementType type,
6788 vector< const SMDS_MeshElement* >& foundElems)
6790 if ( !_ebbTree || _elementType != type )
6792 if ( _ebbTree ) delete _ebbTree;
6793 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6795 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
6796 _ebbTree->getElementsNearLine( line, suspectFaces );
6797 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
6800 //=======================================================================
6802 * \brief Return SMESH_ElementSearcher
6804 //=======================================================================
6806 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
6808 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
6811 //=======================================================================
6813 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
6815 //=======================================================================
6817 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
6819 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
6822 //=======================================================================
6824 * \brief Return true if the point is IN or ON of the element
6826 //=======================================================================
6828 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
6830 if ( element->GetType() == SMDSAbs_Volume)
6832 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
6835 // get ordered nodes
6837 vector< gp_XYZ > xyz;
6838 vector<const SMDS_MeshNode*> nodeList;
6840 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
6841 if ( element->IsQuadratic() ) {
6842 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
6843 nodeIt = f->interlacedNodesElemIterator();
6844 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
6845 nodeIt = e->interlacedNodesElemIterator();
6847 while ( nodeIt->more() )
6849 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
6850 xyz.push_back( SMESH_TNodeXYZ(node) );
6851 nodeList.push_back(node);
6854 int i, nbNodes = element->NbNodes();
6856 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
6858 // compute face normal
6859 gp_Vec faceNorm(0,0,0);
6860 xyz.push_back( xyz.front() );
6861 nodeList.push_back( nodeList.front() );
6862 for ( i = 0; i < nbNodes; ++i )
6864 gp_Vec edge1( xyz[i+1], xyz[i]);
6865 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
6866 faceNorm += edge1 ^ edge2;
6868 double normSize = faceNorm.Magnitude();
6869 if ( normSize <= tol )
6871 // degenerated face: point is out if it is out of all face edges
6872 for ( i = 0; i < nbNodes; ++i )
6874 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
6875 if ( !isOut( &edge, point, tol ))
6880 faceNorm /= normSize;
6882 // check if the point lays on face plane
6883 gp_Vec n2p( xyz[0], point );
6884 if ( fabs( n2p * faceNorm ) > tol )
6885 return true; // not on face plane
6887 // check if point is out of face boundary:
6888 // define it by closest transition of a ray point->infinity through face boundary
6889 // on the face plane.
6890 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
6891 // to find intersections of the ray with the boundary.
6893 gp_Vec plnNorm = ray ^ faceNorm;
6894 normSize = plnNorm.Magnitude();
6895 if ( normSize <= tol ) return false; // point coincides with the first node
6896 plnNorm /= normSize;
6897 // for each node of the face, compute its signed distance to the plane
6898 vector<double> dist( nbNodes + 1);
6899 for ( i = 0; i < nbNodes; ++i )
6901 gp_Vec n2p( xyz[i], point );
6902 dist[i] = n2p * plnNorm;
6904 dist.back() = dist.front();
6905 // find the closest intersection
6907 double rClosest, distClosest = 1e100;;
6909 for ( i = 0; i < nbNodes; ++i )
6912 if ( fabs( dist[i]) < tol )
6914 else if ( fabs( dist[i+1]) < tol )
6916 else if ( dist[i] * dist[i+1] < 0 )
6917 r = dist[i] / ( dist[i] - dist[i+1] );
6919 continue; // no intersection
6920 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
6921 gp_Vec p2int ( point, pInt);
6922 if ( p2int * ray > -tol ) // right half-space
6924 double intDist = p2int.SquareMagnitude();
6925 if ( intDist < distClosest )
6930 distClosest = intDist;
6935 return true; // no intesections - out
6937 // analyse transition
6938 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
6939 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
6940 gp_Vec p2int ( point, pClosest );
6941 bool out = (edgeNorm * p2int) < -tol;
6942 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
6945 // ray pass through a face node; analyze transition through an adjacent edge
6946 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
6947 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
6948 gp_Vec edgeAdjacent( p1, p2 );
6949 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
6950 bool out2 = (edgeNorm2 * p2int) < -tol;
6952 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
6953 return covexCorner ? (out || out2) : (out && out2);
6955 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
6957 // point is out of edge if it is NOT ON any straight part of edge
6958 // (we consider quadratic edge as being composed of two straight parts)
6959 for ( i = 1; i < nbNodes; ++i )
6961 gp_Vec edge( xyz[i-1], xyz[i]);
6962 gp_Vec n1p ( xyz[i-1], point);
6963 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
6966 gp_Vec n2p( xyz[i], point );
6967 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
6969 return false; // point is ON this part
6973 // Node or 0D element -------------------------------------------------------------------------
6975 gp_Vec n2p ( xyz[0], point );
6976 return n2p.Magnitude() <= tol;
6981 //=======================================================================
6982 //function : SimplifyFace
6984 //=======================================================================
6985 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
6986 vector<const SMDS_MeshNode *>& poly_nodes,
6987 vector<int>& quantities) const
6989 int nbNodes = faceNodes.size();
6994 set<const SMDS_MeshNode*> nodeSet;
6996 // get simple seq of nodes
6997 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6998 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6999 int iSimple = 0, nbUnique = 0;
7001 simpleNodes[iSimple++] = faceNodes[0];
7003 for (int iCur = 1; iCur < nbNodes; iCur++) {
7004 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7005 simpleNodes[iSimple++] = faceNodes[iCur];
7006 if (nodeSet.insert( faceNodes[iCur] ).second)
7010 int nbSimple = iSimple;
7011 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7021 bool foundLoop = (nbSimple > nbUnique);
7024 set<const SMDS_MeshNode*> loopSet;
7025 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7026 const SMDS_MeshNode* n = simpleNodes[iSimple];
7027 if (!loopSet.insert( n ).second) {
7031 int iC = 0, curLast = iSimple;
7032 for (; iC < curLast; iC++) {
7033 if (simpleNodes[iC] == n) break;
7035 int loopLen = curLast - iC;
7037 // create sub-element
7039 quantities.push_back(loopLen);
7040 for (; iC < curLast; iC++) {
7041 poly_nodes.push_back(simpleNodes[iC]);
7044 // shift the rest nodes (place from the first loop position)
7045 for (iC = curLast + 1; iC < nbSimple; iC++) {
7046 simpleNodes[iC - loopLen] = simpleNodes[iC];
7048 nbSimple -= loopLen;
7051 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7052 } // while (foundLoop)
7056 quantities.push_back(iSimple);
7057 for (int i = 0; i < iSimple; i++)
7058 poly_nodes.push_back(simpleNodes[i]);
7064 //=======================================================================
7065 //function : MergeNodes
7066 //purpose : In each group, the cdr of nodes are substituted by the first one
7068 //=======================================================================
7070 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7072 MESSAGE("MergeNodes");
7073 myLastCreatedElems.Clear();
7074 myLastCreatedNodes.Clear();
7076 SMESHDS_Mesh* aMesh = GetMeshDS();
7078 TNodeNodeMap nodeNodeMap; // node to replace - new node
7079 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7080 list< int > rmElemIds, rmNodeIds;
7082 // Fill nodeNodeMap and elems
7084 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7085 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7086 list<const SMDS_MeshNode*>& nodes = *grIt;
7087 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7088 const SMDS_MeshNode* nToKeep = *nIt;
7089 //MESSAGE("node to keep " << nToKeep->GetID());
7090 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7091 const SMDS_MeshNode* nToRemove = *nIt;
7092 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7093 if ( nToRemove != nToKeep ) {
7094 //MESSAGE(" node to remove " << nToRemove->GetID());
7095 rmNodeIds.push_back( nToRemove->GetID() );
7096 AddToSameGroups( nToKeep, nToRemove, aMesh );
7099 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7100 while ( invElemIt->more() ) {
7101 const SMDS_MeshElement* elem = invElemIt->next();
7106 // Change element nodes or remove an element
7108 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7109 for ( ; eIt != elems.end(); eIt++ ) {
7110 const SMDS_MeshElement* elem = *eIt;
7111 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7112 int nbNodes = elem->NbNodes();
7113 int aShapeId = FindShape( elem );
7115 set<const SMDS_MeshNode*> nodeSet;
7116 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7117 int iUnique = 0, iCur = 0, nbRepl = 0;
7118 vector<int> iRepl( nbNodes );
7120 // get new seq of nodes
7121 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7122 while ( itN->more() ) {
7123 const SMDS_MeshNode* n =
7124 static_cast<const SMDS_MeshNode*>( itN->next() );
7126 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7127 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7129 // BUG 0020185: begin
7131 bool stopRecur = false;
7132 set<const SMDS_MeshNode*> nodesRecur;
7133 nodesRecur.insert(n);
7134 while (!stopRecur) {
7135 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7136 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7137 n = (*nnIt_i).second;
7138 if (!nodesRecur.insert(n).second) {
7139 // error: recursive dependancy
7148 iRepl[ nbRepl++ ] = iCur;
7150 curNodes[ iCur ] = n;
7151 bool isUnique = nodeSet.insert( n ).second;
7153 uniqueNodes[ iUnique++ ] = n;
7154 if ( nbRepl && iRepl[ nbRepl-1 ] == iCur )
7155 --nbRepl; // n do not stick to a node of the elem
7160 // Analyse element topology after replacement
7163 int nbUniqueNodes = nodeSet.size();
7164 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7165 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7166 // Polygons and Polyhedral volumes
7167 if (elem->IsPoly()) {
7169 if (elem->GetType() == SMDSAbs_Face) {
7171 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7173 for (; inode < nbNodes; inode++) {
7174 face_nodes[inode] = curNodes[inode];
7177 vector<const SMDS_MeshNode *> polygons_nodes;
7178 vector<int> quantities;
7179 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7182 for (int iface = 0; iface < nbNew; iface++) {
7183 int nbNodes = quantities[iface];
7184 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7185 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7186 poly_nodes[ii] = polygons_nodes[inode];
7188 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7189 myLastCreatedElems.Append(newElem);
7191 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7194 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7195 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7196 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7198 if (nbNew > 0) quid = nbNew - 1;
7199 vector<int> newquant(quantities.begin()+quid, quantities.end());
7200 const SMDS_MeshElement* newElem = 0;
7201 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7202 myLastCreatedElems.Append(newElem);
7203 if ( aShapeId && newElem )
7204 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7205 rmElemIds.push_back(elem->GetID());
7208 rmElemIds.push_back(elem->GetID());
7212 else if (elem->GetType() == SMDSAbs_Volume) {
7213 // Polyhedral volume
7214 if (nbUniqueNodes < 4) {
7215 rmElemIds.push_back(elem->GetID());
7218 // each face has to be analyzed in order to check volume validity
7219 const SMDS_VtkVolume* aPolyedre =
7220 dynamic_cast<const SMDS_VtkVolume*>( elem );
7222 int nbFaces = aPolyedre->NbFaces();
7224 vector<const SMDS_MeshNode *> poly_nodes;
7225 vector<int> quantities;
7227 for (int iface = 1; iface <= nbFaces; iface++) {
7228 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7229 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7231 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7232 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7233 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7234 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7235 faceNode = (*nnIt).second;
7237 faceNodes[inode - 1] = faceNode;
7240 SimplifyFace(faceNodes, poly_nodes, quantities);
7243 if (quantities.size() > 3) {
7244 // to be done: remove coincident faces
7247 if (quantities.size() > 3)
7249 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7250 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7251 const SMDS_MeshElement* newElem = 0;
7252 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7253 myLastCreatedElems.Append(newElem);
7254 if ( aShapeId && newElem )
7255 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7256 rmElemIds.push_back(elem->GetID());
7260 rmElemIds.push_back(elem->GetID());
7271 // TODO not all the possible cases are solved. Find something more generic?
7272 switch ( nbNodes ) {
7273 case 2: ///////////////////////////////////// EDGE
7274 isOk = false; break;
7275 case 3: ///////////////////////////////////// TRIANGLE
7276 isOk = false; break;
7278 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7280 else { //////////////////////////////////// QUADRANGLE
7281 if ( nbUniqueNodes < 3 )
7283 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7284 isOk = false; // opposite nodes stick
7285 //MESSAGE("isOk " << isOk);
7288 case 6: ///////////////////////////////////// PENTAHEDRON
7289 if ( nbUniqueNodes == 4 ) {
7290 // ---------------------------------> tetrahedron
7292 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7293 // all top nodes stick: reverse a bottom
7294 uniqueNodes[ 0 ] = curNodes [ 1 ];
7295 uniqueNodes[ 1 ] = curNodes [ 0 ];
7297 else if (nbRepl == 3 &&
7298 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7299 // all bottom nodes stick: set a top before
7300 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7301 uniqueNodes[ 0 ] = curNodes [ 3 ];
7302 uniqueNodes[ 1 ] = curNodes [ 4 ];
7303 uniqueNodes[ 2 ] = curNodes [ 5 ];
7305 else if (nbRepl == 4 &&
7306 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7307 // a lateral face turns into a line: reverse a bottom
7308 uniqueNodes[ 0 ] = curNodes [ 1 ];
7309 uniqueNodes[ 1 ] = curNodes [ 0 ];
7314 else if ( nbUniqueNodes == 5 ) {
7315 // PENTAHEDRON --------------------> 2 tetrahedrons
7316 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7317 // a bottom node sticks with a linked top one
7319 SMDS_MeshElement* newElem =
7320 aMesh->AddVolume(curNodes[ 3 ],
7323 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7324 myLastCreatedElems.Append(newElem);
7326 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7327 // 2. : reverse a bottom
7328 uniqueNodes[ 0 ] = curNodes [ 1 ];
7329 uniqueNodes[ 1 ] = curNodes [ 0 ];
7339 if(elem->IsQuadratic()) { // Quadratic quadrangle
7351 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7354 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7356 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7357 uniqueNodes[0] = curNodes[0];
7358 uniqueNodes[1] = curNodes[2];
7359 uniqueNodes[2] = curNodes[3];
7360 uniqueNodes[3] = curNodes[5];
7361 uniqueNodes[4] = curNodes[6];
7362 uniqueNodes[5] = curNodes[7];
7365 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7366 uniqueNodes[0] = curNodes[0];
7367 uniqueNodes[1] = curNodes[1];
7368 uniqueNodes[2] = curNodes[2];
7369 uniqueNodes[3] = curNodes[4];
7370 uniqueNodes[4] = curNodes[5];
7371 uniqueNodes[5] = curNodes[6];
7374 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7375 uniqueNodes[0] = curNodes[1];
7376 uniqueNodes[1] = curNodes[2];
7377 uniqueNodes[2] = curNodes[3];
7378 uniqueNodes[3] = curNodes[5];
7379 uniqueNodes[4] = curNodes[6];
7380 uniqueNodes[5] = curNodes[0];
7383 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7384 uniqueNodes[0] = curNodes[0];
7385 uniqueNodes[1] = curNodes[1];
7386 uniqueNodes[2] = curNodes[3];
7387 uniqueNodes[3] = curNodes[4];
7388 uniqueNodes[4] = curNodes[6];
7389 uniqueNodes[5] = curNodes[7];
7392 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7393 uniqueNodes[0] = curNodes[0];
7394 uniqueNodes[1] = curNodes[2];
7395 uniqueNodes[2] = curNodes[3];
7396 uniqueNodes[3] = curNodes[1];
7397 uniqueNodes[4] = curNodes[6];
7398 uniqueNodes[5] = curNodes[7];
7401 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7402 uniqueNodes[0] = curNodes[0];
7403 uniqueNodes[1] = curNodes[1];
7404 uniqueNodes[2] = curNodes[2];
7405 uniqueNodes[3] = curNodes[4];
7406 uniqueNodes[4] = curNodes[5];
7407 uniqueNodes[5] = curNodes[7];
7410 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7411 uniqueNodes[0] = curNodes[0];
7412 uniqueNodes[1] = curNodes[1];
7413 uniqueNodes[2] = curNodes[3];
7414 uniqueNodes[3] = curNodes[4];
7415 uniqueNodes[4] = curNodes[2];
7416 uniqueNodes[5] = curNodes[7];
7419 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7420 uniqueNodes[0] = curNodes[0];
7421 uniqueNodes[1] = curNodes[1];
7422 uniqueNodes[2] = curNodes[2];
7423 uniqueNodes[3] = curNodes[4];
7424 uniqueNodes[4] = curNodes[5];
7425 uniqueNodes[5] = curNodes[3];
7430 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7433 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7437 //////////////////////////////////// HEXAHEDRON
7439 SMDS_VolumeTool hexa (elem);
7440 hexa.SetExternalNormal();
7441 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7442 //////////////////////// HEX ---> 1 tetrahedron
7443 for ( int iFace = 0; iFace < 6; iFace++ ) {
7444 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7445 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7446 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7447 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7448 // one face turns into a point ...
7449 int iOppFace = hexa.GetOppFaceIndex( iFace );
7450 ind = hexa.GetFaceNodesIndices( iOppFace );
7452 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7453 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7456 if ( nbStick == 1 ) {
7457 // ... and the opposite one - into a triangle.
7459 ind = hexa.GetFaceNodesIndices( iFace );
7460 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7467 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7468 //////////////////////// HEX ---> 1 prism
7469 int nbTria = 0, iTria[3];
7470 const int *ind; // indices of face nodes
7471 // look for triangular faces
7472 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7473 ind = hexa.GetFaceNodesIndices( iFace );
7474 TIDSortedNodeSet faceNodes;
7475 for ( iCur = 0; iCur < 4; iCur++ )
7476 faceNodes.insert( curNodes[ind[iCur]] );
7477 if ( faceNodes.size() == 3 )
7478 iTria[ nbTria++ ] = iFace;
7480 // check if triangles are opposite
7481 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7484 // set nodes of the bottom triangle
7485 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7487 for ( iCur = 0; iCur < 4; iCur++ )
7488 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7489 indB.push_back( ind[iCur] );
7490 if ( !hexa.IsForward() )
7491 std::swap( indB[0], indB[2] );
7492 for ( iCur = 0; iCur < 3; iCur++ )
7493 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7494 // set nodes of the top triangle
7495 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7496 for ( iCur = 0; iCur < 3; ++iCur )
7497 for ( int j = 0; j < 4; ++j )
7498 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7500 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7506 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7507 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7508 for ( int iFace = 0; iFace < 6; iFace++ ) {
7509 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7510 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7511 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7512 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7513 // one face turns into a point ...
7514 int iOppFace = hexa.GetOppFaceIndex( iFace );
7515 ind = hexa.GetFaceNodesIndices( iOppFace );
7517 iUnique = 2; // reverse a tetrahedron 1 bottom
7518 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7519 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7521 else if ( iUnique >= 0 )
7522 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7524 if ( nbStick == 0 ) {
7525 // ... and the opposite one is a quadrangle
7527 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7528 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7531 SMDS_MeshElement* newElem =
7532 aMesh->AddVolume(curNodes[ind[ 0 ]],
7535 curNodes[indTop[ 0 ]]);
7536 myLastCreatedElems.Append(newElem);
7538 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7545 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7546 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7547 // find indices of quad and tri faces
7548 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7549 for ( iFace = 0; iFace < 6; iFace++ ) {
7550 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7552 for ( iCur = 0; iCur < 4; iCur++ )
7553 nodeSet.insert( curNodes[ind[ iCur ]] );
7554 nbUniqueNodes = nodeSet.size();
7555 if ( nbUniqueNodes == 3 )
7556 iTriFace[ nbTri++ ] = iFace;
7557 else if ( nbUniqueNodes == 4 )
7558 iQuadFace[ nbQuad++ ] = iFace;
7560 if (nbQuad == 2 && nbTri == 4 &&
7561 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7562 // 2 opposite quadrangles stuck with a diagonal;
7563 // sample groups of merged indices: (0-4)(2-6)
7564 // --------------------------------------------> 2 tetrahedrons
7565 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7566 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7567 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7568 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7569 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7570 // stuck with 0-2 diagonal
7578 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7579 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7580 // stuck with 1-3 diagonal
7592 uniqueNodes[ 0 ] = curNodes [ i0 ];
7593 uniqueNodes[ 1 ] = curNodes [ i1d ];
7594 uniqueNodes[ 2 ] = curNodes [ i3d ];
7595 uniqueNodes[ 3 ] = curNodes [ i0t ];
7598 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7602 myLastCreatedElems.Append(newElem);
7604 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7607 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7608 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7609 // --------------------------------------------> prism
7610 // find 2 opposite triangles
7612 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7613 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7614 // find indices of kept and replaced nodes
7615 // and fill unique nodes of 2 opposite triangles
7616 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7617 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7618 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7619 // fill unique nodes
7622 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7623 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7624 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7626 // iCur of a linked node of the opposite face (make normals co-directed):
7627 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7628 // check that correspondent corners of triangles are linked
7629 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7632 uniqueNodes[ iUnique ] = n;
7633 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7642 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7645 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7652 } // switch ( nbNodes )
7654 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7656 if ( isOk ) { // the elem remains valid after sticking nodes
7657 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7659 // Change nodes of polyedre
7660 const SMDS_VtkVolume* aPolyedre =
7661 dynamic_cast<const SMDS_VtkVolume*>( elem );
7663 int nbFaces = aPolyedre->NbFaces();
7665 vector<const SMDS_MeshNode *> poly_nodes;
7666 vector<int> quantities (nbFaces);
7668 for (int iface = 1; iface <= nbFaces; iface++) {
7669 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7670 quantities[iface - 1] = nbFaceNodes;
7672 for (inode = 1; inode <= nbFaceNodes; inode++) {
7673 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7675 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7676 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7677 curNode = (*nnIt).second;
7679 poly_nodes.push_back(curNode);
7682 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7685 else // replace non-polyhedron elements
7687 const SMDSAbs_ElementType etyp = elem->GetType();
7688 const int elemId = elem->GetID();
7689 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7690 uniqueNodes.resize(nbUniqueNodes);
7692 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7694 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7695 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7696 if ( sm && newElem )
7697 sm->AddElement( newElem );
7698 if ( elem != newElem )
7699 ReplaceElemInGroups( elem, newElem, aMesh );
7703 // Remove invalid regular element or invalid polygon
7704 rmElemIds.push_back( elem->GetID() );
7707 } // loop on elements
7709 // Remove bad elements, then equal nodes (order important)
7711 Remove( rmElemIds, false );
7712 Remove( rmNodeIds, true );
7717 // ========================================================
7718 // class : SortableElement
7719 // purpose : allow sorting elements basing on their nodes
7720 // ========================================================
7721 class SortableElement : public set <const SMDS_MeshElement*>
7725 SortableElement( const SMDS_MeshElement* theElem )
7728 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7729 while ( nodeIt->more() )
7730 this->insert( nodeIt->next() );
7733 const SMDS_MeshElement* Get() const
7736 void Set(const SMDS_MeshElement* e) const
7741 mutable const SMDS_MeshElement* myElem;
7744 //=======================================================================
7745 //function : FindEqualElements
7746 //purpose : Return list of group of elements built on the same nodes.
7747 // Search among theElements or in the whole mesh if theElements is empty
7748 //=======================================================================
7749 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7750 TListOfListOfElementsID & theGroupsOfElementsID)
7752 myLastCreatedElems.Clear();
7753 myLastCreatedNodes.Clear();
7755 typedef set<const SMDS_MeshElement*> TElemsSet;
7756 typedef map< SortableElement, int > TMapOfNodeSet;
7757 typedef list<int> TGroupOfElems;
7760 if ( theElements.empty() )
7761 { // get all elements in the mesh
7762 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7763 while ( eIt->more() )
7764 elems.insert( elems.end(), eIt->next());
7767 elems = theElements;
7769 vector< TGroupOfElems > arrayOfGroups;
7770 TGroupOfElems groupOfElems;
7771 TMapOfNodeSet mapOfNodeSet;
7773 TElemsSet::iterator elemIt = elems.begin();
7774 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
7775 const SMDS_MeshElement* curElem = *elemIt;
7776 SortableElement SE(curElem);
7779 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7780 if( !(pp.second) ) {
7781 TMapOfNodeSet::iterator& itSE = pp.first;
7782 ind = (*itSE).second;
7783 arrayOfGroups[ind].push_back(curElem->GetID());
7786 groupOfElems.clear();
7787 groupOfElems.push_back(curElem->GetID());
7788 arrayOfGroups.push_back(groupOfElems);
7793 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7794 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7795 groupOfElems = *groupIt;
7796 if ( groupOfElems.size() > 1 ) {
7797 groupOfElems.sort();
7798 theGroupsOfElementsID.push_back(groupOfElems);
7803 //=======================================================================
7804 //function : MergeElements
7805 //purpose : In each given group, substitute all elements by the first one.
7806 //=======================================================================
7808 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7810 myLastCreatedElems.Clear();
7811 myLastCreatedNodes.Clear();
7813 typedef list<int> TListOfIDs;
7814 TListOfIDs rmElemIds; // IDs of elems to remove
7816 SMESHDS_Mesh* aMesh = GetMeshDS();
7818 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7819 while ( groupsIt != theGroupsOfElementsID.end() ) {
7820 TListOfIDs& aGroupOfElemID = *groupsIt;
7821 aGroupOfElemID.sort();
7822 int elemIDToKeep = aGroupOfElemID.front();
7823 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7824 aGroupOfElemID.pop_front();
7825 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7826 while ( idIt != aGroupOfElemID.end() ) {
7827 int elemIDToRemove = *idIt;
7828 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7829 // add the kept element in groups of removed one (PAL15188)
7830 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7831 rmElemIds.push_back( elemIDToRemove );
7837 Remove( rmElemIds, false );
7840 //=======================================================================
7841 //function : MergeEqualElements
7842 //purpose : Remove all but one of elements built on the same nodes.
7843 //=======================================================================
7845 void SMESH_MeshEditor::MergeEqualElements()
7847 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
7848 to merge equal elements in the whole mesh */
7849 TListOfListOfElementsID aGroupsOfElementsID;
7850 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7851 MergeElements(aGroupsOfElementsID);
7854 //=======================================================================
7855 //function : FindFaceInSet
7856 //purpose : Return a face having linked nodes n1 and n2 and which is
7857 // - not in avoidSet,
7858 // - in elemSet provided that !elemSet.empty()
7859 // i1 and i2 optionally returns indices of n1 and n2
7860 //=======================================================================
7862 const SMDS_MeshElement*
7863 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
7864 const SMDS_MeshNode* n2,
7865 const TIDSortedElemSet& elemSet,
7866 const TIDSortedElemSet& avoidSet,
7872 const SMDS_MeshElement* face = 0;
7874 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
7875 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
7876 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
7878 //MESSAGE("in while ( invElemIt->more() && !face )");
7879 const SMDS_MeshElement* elem = invElemIt->next();
7880 if (avoidSet.count( elem ))
7882 if ( !elemSet.empty() && !elemSet.count( elem ))
7885 i1 = elem->GetNodeIndex( n1 );
7886 // find a n2 linked to n1
7887 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
7888 for ( int di = -1; di < 2 && !face; di += 2 )
7890 i2 = (i1+di+nbN) % nbN;
7891 if ( elem->GetNode( i2 ) == n2 )
7894 if ( !face && elem->IsQuadratic())
7896 // analysis for quadratic elements using all nodes
7897 const SMDS_VtkFace* F =
7898 dynamic_cast<const SMDS_VtkFace*>(elem);
7899 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7900 // use special nodes iterator
7901 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7902 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
7903 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
7905 const SMDS_MeshNode* n = cast2Node( anIter->next() );
7906 if ( n1 == prevN && n2 == n )
7910 else if ( n2 == prevN && n1 == n )
7912 face = elem; swap( i1, i2 );
7918 if ( n1ind ) *n1ind = i1;
7919 if ( n2ind ) *n2ind = i2;
7923 //=======================================================================
7924 //function : findAdjacentFace
7926 //=======================================================================
7928 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7929 const SMDS_MeshNode* n2,
7930 const SMDS_MeshElement* elem)
7932 TIDSortedElemSet elemSet, avoidSet;
7934 avoidSet.insert ( elem );
7935 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
7938 //=======================================================================
7939 //function : FindFreeBorder
7941 //=======================================================================
7943 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7945 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7946 const SMDS_MeshNode* theSecondNode,
7947 const SMDS_MeshNode* theLastNode,
7948 list< const SMDS_MeshNode* > & theNodes,
7949 list< const SMDS_MeshElement* >& theFaces)
7951 if ( !theFirstNode || !theSecondNode )
7953 // find border face between theFirstNode and theSecondNode
7954 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7958 theFaces.push_back( curElem );
7959 theNodes.push_back( theFirstNode );
7960 theNodes.push_back( theSecondNode );
7962 //vector<const SMDS_MeshNode*> nodes;
7963 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7964 TIDSortedElemSet foundElems;
7965 bool needTheLast = ( theLastNode != 0 );
7967 while ( nStart != theLastNode ) {
7968 if ( nStart == theFirstNode )
7969 return !needTheLast;
7971 // find all free border faces sharing form nStart
7973 list< const SMDS_MeshElement* > curElemList;
7974 list< const SMDS_MeshNode* > nStartList;
7975 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7976 while ( invElemIt->more() ) {
7977 const SMDS_MeshElement* e = invElemIt->next();
7978 if ( e == curElem || foundElems.insert( e ).second ) {
7980 int iNode = 0, nbNodes = e->NbNodes();
7981 //const SMDS_MeshNode* nodes[nbNodes+1];
7982 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7984 if(e->IsQuadratic()) {
7985 const SMDS_VtkFace* F =
7986 dynamic_cast<const SMDS_VtkFace*>(e);
7987 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7988 // use special nodes iterator
7989 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7990 while( anIter->more() ) {
7991 nodes[ iNode++ ] = cast2Node(anIter->next());
7995 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7996 while ( nIt->more() )
7997 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7999 nodes[ iNode ] = nodes[ 0 ];
8001 for ( iNode = 0; iNode < nbNodes; iNode++ )
8002 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8003 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8004 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8006 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8007 curElemList.push_back( e );
8011 // analyse the found
8013 int nbNewBorders = curElemList.size();
8014 if ( nbNewBorders == 0 ) {
8015 // no free border furthermore
8016 return !needTheLast;
8018 else if ( nbNewBorders == 1 ) {
8019 // one more element found
8021 nStart = nStartList.front();
8022 curElem = curElemList.front();
8023 theFaces.push_back( curElem );
8024 theNodes.push_back( nStart );
8027 // several continuations found
8028 list< const SMDS_MeshElement* >::iterator curElemIt;
8029 list< const SMDS_MeshNode* >::iterator nStartIt;
8030 // check if one of them reached the last node
8031 if ( needTheLast ) {
8032 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8033 curElemIt!= curElemList.end();
8034 curElemIt++, nStartIt++ )
8035 if ( *nStartIt == theLastNode ) {
8036 theFaces.push_back( *curElemIt );
8037 theNodes.push_back( *nStartIt );
8041 // find the best free border by the continuations
8042 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8043 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8044 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8045 curElemIt!= curElemList.end();
8046 curElemIt++, nStartIt++ )
8048 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8049 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8050 // find one more free border
8051 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8055 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8056 // choice: clear a worse one
8057 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8058 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8059 contNodes[ iWorse ].clear();
8060 contFaces[ iWorse ].clear();
8063 if ( contNodes[0].empty() && contNodes[1].empty() )
8066 // append the best free border
8067 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8068 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8069 theNodes.pop_back(); // remove nIgnore
8070 theNodes.pop_back(); // remove nStart
8071 theFaces.pop_back(); // remove curElem
8072 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8073 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8074 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8075 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8078 } // several continuations found
8079 } // while ( nStart != theLastNode )
8084 //=======================================================================
8085 //function : CheckFreeBorderNodes
8086 //purpose : Return true if the tree nodes are on a free border
8087 //=======================================================================
8089 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8090 const SMDS_MeshNode* theNode2,
8091 const SMDS_MeshNode* theNode3)
8093 list< const SMDS_MeshNode* > nodes;
8094 list< const SMDS_MeshElement* > faces;
8095 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8098 //=======================================================================
8099 //function : SewFreeBorder
8101 //=======================================================================
8103 SMESH_MeshEditor::Sew_Error
8104 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8105 const SMDS_MeshNode* theBordSecondNode,
8106 const SMDS_MeshNode* theBordLastNode,
8107 const SMDS_MeshNode* theSideFirstNode,
8108 const SMDS_MeshNode* theSideSecondNode,
8109 const SMDS_MeshNode* theSideThirdNode,
8110 const bool theSideIsFreeBorder,
8111 const bool toCreatePolygons,
8112 const bool toCreatePolyedrs)
8114 myLastCreatedElems.Clear();
8115 myLastCreatedNodes.Clear();
8117 MESSAGE("::SewFreeBorder()");
8118 Sew_Error aResult = SEW_OK;
8120 // ====================================
8121 // find side nodes and elements
8122 // ====================================
8124 list< const SMDS_MeshNode* > nSide[ 2 ];
8125 list< const SMDS_MeshElement* > eSide[ 2 ];
8126 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8127 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8131 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8132 nSide[0], eSide[0])) {
8133 MESSAGE(" Free Border 1 not found " );
8134 aResult = SEW_BORDER1_NOT_FOUND;
8136 if (theSideIsFreeBorder) {
8139 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8140 nSide[1], eSide[1])) {
8141 MESSAGE(" Free Border 2 not found " );
8142 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8145 if ( aResult != SEW_OK )
8148 if (!theSideIsFreeBorder) {
8152 // -------------------------------------------------------------------------
8154 // 1. If nodes to merge are not coincident, move nodes of the free border
8155 // from the coord sys defined by the direction from the first to last
8156 // nodes of the border to the correspondent sys of the side 2
8157 // 2. On the side 2, find the links most co-directed with the correspondent
8158 // links of the free border
8159 // -------------------------------------------------------------------------
8161 // 1. Since sewing may break if there are volumes to split on the side 2,
8162 // we wont move nodes but just compute new coordinates for them
8163 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8164 TNodeXYZMap nBordXYZ;
8165 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8166 list< const SMDS_MeshNode* >::iterator nBordIt;
8168 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8169 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8170 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8171 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8172 double tol2 = 1.e-8;
8173 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8174 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8175 // Need node movement.
8177 // find X and Z axes to create trsf
8178 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8180 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8182 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8185 gp_Ax3 toBordAx( Pb1, Zb, X );
8186 gp_Ax3 fromSideAx( Ps1, Zs, X );
8187 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8189 gp_Trsf toBordSys, fromSide2Sys;
8190 toBordSys.SetTransformation( toBordAx );
8191 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8192 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8195 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8196 const SMDS_MeshNode* n = *nBordIt;
8197 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8198 toBordSys.Transforms( xyz );
8199 fromSide2Sys.Transforms( xyz );
8200 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8204 // just insert nodes XYZ in the nBordXYZ map
8205 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8206 const SMDS_MeshNode* n = *nBordIt;
8207 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8211 // 2. On the side 2, find the links most co-directed with the correspondent
8212 // links of the free border
8214 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8215 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8216 sideNodes.push_back( theSideFirstNode );
8218 bool hasVolumes = false;
8219 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8220 set<long> foundSideLinkIDs, checkedLinkIDs;
8221 SMDS_VolumeTool volume;
8222 //const SMDS_MeshNode* faceNodes[ 4 ];
8224 const SMDS_MeshNode* sideNode;
8225 const SMDS_MeshElement* sideElem;
8226 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8227 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8228 nBordIt = bordNodes.begin();
8230 // border node position and border link direction to compare with
8231 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8232 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8233 // choose next side node by link direction or by closeness to
8234 // the current border node:
8235 bool searchByDir = ( *nBordIt != theBordLastNode );
8237 // find the next node on the Side 2
8239 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8241 checkedLinkIDs.clear();
8242 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8244 // loop on inverse elements of current node (prevSideNode) on the Side 2
8245 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8246 while ( invElemIt->more() )
8248 const SMDS_MeshElement* elem = invElemIt->next();
8249 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8250 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8251 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8252 bool isVolume = volume.Set( elem );
8253 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8254 if ( isVolume ) // --volume
8256 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8257 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8258 if(elem->IsQuadratic()) {
8259 const SMDS_VtkFace* F =
8260 dynamic_cast<const SMDS_VtkFace*>(elem);
8261 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8262 // use special nodes iterator
8263 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8264 while( anIter->more() ) {
8265 nodes[ iNode ] = cast2Node(anIter->next());
8266 if ( nodes[ iNode++ ] == prevSideNode )
8267 iPrevNode = iNode - 1;
8271 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8272 while ( nIt->more() ) {
8273 nodes[ iNode ] = cast2Node( nIt->next() );
8274 if ( nodes[ iNode++ ] == prevSideNode )
8275 iPrevNode = iNode - 1;
8278 // there are 2 links to check
8283 // loop on links, to be precise, on the second node of links
8284 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8285 const SMDS_MeshNode* n = nodes[ iNode ];
8287 if ( !volume.IsLinked( n, prevSideNode ))
8291 if ( iNode ) // a node before prevSideNode
8292 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8293 else // a node after prevSideNode
8294 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8296 // check if this link was already used
8297 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8298 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8299 if (!isJustChecked &&
8300 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8302 // test a link geometrically
8303 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8304 bool linkIsBetter = false;
8305 double dot = 0.0, dist = 0.0;
8306 if ( searchByDir ) { // choose most co-directed link
8307 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8308 linkIsBetter = ( dot > maxDot );
8310 else { // choose link with the node closest to bordPos
8311 dist = ( nextXYZ - bordPos ).SquareModulus();
8312 linkIsBetter = ( dist < minDist );
8314 if ( linkIsBetter ) {
8323 } // loop on inverse elements of prevSideNode
8326 MESSAGE(" Cant find path by links of the Side 2 ");
8327 return SEW_BAD_SIDE_NODES;
8329 sideNodes.push_back( sideNode );
8330 sideElems.push_back( sideElem );
8331 foundSideLinkIDs.insert ( linkID );
8332 prevSideNode = sideNode;
8334 if ( *nBordIt == theBordLastNode )
8335 searchByDir = false;
8337 // find the next border link to compare with
8338 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8339 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8340 // move to next border node if sideNode is before forward border node (bordPos)
8341 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8342 prevBordNode = *nBordIt;
8344 bordPos = nBordXYZ[ *nBordIt ];
8345 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8346 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8350 while ( sideNode != theSideSecondNode );
8352 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8353 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8354 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8356 } // end nodes search on the side 2
8358 // ============================
8359 // sew the border to the side 2
8360 // ============================
8362 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8363 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8365 TListOfListOfNodes nodeGroupsToMerge;
8366 if ( nbNodes[0] == nbNodes[1] ||
8367 ( theSideIsFreeBorder && !theSideThirdNode)) {
8369 // all nodes are to be merged
8371 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8372 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8373 nIt[0]++, nIt[1]++ )
8375 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8376 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8377 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8382 // insert new nodes into the border and the side to get equal nb of segments
8384 // get normalized parameters of nodes on the borders
8385 //double param[ 2 ][ maxNbNodes ];
8387 param[0] = new double [ maxNbNodes ];
8388 param[1] = new double [ maxNbNodes ];
8390 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8391 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8392 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8393 const SMDS_MeshNode* nPrev = *nIt;
8394 double bordLength = 0;
8395 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8396 const SMDS_MeshNode* nCur = *nIt;
8397 gp_XYZ segment (nCur->X() - nPrev->X(),
8398 nCur->Y() - nPrev->Y(),
8399 nCur->Z() - nPrev->Z());
8400 double segmentLen = segment.Modulus();
8401 bordLength += segmentLen;
8402 param[ iBord ][ iNode ] = bordLength;
8405 // normalize within [0,1]
8406 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8407 param[ iBord ][ iNode ] /= bordLength;
8411 // loop on border segments
8412 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8413 int i[ 2 ] = { 0, 0 };
8414 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8415 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8417 TElemOfNodeListMap insertMap;
8418 TElemOfNodeListMap::iterator insertMapIt;
8420 // key: elem to insert nodes into
8421 // value: 2 nodes to insert between + nodes to be inserted
8423 bool next[ 2 ] = { false, false };
8425 // find min adjacent segment length after sewing
8426 double nextParam = 10., prevParam = 0;
8427 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8428 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8429 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8430 if ( i[ iBord ] > 0 )
8431 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8433 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8434 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8435 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8437 // choose to insert or to merge nodes
8438 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8439 if ( Abs( du ) <= minSegLen * 0.2 ) {
8442 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8443 const SMDS_MeshNode* n0 = *nIt[0];
8444 const SMDS_MeshNode* n1 = *nIt[1];
8445 nodeGroupsToMerge.back().push_back( n1 );
8446 nodeGroupsToMerge.back().push_back( n0 );
8447 // position of node of the border changes due to merge
8448 param[ 0 ][ i[0] ] += du;
8449 // move n1 for the sake of elem shape evaluation during insertion.
8450 // n1 will be removed by MergeNodes() anyway
8451 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8452 next[0] = next[1] = true;
8457 int intoBord = ( du < 0 ) ? 0 : 1;
8458 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8459 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8460 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8461 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8462 if ( intoBord == 1 ) {
8463 // move node of the border to be on a link of elem of the side
8464 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8465 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8466 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8467 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8468 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8470 insertMapIt = insertMap.find( elem );
8471 bool notFound = ( insertMapIt == insertMap.end() );
8472 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8474 // insert into another link of the same element:
8475 // 1. perform insertion into the other link of the elem
8476 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8477 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8478 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8479 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8480 // 2. perform insertion into the link of adjacent faces
8482 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8484 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8488 if (toCreatePolyedrs) {
8489 // perform insertion into the links of adjacent volumes
8490 UpdateVolumes(n12, n22, nodeList);
8492 // 3. find an element appeared on n1 and n2 after the insertion
8493 insertMap.erase( elem );
8494 elem = findAdjacentFace( n1, n2, 0 );
8496 if ( notFound || otherLink ) {
8497 // add element and nodes of the side into the insertMap
8498 insertMapIt = insertMap.insert
8499 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8500 (*insertMapIt).second.push_back( n1 );
8501 (*insertMapIt).second.push_back( n2 );
8503 // add node to be inserted into elem
8504 (*insertMapIt).second.push_back( nIns );
8505 next[ 1 - intoBord ] = true;
8508 // go to the next segment
8509 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8510 if ( next[ iBord ] ) {
8511 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8513 nPrev[ iBord ] = *nIt[ iBord ];
8514 nIt[ iBord ]++; i[ iBord ]++;
8518 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8520 // perform insertion of nodes into elements
8522 for (insertMapIt = insertMap.begin();
8523 insertMapIt != insertMap.end();
8526 const SMDS_MeshElement* elem = (*insertMapIt).first;
8527 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8528 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8529 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8531 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8533 if ( !theSideIsFreeBorder ) {
8534 // look for and insert nodes into the faces adjacent to elem
8536 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8538 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8543 if (toCreatePolyedrs) {
8544 // perform insertion into the links of adjacent volumes
8545 UpdateVolumes(n1, n2, nodeList);
8551 } // end: insert new nodes
8553 MergeNodes ( nodeGroupsToMerge );
8558 //=======================================================================
8559 //function : InsertNodesIntoLink
8560 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8561 // and theBetweenNode2 and split theElement
8562 //=======================================================================
8564 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8565 const SMDS_MeshNode* theBetweenNode1,
8566 const SMDS_MeshNode* theBetweenNode2,
8567 list<const SMDS_MeshNode*>& theNodesToInsert,
8568 const bool toCreatePoly)
8570 if ( theFace->GetType() != SMDSAbs_Face ) return;
8572 // find indices of 2 link nodes and of the rest nodes
8573 int iNode = 0, il1, il2, i3, i4;
8574 il1 = il2 = i3 = i4 = -1;
8575 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8576 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8578 if(theFace->IsQuadratic()) {
8579 const SMDS_VtkFace* F =
8580 dynamic_cast<const SMDS_VtkFace*>(theFace);
8581 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8582 // use special nodes iterator
8583 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8584 while( anIter->more() ) {
8585 const SMDS_MeshNode* n = cast2Node(anIter->next());
8586 if ( n == theBetweenNode1 )
8588 else if ( n == theBetweenNode2 )
8594 nodes[ iNode++ ] = n;
8598 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8599 while ( nodeIt->more() ) {
8600 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8601 if ( n == theBetweenNode1 )
8603 else if ( n == theBetweenNode2 )
8609 nodes[ iNode++ ] = n;
8612 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8615 // arrange link nodes to go one after another regarding the face orientation
8616 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8617 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8622 aNodesToInsert.reverse();
8624 // check that not link nodes of a quadrangles are in good order
8625 int nbFaceNodes = theFace->NbNodes();
8626 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8632 if (toCreatePoly || theFace->IsPoly()) {
8635 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8637 // add nodes of face up to first node of link
8640 if(theFace->IsQuadratic()) {
8641 const SMDS_VtkFace* F =
8642 dynamic_cast<const SMDS_VtkFace*>(theFace);
8643 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8644 // use special nodes iterator
8645 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8646 while( anIter->more() && !isFLN ) {
8647 const SMDS_MeshNode* n = cast2Node(anIter->next());
8648 poly_nodes[iNode++] = n;
8649 if (n == nodes[il1]) {
8653 // add nodes to insert
8654 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8655 for (; nIt != aNodesToInsert.end(); nIt++) {
8656 poly_nodes[iNode++] = *nIt;
8658 // add nodes of face starting from last node of link
8659 while ( anIter->more() ) {
8660 poly_nodes[iNode++] = cast2Node(anIter->next());
8664 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8665 while ( nodeIt->more() && !isFLN ) {
8666 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8667 poly_nodes[iNode++] = n;
8668 if (n == nodes[il1]) {
8672 // add nodes to insert
8673 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8674 for (; nIt != aNodesToInsert.end(); nIt++) {
8675 poly_nodes[iNode++] = *nIt;
8677 // add nodes of face starting from last node of link
8678 while ( nodeIt->more() ) {
8679 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8680 poly_nodes[iNode++] = n;
8684 // edit or replace the face
8685 SMESHDS_Mesh *aMesh = GetMeshDS();
8687 if (theFace->IsPoly()) {
8688 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8691 int aShapeId = FindShape( theFace );
8693 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8694 myLastCreatedElems.Append(newElem);
8695 if ( aShapeId && newElem )
8696 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8698 aMesh->RemoveElement(theFace);
8703 SMESHDS_Mesh *aMesh = GetMeshDS();
8704 if( !theFace->IsQuadratic() ) {
8706 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8707 int nbLinkNodes = 2 + aNodesToInsert.size();
8708 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8709 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8710 linkNodes[ 0 ] = nodes[ il1 ];
8711 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8712 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8713 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8714 linkNodes[ iNode++ ] = *nIt;
8716 // decide how to split a quadrangle: compare possible variants
8717 // and choose which of splits to be a quadrangle
8718 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8719 if ( nbFaceNodes == 3 ) {
8720 iBestQuad = nbSplits;
8723 else if ( nbFaceNodes == 4 ) {
8724 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8725 double aBestRate = DBL_MAX;
8726 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8728 double aBadRate = 0;
8729 // evaluate elements quality
8730 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8731 if ( iSplit == iQuad ) {
8732 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8736 aBadRate += getBadRate( &quad, aCrit );
8739 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8741 nodes[ iSplit < iQuad ? i4 : i3 ]);
8742 aBadRate += getBadRate( &tria, aCrit );
8746 if ( aBadRate < aBestRate ) {
8748 aBestRate = aBadRate;
8753 // create new elements
8754 int aShapeId = FindShape( theFace );
8757 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8758 SMDS_MeshElement* newElem = 0;
8759 if ( iSplit == iBestQuad )
8760 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8765 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8767 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8768 myLastCreatedElems.Append(newElem);
8769 if ( aShapeId && newElem )
8770 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8773 // change nodes of theFace
8774 const SMDS_MeshNode* newNodes[ 4 ];
8775 newNodes[ 0 ] = linkNodes[ i1 ];
8776 newNodes[ 1 ] = linkNodes[ i2 ];
8777 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8778 newNodes[ 3 ] = nodes[ i4 ];
8779 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8780 const SMDS_MeshElement* newElem = 0;
8781 if (iSplit == iBestQuad)
8782 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8784 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8785 myLastCreatedElems.Append(newElem);
8786 if ( aShapeId && newElem )
8787 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8788 } // end if(!theFace->IsQuadratic())
8789 else { // theFace is quadratic
8790 // we have to split theFace on simple triangles and one simple quadrangle
8792 int nbshift = tmp*2;
8793 // shift nodes in nodes[] by nbshift
8795 for(i=0; i<nbshift; i++) {
8796 const SMDS_MeshNode* n = nodes[0];
8797 for(j=0; j<nbFaceNodes-1; j++) {
8798 nodes[j] = nodes[j+1];
8800 nodes[nbFaceNodes-1] = n;
8802 il1 = il1 - nbshift;
8803 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8804 // n0 n1 n2 n0 n1 n2
8805 // +-----+-----+ +-----+-----+
8814 // create new elements
8815 int aShapeId = FindShape( theFace );
8818 if(nbFaceNodes==6) { // quadratic triangle
8819 SMDS_MeshElement* newElem =
8820 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8821 myLastCreatedElems.Append(newElem);
8822 if ( aShapeId && newElem )
8823 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8824 if(theFace->IsMediumNode(nodes[il1])) {
8825 // create quadrangle
8826 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8827 myLastCreatedElems.Append(newElem);
8828 if ( aShapeId && newElem )
8829 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8835 // create quadrangle
8836 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8837 myLastCreatedElems.Append(newElem);
8838 if ( aShapeId && newElem )
8839 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8845 else { // nbFaceNodes==8 - quadratic quadrangle
8846 SMDS_MeshElement* newElem =
8847 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8848 myLastCreatedElems.Append(newElem);
8849 if ( aShapeId && newElem )
8850 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8851 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8852 myLastCreatedElems.Append(newElem);
8853 if ( aShapeId && newElem )
8854 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8855 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8856 myLastCreatedElems.Append(newElem);
8857 if ( aShapeId && newElem )
8858 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8859 if(theFace->IsMediumNode(nodes[il1])) {
8860 // create quadrangle
8861 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8862 myLastCreatedElems.Append(newElem);
8863 if ( aShapeId && newElem )
8864 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8870 // create quadrangle
8871 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8872 myLastCreatedElems.Append(newElem);
8873 if ( aShapeId && newElem )
8874 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8880 // create needed triangles using n1,n2,n3 and inserted nodes
8881 int nbn = 2 + aNodesToInsert.size();
8882 //const SMDS_MeshNode* aNodes[nbn];
8883 vector<const SMDS_MeshNode*> aNodes(nbn);
8884 aNodes[0] = nodes[n1];
8885 aNodes[nbn-1] = nodes[n2];
8886 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8887 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8888 aNodes[iNode++] = *nIt;
8890 for(i=1; i<nbn; i++) {
8891 SMDS_MeshElement* newElem =
8892 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8893 myLastCreatedElems.Append(newElem);
8894 if ( aShapeId && newElem )
8895 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8899 aMesh->RemoveElement(theFace);
8902 //=======================================================================
8903 //function : UpdateVolumes
8905 //=======================================================================
8906 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8907 const SMDS_MeshNode* theBetweenNode2,
8908 list<const SMDS_MeshNode*>& theNodesToInsert)
8910 myLastCreatedElems.Clear();
8911 myLastCreatedNodes.Clear();
8913 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8914 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8915 const SMDS_MeshElement* elem = invElemIt->next();
8917 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8918 SMDS_VolumeTool aVolume (elem);
8919 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8922 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8923 int iface, nbFaces = aVolume.NbFaces();
8924 vector<const SMDS_MeshNode *> poly_nodes;
8925 vector<int> quantities (nbFaces);
8927 for (iface = 0; iface < nbFaces; iface++) {
8928 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8929 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8930 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8932 for (int inode = 0; inode < nbFaceNodes; inode++) {
8933 poly_nodes.push_back(faceNodes[inode]);
8935 if (nbInserted == 0) {
8936 if (faceNodes[inode] == theBetweenNode1) {
8937 if (faceNodes[inode + 1] == theBetweenNode2) {
8938 nbInserted = theNodesToInsert.size();
8940 // add nodes to insert
8941 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8942 for (; nIt != theNodesToInsert.end(); nIt++) {
8943 poly_nodes.push_back(*nIt);
8947 else if (faceNodes[inode] == theBetweenNode2) {
8948 if (faceNodes[inode + 1] == theBetweenNode1) {
8949 nbInserted = theNodesToInsert.size();
8951 // add nodes to insert in reversed order
8952 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8954 for (; nIt != theNodesToInsert.begin(); nIt--) {
8955 poly_nodes.push_back(*nIt);
8957 poly_nodes.push_back(*nIt);
8964 quantities[iface] = nbFaceNodes + nbInserted;
8967 // Replace or update the volume
8968 SMESHDS_Mesh *aMesh = GetMeshDS();
8970 if (elem->IsPoly()) {
8971 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8975 int aShapeId = FindShape( elem );
8977 SMDS_MeshElement* newElem =
8978 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8979 myLastCreatedElems.Append(newElem);
8980 if (aShapeId && newElem)
8981 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8983 aMesh->RemoveElement(elem);
8990 //================================================================================
8992 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8994 //================================================================================
8996 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8997 vector<const SMDS_MeshNode *> & nodes,
8998 vector<int> & nbNodeInFaces )
9001 nbNodeInFaces.clear();
9002 SMDS_VolumeTool vTool ( elem );
9003 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9005 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9006 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9007 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9012 //=======================================================================
9014 * \brief Convert elements contained in a submesh to quadratic
9015 * \return int - nb of checked elements
9017 //=======================================================================
9019 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9020 SMESH_MesherHelper& theHelper,
9021 const bool theForce3d)
9024 if( !theSm ) return nbElem;
9026 vector<int> nbNodeInFaces;
9027 vector<const SMDS_MeshNode *> nodes;
9028 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9029 while(ElemItr->more())
9032 const SMDS_MeshElement* elem = ElemItr->next();
9033 if( !elem || elem->IsQuadratic() ) continue;
9035 // get elem data needed to re-create it
9037 int id = elem->GetID();
9038 int nbNodes = elem->NbNodes();
9039 SMDSAbs_ElementType aType = elem->GetType();
9040 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9041 if ( elem->GetEntityType() == SMDSEntity_Polyhedra )
9042 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9043 else if ( elem->GetEntityType() == SMDSEntity_Hexagonal_Prism )
9044 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9046 // remove a linear element
9047 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9049 const SMDS_MeshElement* NewElem = 0;
9055 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9063 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9066 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9069 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9074 case SMDSAbs_Volume :
9076 switch( elem->GetEntityType() )
9078 case SMDSEntity_Tetra:
9079 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9081 case SMDSEntity_Pyramid:
9082 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9084 case SMDSEntity_Penta:
9085 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9087 case SMDSEntity_Hexa:
9088 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9089 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9091 case SMDSEntity_Hexagonal_Prism:
9093 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9100 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9102 theSm->AddElement( NewElem );
9104 // if (!GetMeshDS()->isCompacted())
9105 // GetMeshDS()->compactMesh();
9109 //=======================================================================
9110 //function : ConvertToQuadratic
9112 //=======================================================================
9114 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9116 SMESHDS_Mesh* meshDS = GetMeshDS();
9118 SMESH_MesherHelper aHelper(*myMesh);
9119 aHelper.SetIsQuadratic( true );
9121 int nbCheckedElems = 0;
9122 if ( myMesh->HasShapeToMesh() )
9124 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9126 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9127 while ( smIt->more() ) {
9128 SMESH_subMesh* sm = smIt->next();
9129 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9130 aHelper.SetSubShape( sm->GetSubShape() );
9131 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9136 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9137 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9139 SMESHDS_SubMesh *smDS = 0;
9140 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9141 while(aEdgeItr->more())
9143 const SMDS_MeshEdge* edge = aEdgeItr->next();
9144 if(edge && !edge->IsQuadratic())
9146 int id = edge->GetID();
9147 //MESSAGE("edge->GetID() " << id);
9148 const SMDS_MeshNode* n1 = edge->GetNode(0);
9149 const SMDS_MeshNode* n2 = edge->GetNode(1);
9151 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9153 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9154 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9157 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9158 while(aFaceItr->more())
9160 const SMDS_MeshFace* face = aFaceItr->next();
9161 if(!face || face->IsQuadratic() ) continue;
9163 const int id = face->GetID();
9164 const SMDSAbs_EntityType type = face->GetEntityType();
9165 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9167 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9169 SMDS_MeshFace * NewFace = 0;
9172 case SMDSEntity_Triangle:
9173 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9175 case SMDSEntity_Quadrangle:
9176 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9179 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9181 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9183 vector<int> nbNodeInFaces;
9184 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9185 while(aVolumeItr->more())
9187 const SMDS_MeshVolume* volume = aVolumeItr->next();
9188 if(!volume || volume->IsQuadratic() ) continue;
9190 const int id = volume->GetID();
9191 const SMDSAbs_EntityType type = volume->GetEntityType();
9192 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9193 if ( type == SMDSEntity_Polyhedra )
9194 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9195 else if ( type == SMDSEntity_Hexagonal_Prism )
9196 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9198 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9200 SMDS_MeshVolume * NewVolume = 0;
9203 case SMDSEntity_Tetra:
9204 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9205 nodes[3], id, theForce3d );
9207 case SMDSEntity_Hexa:
9208 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9209 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9211 case SMDSEntity_Pyramid:
9212 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9213 nodes[3], nodes[4], id, theForce3d);
9215 case SMDSEntity_Penta:
9216 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9217 nodes[3], nodes[4], nodes[5], id, theForce3d);
9219 case SMDSEntity_Hexagonal_Prism:
9221 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9223 ReplaceElemInGroups(volume, NewVolume, meshDS);
9228 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9229 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9230 aHelper.FixQuadraticElements();
9234 //================================================================================
9236 * \brief Makes given elements quadratic
9237 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9238 * \param theElements - elements to make quadratic
9240 //================================================================================
9242 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9243 TIDSortedElemSet& theElements)
9245 if ( theElements.empty() ) return;
9247 // we believe that all theElements are of the same type
9248 SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9250 // get all nodes shared by theElements
9251 TIDSortedNodeSet allNodes;
9252 TIDSortedElemSet::iterator eIt = theElements.begin();
9253 for ( ; eIt != theElements.end(); ++eIt )
9254 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9256 // complete theElements with elements of lower dim whose all nodes are in allNodes
9258 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9259 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9260 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9261 for ( ; nIt != allNodes.end(); ++nIt )
9263 const SMDS_MeshNode* n = *nIt;
9264 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9265 while ( invIt->more() )
9267 const SMDS_MeshElement* e = invIt->next();
9268 if ( e->IsQuadratic() )
9270 quadAdjacentElems[ e->GetType() ].insert( e );
9273 if ( e->GetType() >= elemType )
9275 continue; // same type of more complex linear element
9278 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9279 continue; // e is already checked
9283 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9284 while ( nodeIt->more() && allIn )
9285 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9287 theElements.insert(e );
9291 SMESH_MesherHelper helper(*myMesh);
9292 helper.SetIsQuadratic( true );
9294 // add links of quadratic adjacent elements to the helper
9296 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9297 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9298 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9300 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9302 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9303 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9304 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9306 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9308 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9309 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9310 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9312 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9315 // make quadratic elements instead of linear ones
9317 SMESHDS_Mesh* meshDS = GetMeshDS();
9318 SMESHDS_SubMesh* smDS = 0;
9319 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9321 const SMDS_MeshElement* elem = *eIt;
9322 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9325 int id = elem->GetID();
9326 SMDSAbs_ElementType type = elem->GetType();
9327 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9329 if ( !smDS || !smDS->Contains( elem ))
9330 smDS = meshDS->MeshElements( elem->getshapeId() );
9331 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9333 SMDS_MeshElement * newElem = 0;
9334 switch( nodes.size() )
9336 case 4: // cases for most multiple element types go first (for optimization)
9337 if ( type == SMDSAbs_Volume )
9338 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9340 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9343 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9344 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9347 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9350 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9353 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9354 nodes[4], id, theForce3d);
9357 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9358 nodes[4], nodes[5], id, theForce3d);
9362 ReplaceElemInGroups( elem, newElem, meshDS);
9363 if( newElem && smDS )
9364 smDS->AddElement( newElem );
9367 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9368 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9369 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9370 helper.FixQuadraticElements();
9374 //=======================================================================
9376 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9377 * \return int - nb of checked elements
9379 //=======================================================================
9381 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9382 SMDS_ElemIteratorPtr theItr,
9383 const int theShapeID)
9386 SMESHDS_Mesh* meshDS = GetMeshDS();
9388 while( theItr->more() )
9390 const SMDS_MeshElement* elem = theItr->next();
9392 if( elem && elem->IsQuadratic())
9394 int id = elem->GetID();
9395 int nbCornerNodes = elem->NbCornerNodes();
9396 SMDSAbs_ElementType aType = elem->GetType();
9398 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9400 //remove a quadratic element
9401 if ( !theSm || !theSm->Contains( elem ))
9402 theSm = meshDS->MeshElements( elem->getshapeId() );
9403 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9405 // remove medium nodes
9406 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9407 if ( nodes[i]->NbInverseElements() == 0 )
9408 meshDS->RemoveFreeNode( nodes[i], theSm );
9410 // add a linear element
9411 nodes.resize( nbCornerNodes );
9412 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9413 ReplaceElemInGroups(elem, newElem, meshDS);
9414 if( theSm && newElem )
9415 theSm->AddElement( newElem );
9421 //=======================================================================
9422 //function : ConvertFromQuadratic
9424 //=======================================================================
9426 bool SMESH_MeshEditor::ConvertFromQuadratic()
9428 int nbCheckedElems = 0;
9429 if ( myMesh->HasShapeToMesh() )
9431 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9433 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9434 while ( smIt->more() ) {
9435 SMESH_subMesh* sm = smIt->next();
9436 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9437 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9443 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9444 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9446 SMESHDS_SubMesh *aSM = 0;
9447 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9455 //================================================================================
9457 * \brief Return true if all medium nodes of the element are in the node set
9459 //================================================================================
9461 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9463 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9464 if ( !nodeSet.count( elem->GetNode(i) ))
9470 //================================================================================
9472 * \brief Makes given elements linear
9474 //================================================================================
9476 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9478 if ( theElements.empty() ) return;
9480 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9481 set<int> mediumNodeIDs;
9482 TIDSortedElemSet::iterator eIt = theElements.begin();
9483 for ( ; eIt != theElements.end(); ++eIt )
9485 const SMDS_MeshElement* e = *eIt;
9486 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9487 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9490 // replace given elements by linear ones
9491 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9492 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9493 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9495 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9496 // except those elements sharing medium nodes of quadratic element whose medium nodes
9497 // are not all in mediumNodeIDs
9499 // get remaining medium nodes
9500 TIDSortedNodeSet mediumNodes;
9501 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9502 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9503 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9504 mediumNodes.insert( mediumNodes.end(), n );
9506 // find more quadratic elements to convert
9507 TIDSortedElemSet moreElemsToConvert;
9508 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9509 for ( ; nIt != mediumNodes.end(); ++nIt )
9511 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9512 while ( invIt->more() )
9514 const SMDS_MeshElement* e = invIt->next();
9515 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9517 // find a more complex element including e and
9518 // whose medium nodes are not in mediumNodes
9519 bool complexFound = false;
9520 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9522 SMDS_ElemIteratorPtr invIt2 =
9523 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9524 while ( invIt2->more() )
9526 const SMDS_MeshElement* eComplex = invIt2->next();
9527 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9529 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9530 if ( nbCommonNodes == e->NbNodes())
9532 complexFound = true;
9533 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9539 if ( !complexFound )
9540 moreElemsToConvert.insert( e );
9544 elemIt = SMDS_ElemIteratorPtr
9545 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9546 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9549 //=======================================================================
9550 //function : SewSideElements
9552 //=======================================================================
9554 SMESH_MeshEditor::Sew_Error
9555 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9556 TIDSortedElemSet& theSide2,
9557 const SMDS_MeshNode* theFirstNode1,
9558 const SMDS_MeshNode* theFirstNode2,
9559 const SMDS_MeshNode* theSecondNode1,
9560 const SMDS_MeshNode* theSecondNode2)
9562 myLastCreatedElems.Clear();
9563 myLastCreatedNodes.Clear();
9565 MESSAGE ("::::SewSideElements()");
9566 if ( theSide1.size() != theSide2.size() )
9567 return SEW_DIFF_NB_OF_ELEMENTS;
9569 Sew_Error aResult = SEW_OK;
9571 // 1. Build set of faces representing each side
9572 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9573 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9575 // =======================================================================
9576 // 1. Build set of faces representing each side:
9577 // =======================================================================
9578 // a. build set of nodes belonging to faces
9579 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9580 // c. create temporary faces representing side of volumes if correspondent
9581 // face does not exist
9583 SMESHDS_Mesh* aMesh = GetMeshDS();
9584 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9585 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9586 set<const SMDS_MeshElement*> faceSet1, faceSet2;
9587 set<const SMDS_MeshElement*> volSet1, volSet2;
9588 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9589 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9590 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9591 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9592 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9593 int iSide, iFace, iNode;
9595 list<const SMDS_MeshElement* > tempFaceList;
9596 for ( iSide = 0; iSide < 2; iSide++ ) {
9597 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9598 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9599 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9600 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9601 set<const SMDS_MeshElement*>::iterator vIt;
9602 TIDSortedElemSet::iterator eIt;
9603 set<const SMDS_MeshNode*>::iterator nIt;
9605 // check that given nodes belong to given elements
9606 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9607 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9608 int firstIndex = -1, secondIndex = -1;
9609 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9610 const SMDS_MeshElement* elem = *eIt;
9611 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9612 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9613 if ( firstIndex > -1 && secondIndex > -1 ) break;
9615 if ( firstIndex < 0 || secondIndex < 0 ) {
9616 // we can simply return until temporary faces created
9617 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9620 // -----------------------------------------------------------
9621 // 1a. Collect nodes of existing faces
9622 // and build set of face nodes in order to detect missing
9623 // faces corresponding to sides of volumes
9624 // -----------------------------------------------------------
9626 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9628 // loop on the given element of a side
9629 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9630 //const SMDS_MeshElement* elem = *eIt;
9631 const SMDS_MeshElement* elem = *eIt;
9632 if ( elem->GetType() == SMDSAbs_Face ) {
9633 faceSet->insert( elem );
9634 set <const SMDS_MeshNode*> faceNodeSet;
9635 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9636 while ( nodeIt->more() ) {
9637 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9638 nodeSet->insert( n );
9639 faceNodeSet.insert( n );
9641 setOfFaceNodeSet.insert( faceNodeSet );
9643 else if ( elem->GetType() == SMDSAbs_Volume )
9644 volSet->insert( elem );
9646 // ------------------------------------------------------------------------------
9647 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9648 // ------------------------------------------------------------------------------
9650 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9651 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9652 while ( fIt->more() ) { // loop on faces sharing a node
9653 const SMDS_MeshElement* f = fIt->next();
9654 if ( faceSet->find( f ) == faceSet->end() ) {
9655 // check if all nodes are in nodeSet and
9656 // complete setOfFaceNodeSet if they are
9657 set <const SMDS_MeshNode*> faceNodeSet;
9658 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9659 bool allInSet = true;
9660 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9661 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9662 if ( nodeSet->find( n ) == nodeSet->end() )
9665 faceNodeSet.insert( n );
9668 faceSet->insert( f );
9669 setOfFaceNodeSet.insert( faceNodeSet );
9675 // -------------------------------------------------------------------------
9676 // 1c. Create temporary faces representing sides of volumes if correspondent
9677 // face does not exist
9678 // -------------------------------------------------------------------------
9680 if ( !volSet->empty() ) {
9681 //int nodeSetSize = nodeSet->size();
9683 // loop on given volumes
9684 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9685 SMDS_VolumeTool vol (*vIt);
9686 // loop on volume faces: find free faces
9687 // --------------------------------------
9688 list<const SMDS_MeshElement* > freeFaceList;
9689 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9690 if ( !vol.IsFreeFace( iFace ))
9692 // check if there is already a face with same nodes in a face set
9693 const SMDS_MeshElement* aFreeFace = 0;
9694 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9695 int nbNodes = vol.NbFaceNodes( iFace );
9696 set <const SMDS_MeshNode*> faceNodeSet;
9697 vol.GetFaceNodes( iFace, faceNodeSet );
9698 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9700 // no such a face is given but it still can exist, check it
9701 if ( nbNodes == 3 ) {
9702 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9704 else if ( nbNodes == 4 ) {
9705 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9708 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9709 aFreeFace = aMesh->FindFace(poly_nodes);
9713 // create a temporary face
9714 if ( nbNodes == 3 ) {
9715 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9716 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9718 else if ( nbNodes == 4 ) {
9719 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9720 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9723 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9724 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9725 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9729 freeFaceList.push_back( aFreeFace );
9730 tempFaceList.push_back( aFreeFace );
9733 } // loop on faces of a volume
9735 // choose one of several free faces
9736 // --------------------------------------
9737 if ( freeFaceList.size() > 1 ) {
9738 // choose a face having max nb of nodes shared by other elems of a side
9739 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9740 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9741 while ( fIt != freeFaceList.end() ) { // loop on free faces
9742 int nbSharedNodes = 0;
9743 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9744 while ( nodeIt->more() ) { // loop on free face nodes
9745 const SMDS_MeshNode* n =
9746 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9747 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9748 while ( invElemIt->more() ) {
9749 const SMDS_MeshElement* e = invElemIt->next();
9750 if ( faceSet->find( e ) != faceSet->end() )
9752 if ( elemSet->find( e ) != elemSet->end() )
9756 if ( nbSharedNodes >= maxNbNodes ) {
9757 maxNbNodes = nbSharedNodes;
9761 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9763 if ( freeFaceList.size() > 1 )
9765 // could not choose one face, use another way
9766 // choose a face most close to the bary center of the opposite side
9767 gp_XYZ aBC( 0., 0., 0. );
9768 set <const SMDS_MeshNode*> addedNodes;
9769 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9770 eIt = elemSet2->begin();
9771 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9772 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9773 while ( nodeIt->more() ) { // loop on free face nodes
9774 const SMDS_MeshNode* n =
9775 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9776 if ( addedNodes.insert( n ).second )
9777 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9780 aBC /= addedNodes.size();
9781 double minDist = DBL_MAX;
9782 fIt = freeFaceList.begin();
9783 while ( fIt != freeFaceList.end() ) { // loop on free faces
9785 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9786 while ( nodeIt->more() ) { // loop on free face nodes
9787 const SMDS_MeshNode* n =
9788 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9789 gp_XYZ p( n->X(),n->Y(),n->Z() );
9790 dist += ( aBC - p ).SquareModulus();
9792 if ( dist < minDist ) {
9794 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9797 fIt = freeFaceList.erase( fIt++ );
9800 } // choose one of several free faces of a volume
9802 if ( freeFaceList.size() == 1 ) {
9803 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9804 faceSet->insert( aFreeFace );
9805 // complete a node set with nodes of a found free face
9806 // for ( iNode = 0; iNode < ; iNode++ )
9807 // nodeSet->insert( fNodes[ iNode ] );
9810 } // loop on volumes of a side
9812 // // complete a set of faces if new nodes in a nodeSet appeared
9813 // // ----------------------------------------------------------
9814 // if ( nodeSetSize != nodeSet->size() ) {
9815 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9816 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9817 // while ( fIt->more() ) { // loop on faces sharing a node
9818 // const SMDS_MeshElement* f = fIt->next();
9819 // if ( faceSet->find( f ) == faceSet->end() ) {
9820 // // check if all nodes are in nodeSet and
9821 // // complete setOfFaceNodeSet if they are
9822 // set <const SMDS_MeshNode*> faceNodeSet;
9823 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9824 // bool allInSet = true;
9825 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9826 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9827 // if ( nodeSet->find( n ) == nodeSet->end() )
9828 // allInSet = false;
9830 // faceNodeSet.insert( n );
9832 // if ( allInSet ) {
9833 // faceSet->insert( f );
9834 // setOfFaceNodeSet.insert( faceNodeSet );
9840 } // Create temporary faces, if there are volumes given
9843 if ( faceSet1.size() != faceSet2.size() ) {
9844 // delete temporary faces: they are in reverseElements of actual nodes
9845 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9846 // while ( tmpFaceIt->more() )
9847 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9848 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9849 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9850 // aMesh->RemoveElement(*tmpFaceIt);
9851 MESSAGE("Diff nb of faces");
9852 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9855 // ============================================================
9856 // 2. Find nodes to merge:
9857 // bind a node to remove to a node to put instead
9858 // ============================================================
9860 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9861 if ( theFirstNode1 != theFirstNode2 )
9862 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
9863 if ( theSecondNode1 != theSecondNode2 )
9864 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
9866 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9867 set< long > linkIdSet; // links to process
9868 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9870 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9871 list< NLink > linkList[2];
9872 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9873 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9874 // loop on links in linkList; find faces by links and append links
9875 // of the found faces to linkList
9876 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9877 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9878 NLink link[] = { *linkIt[0], *linkIt[1] };
9879 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9880 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
9883 // by links, find faces in the face sets,
9884 // and find indices of link nodes in the found faces;
9885 // in a face set, there is only one or no face sharing a link
9886 // ---------------------------------------------------------------
9888 const SMDS_MeshElement* face[] = { 0, 0 };
9889 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
9890 vector<const SMDS_MeshNode*> fnodes1(9);
9891 vector<const SMDS_MeshNode*> fnodes2(9);
9892 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
9893 vector<const SMDS_MeshNode*> notLinkNodes1(6);
9894 vector<const SMDS_MeshNode*> notLinkNodes2(6);
9895 int iLinkNode[2][2];
9896 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9897 const SMDS_MeshNode* n1 = link[iSide].first;
9898 const SMDS_MeshNode* n2 = link[iSide].second;
9899 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9900 set< const SMDS_MeshElement* > fMap;
9901 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
9902 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
9903 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9904 while ( fIt->more() ) { // loop on faces sharing a node
9905 const SMDS_MeshElement* f = fIt->next();
9906 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9907 ! fMap.insert( f ).second ) // f encounters twice
9909 if ( face[ iSide ] ) {
9910 MESSAGE( "2 faces per link " );
9911 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
9915 faceSet->erase( f );
9916 // get face nodes and find ones of a link
9921 fnodes1.resize(f->NbNodes()+1);
9922 notLinkNodes1.resize(f->NbNodes()-2);
9925 fnodes2.resize(f->NbNodes()+1);
9926 notLinkNodes2.resize(f->NbNodes()-2);
9929 if(!f->IsQuadratic()) {
9930 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
9931 while ( nIt->more() ) {
9932 const SMDS_MeshNode* n =
9933 static_cast<const SMDS_MeshNode*>( nIt->next() );
9935 iLinkNode[ iSide ][ 0 ] = iNode;
9937 else if ( n == n2 ) {
9938 iLinkNode[ iSide ][ 1 ] = iNode;
9940 //else if ( notLinkNodes[ iSide ][ 0 ] )
9941 // notLinkNodes[ iSide ][ 1 ] = n;
9943 // notLinkNodes[ iSide ][ 0 ] = n;
9947 notLinkNodes1[nbl] = n;
9948 //notLinkNodes1.push_back(n);
9950 notLinkNodes2[nbl] = n;
9951 //notLinkNodes2.push_back(n);
9953 //faceNodes[ iSide ][ iNode++ ] = n;
9955 fnodes1[iNode++] = n;
9958 fnodes2[iNode++] = n;
9962 else { // f->IsQuadratic()
9963 const SMDS_VtkFace* F =
9964 dynamic_cast<const SMDS_VtkFace*>(f);
9965 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9966 // use special nodes iterator
9967 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9968 while ( anIter->more() ) {
9969 const SMDS_MeshNode* n =
9970 static_cast<const SMDS_MeshNode*>( anIter->next() );
9972 iLinkNode[ iSide ][ 0 ] = iNode;
9974 else if ( n == n2 ) {
9975 iLinkNode[ iSide ][ 1 ] = iNode;
9980 notLinkNodes1[nbl] = n;
9983 notLinkNodes2[nbl] = n;
9987 fnodes1[iNode++] = n;
9990 fnodes2[iNode++] = n;
9994 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
9996 fnodes1[iNode] = fnodes1[0];
9999 fnodes2[iNode] = fnodes1[0];
10006 // check similarity of elements of the sides
10007 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10008 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10009 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10010 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10013 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10015 break; // do not return because it s necessary to remove tmp faces
10018 // set nodes to merge
10019 // -------------------
10021 if ( face[0] && face[1] ) {
10022 int nbNodes = face[0]->NbNodes();
10023 if ( nbNodes != face[1]->NbNodes() ) {
10024 MESSAGE("Diff nb of face nodes");
10025 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10026 break; // do not return because it s necessary to remove tmp faces
10028 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
10029 if ( nbNodes == 3 ) {
10030 //nReplaceMap.insert( TNodeNodeMap::value_type
10031 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10032 nReplaceMap.insert( TNodeNodeMap::value_type
10033 ( notLinkNodes1[0], notLinkNodes2[0] ));
10036 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10037 // analyse link orientation in faces
10038 int i1 = iLinkNode[ iSide ][ 0 ];
10039 int i2 = iLinkNode[ iSide ][ 1 ];
10040 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10041 // if notLinkNodes are the first and the last ones, then
10042 // their order does not correspond to the link orientation
10043 if (( i1 == 1 && i2 == 2 ) ||
10044 ( i1 == 2 && i2 == 1 ))
10045 reverse[ iSide ] = !reverse[ iSide ];
10047 if ( reverse[0] == reverse[1] ) {
10048 //nReplaceMap.insert( TNodeNodeMap::value_type
10049 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10050 //nReplaceMap.insert( TNodeNodeMap::value_type
10051 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
10052 for(int nn=0; nn<nbNodes-2; nn++) {
10053 nReplaceMap.insert( TNodeNodeMap::value_type
10054 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
10058 //nReplaceMap.insert( TNodeNodeMap::value_type
10059 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
10060 //nReplaceMap.insert( TNodeNodeMap::value_type
10061 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
10062 for(int nn=0; nn<nbNodes-2; nn++) {
10063 nReplaceMap.insert( TNodeNodeMap::value_type
10064 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
10069 // add other links of the faces to linkList
10070 // -----------------------------------------
10072 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
10073 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10074 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
10075 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
10076 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10077 if ( !iter_isnew.second ) { // already in a set: no need to process
10078 linkIdSet.erase( iter_isnew.first );
10080 else // new in set == encountered for the first time: add
10082 //const SMDS_MeshNode* n1 = nodes[ iNode ];
10083 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
10084 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
10085 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
10086 linkList[0].push_back ( NLink( n1, n2 ));
10087 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10091 } // loop on link lists
10093 if ( aResult == SEW_OK &&
10094 ( linkIt[0] != linkList[0].end() ||
10095 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10096 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10097 " " << (faceSetPtr[1]->empty()));
10098 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10101 // ====================================================================
10102 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10103 // ====================================================================
10105 // delete temporary faces: they are in reverseElements of actual nodes
10106 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10107 // while ( tmpFaceIt->more() )
10108 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10109 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10110 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10111 // aMesh->RemoveElement(*tmpFaceIt);
10113 if ( aResult != SEW_OK)
10116 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10117 // loop on nodes replacement map
10118 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10119 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10120 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10121 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10122 nodeIDsToRemove.push_back( nToRemove->GetID() );
10123 // loop on elements sharing nToRemove
10124 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10125 while ( invElemIt->more() ) {
10126 const SMDS_MeshElement* e = invElemIt->next();
10127 // get a new suite of nodes: make replacement
10128 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10129 vector< const SMDS_MeshNode*> nodes( nbNodes );
10130 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10131 while ( nIt->more() ) {
10132 const SMDS_MeshNode* n =
10133 static_cast<const SMDS_MeshNode*>( nIt->next() );
10134 nnIt = nReplaceMap.find( n );
10135 if ( nnIt != nReplaceMap.end() ) {
10137 n = (*nnIt).second;
10141 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10142 // elemIDsToRemove.push_back( e->GetID() );
10146 SMDSAbs_ElementType etyp = e->GetType();
10147 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10150 myLastCreatedElems.Append(newElem);
10151 AddToSameGroups(newElem, e, aMesh);
10152 int aShapeId = e->getshapeId();
10155 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10158 aMesh->RemoveElement(e);
10163 Remove( nodeIDsToRemove, true );
10168 //================================================================================
10170 * \brief Find corresponding nodes in two sets of faces
10171 * \param theSide1 - first face set
10172 * \param theSide2 - second first face
10173 * \param theFirstNode1 - a boundary node of set 1
10174 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10175 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10176 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10177 * \param nReplaceMap - output map of corresponding nodes
10178 * \return bool - is a success or not
10180 //================================================================================
10183 //#define DEBUG_MATCHING_NODES
10186 SMESH_MeshEditor::Sew_Error
10187 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10188 set<const SMDS_MeshElement*>& theSide2,
10189 const SMDS_MeshNode* theFirstNode1,
10190 const SMDS_MeshNode* theFirstNode2,
10191 const SMDS_MeshNode* theSecondNode1,
10192 const SMDS_MeshNode* theSecondNode2,
10193 TNodeNodeMap & nReplaceMap)
10195 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10197 nReplaceMap.clear();
10198 if ( theFirstNode1 != theFirstNode2 )
10199 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10200 if ( theSecondNode1 != theSecondNode2 )
10201 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10203 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10204 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10206 list< NLink > linkList[2];
10207 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10208 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10210 // loop on links in linkList; find faces by links and append links
10211 // of the found faces to linkList
10212 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10213 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10214 NLink link[] = { *linkIt[0], *linkIt[1] };
10215 if ( linkSet.find( link[0] ) == linkSet.end() )
10218 // by links, find faces in the face sets,
10219 // and find indices of link nodes in the found faces;
10220 // in a face set, there is only one or no face sharing a link
10221 // ---------------------------------------------------------------
10223 const SMDS_MeshElement* face[] = { 0, 0 };
10224 list<const SMDS_MeshNode*> notLinkNodes[2];
10225 //bool reverse[] = { false, false }; // order of notLinkNodes
10227 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10229 const SMDS_MeshNode* n1 = link[iSide].first;
10230 const SMDS_MeshNode* n2 = link[iSide].second;
10231 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10232 set< const SMDS_MeshElement* > facesOfNode1;
10233 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10235 // during a loop of the first node, we find all faces around n1,
10236 // during a loop of the second node, we find one face sharing both n1 and n2
10237 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10238 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10239 while ( fIt->more() ) { // loop on faces sharing a node
10240 const SMDS_MeshElement* f = fIt->next();
10241 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10242 ! facesOfNode1.insert( f ).second ) // f encounters twice
10244 if ( face[ iSide ] ) {
10245 MESSAGE( "2 faces per link " );
10246 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10249 faceSet->erase( f );
10251 // get not link nodes
10252 int nbN = f->NbNodes();
10253 if ( f->IsQuadratic() )
10255 nbNodes[ iSide ] = nbN;
10256 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10257 int i1 = f->GetNodeIndex( n1 );
10258 int i2 = f->GetNodeIndex( n2 );
10259 int iEnd = nbN, iBeg = -1, iDelta = 1;
10260 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10262 std::swap( iEnd, iBeg ); iDelta = -1;
10267 if ( i == iEnd ) i = iBeg + iDelta;
10268 if ( i == i1 ) break;
10269 nodes.push_back ( f->GetNode( i ) );
10275 // check similarity of elements of the sides
10276 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10277 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10278 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10279 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10282 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10286 // set nodes to merge
10287 // -------------------
10289 if ( face[0] && face[1] ) {
10290 if ( nbNodes[0] != nbNodes[1] ) {
10291 MESSAGE("Diff nb of face nodes");
10292 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10294 #ifdef DEBUG_MATCHING_NODES
10295 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10296 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10297 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10299 int nbN = nbNodes[0];
10301 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10302 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10303 for ( int i = 0 ; i < nbN - 2; ++i ) {
10304 #ifdef DEBUG_MATCHING_NODES
10305 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10307 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10311 // add other links of the face 1 to linkList
10312 // -----------------------------------------
10314 const SMDS_MeshElement* f0 = face[0];
10315 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10316 for ( int i = 0; i < nbN; i++ )
10318 const SMDS_MeshNode* n2 = f0->GetNode( i );
10319 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10320 linkSet.insert( SMESH_TLink( n1, n2 ));
10321 if ( !iter_isnew.second ) { // already in a set: no need to process
10322 linkSet.erase( iter_isnew.first );
10324 else // new in set == encountered for the first time: add
10326 #ifdef DEBUG_MATCHING_NODES
10327 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10328 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10330 linkList[0].push_back ( NLink( n1, n2 ));
10331 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10336 } // loop on link lists
10341 //================================================================================
10343 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10344 \param theElems - the list of elements (edges or faces) to be replicated
10345 The nodes for duplication could be found from these elements
10346 \param theNodesNot - list of nodes to NOT replicate
10347 \param theAffectedElems - the list of elements (cells and edges) to which the
10348 replicated nodes should be associated to.
10349 \return TRUE if operation has been completed successfully, FALSE otherwise
10351 //================================================================================
10353 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10354 const TIDSortedElemSet& theNodesNot,
10355 const TIDSortedElemSet& theAffectedElems )
10357 myLastCreatedElems.Clear();
10358 myLastCreatedNodes.Clear();
10360 if ( theElems.size() == 0 )
10363 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10368 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10369 // duplicate elements and nodes
10370 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10371 // replce nodes by duplications
10372 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10376 //================================================================================
10378 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10379 \param theMeshDS - mesh instance
10380 \param theElems - the elements replicated or modified (nodes should be changed)
10381 \param theNodesNot - nodes to NOT replicate
10382 \param theNodeNodeMap - relation of old node to new created node
10383 \param theIsDoubleElem - flag os to replicate element or modify
10384 \return TRUE if operation has been completed successfully, FALSE otherwise
10386 //================================================================================
10388 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10389 const TIDSortedElemSet& theElems,
10390 const TIDSortedElemSet& theNodesNot,
10391 std::map< const SMDS_MeshNode*,
10392 const SMDS_MeshNode* >& theNodeNodeMap,
10393 const bool theIsDoubleElem )
10395 MESSAGE("doubleNodes");
10396 // iterate on through element and duplicate them (by nodes duplication)
10398 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10399 for ( ; elemItr != theElems.end(); ++elemItr )
10401 const SMDS_MeshElement* anElem = *elemItr;
10405 bool isDuplicate = false;
10406 // duplicate nodes to duplicate element
10407 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10408 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10410 while ( anIter->more() )
10413 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10414 SMDS_MeshNode* aNewNode = aCurrNode;
10415 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10416 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10417 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10420 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10421 theNodeNodeMap[ aCurrNode ] = aNewNode;
10422 myLastCreatedNodes.Append( aNewNode );
10424 isDuplicate |= (aCurrNode != aNewNode);
10425 newNodes[ ind++ ] = aNewNode;
10427 if ( !isDuplicate )
10430 if ( theIsDoubleElem )
10431 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10434 MESSAGE("ChangeElementNodes");
10435 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10442 //================================================================================
10444 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10445 \param theNodes - identifiers of nodes to be doubled
10446 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10447 nodes. If list of element identifiers is empty then nodes are doubled but
10448 they not assigned to elements
10449 \return TRUE if operation has been completed successfully, FALSE otherwise
10451 //================================================================================
10453 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10454 const std::list< int >& theListOfModifiedElems )
10456 MESSAGE("DoubleNodes");
10457 myLastCreatedElems.Clear();
10458 myLastCreatedNodes.Clear();
10460 if ( theListOfNodes.size() == 0 )
10463 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10467 // iterate through nodes and duplicate them
10469 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10471 std::list< int >::const_iterator aNodeIter;
10472 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10474 int aCurr = *aNodeIter;
10475 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10481 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10484 anOldNodeToNewNode[ aNode ] = aNewNode;
10485 myLastCreatedNodes.Append( aNewNode );
10489 // Create map of new nodes for modified elements
10491 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10493 std::list< int >::const_iterator anElemIter;
10494 for ( anElemIter = theListOfModifiedElems.begin();
10495 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10497 int aCurr = *anElemIter;
10498 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10502 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10504 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10506 while ( anIter->more() )
10508 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10509 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10511 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10512 aNodeArr[ ind++ ] = aNewNode;
10515 aNodeArr[ ind++ ] = aCurrNode;
10517 anElemToNodes[ anElem ] = aNodeArr;
10520 // Change nodes of elements
10522 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10523 anElemToNodesIter = anElemToNodes.begin();
10524 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10526 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10527 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10530 MESSAGE("ChangeElementNodes");
10531 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10540 //================================================================================
10542 \brief Check if element located inside shape
10543 \return TRUE if IN or ON shape, FALSE otherwise
10545 //================================================================================
10547 template<class Classifier>
10548 bool isInside(const SMDS_MeshElement* theElem,
10549 Classifier& theClassifier,
10550 const double theTol)
10552 gp_XYZ centerXYZ (0, 0, 0);
10553 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10554 while (aNodeItr->more())
10555 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10557 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10558 theClassifier.Perform(aPnt, theTol);
10559 TopAbs_State aState = theClassifier.State();
10560 return (aState == TopAbs_IN || aState == TopAbs_ON );
10563 //================================================================================
10565 * \brief Classifier of the 3D point on the TopoDS_Face
10566 * with interaface suitable for isInside()
10568 //================================================================================
10570 struct _FaceClassifier
10572 Extrema_ExtPS _extremum;
10573 BRepAdaptor_Surface _surface;
10574 TopAbs_State _state;
10576 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10578 _extremum.Initialize( _surface,
10579 _surface.FirstUParameter(), _surface.LastUParameter(),
10580 _surface.FirstVParameter(), _surface.LastVParameter(),
10581 _surface.Tolerance(), _surface.Tolerance() );
10583 void Perform(const gp_Pnt& aPnt, double theTol)
10585 _state = TopAbs_OUT;
10586 _extremum.Perform(aPnt);
10587 if ( _extremum.IsDone() )
10588 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10589 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10590 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10592 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10595 TopAbs_State State() const
10602 //================================================================================
10604 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10605 \param theElems - group of of elements (edges or faces) to be replicated
10606 \param theNodesNot - group of nodes not to replicate
10607 \param theShape - shape to detect affected elements (element which geometric center
10608 located on or inside shape).
10609 The replicated nodes should be associated to affected elements.
10610 \return TRUE if operation has been completed successfully, FALSE otherwise
10612 //================================================================================
10614 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10615 const TIDSortedElemSet& theNodesNot,
10616 const TopoDS_Shape& theShape )
10618 if ( theShape.IsNull() )
10621 const double aTol = Precision::Confusion();
10622 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10623 auto_ptr<_FaceClassifier> aFaceClassifier;
10624 if ( theShape.ShapeType() == TopAbs_SOLID )
10626 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10627 bsc3d->PerformInfinitePoint(aTol);
10629 else if (theShape.ShapeType() == TopAbs_FACE )
10631 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10634 // iterates on indicated elements and get elements by back references from their nodes
10635 TIDSortedElemSet anAffected;
10636 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10637 for ( ; elemItr != theElems.end(); ++elemItr )
10639 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10643 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10644 while ( nodeItr->more() )
10646 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10647 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10649 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10650 while ( backElemItr->more() )
10652 const SMDS_MeshElement* curElem = backElemItr->next();
10653 if ( curElem && theElems.find(curElem) == theElems.end() &&
10655 isInside( curElem, *bsc3d, aTol ) :
10656 isInside( curElem, *aFaceClassifier, aTol )))
10657 anAffected.insert( curElem );
10661 return DoubleNodes( theElems, theNodesNot, anAffected );
10665 * \brief compute an oriented angle between two planes defined by four points.
10666 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10667 * @param p0 base of the rotation axe
10668 * @param p1 extremity of the rotation axe
10669 * @param g1 belongs to the first plane
10670 * @param g2 belongs to the second plane
10672 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10674 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10675 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10676 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10677 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10678 gp_Vec vref(p0, p1);
10681 gp_Vec n1 = vref.Crossed(v1);
10682 gp_Vec n2 = vref.Crossed(v2);
10683 return n2.AngleWithRef(n1, vref);
10687 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10688 * The list of groups must describe a partition of the mesh volumes.
10689 * The nodes of the internal faces at the boundaries of the groups are doubled.
10690 * In option, the internal faces are replaced by flat elements.
10691 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10692 * The flat elements are stored in groups of volumes.
10693 * @param theElems - list of groups of volumes, where a group of volume is a set of
10694 * SMDS_MeshElements sorted by Id.
10695 * @param createJointElems - if TRUE, create the elements
10696 * @return TRUE if operation has been completed successfully, FALSE otherwise
10698 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10699 bool createJointElems)
10701 MESSAGE("----------------------------------------------");
10702 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10703 MESSAGE("----------------------------------------------");
10705 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10706 meshDS->BuildDownWardConnectivity(true);
10708 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10710 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10711 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10712 // build the list of nodes shared by 2 or more domains, with their domain indexes
10714 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10715 std::map<int,int>celldom; // cell vtkId --> domain
10716 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10717 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10718 faceDomains.clear();
10720 cellDomains.clear();
10721 nodeDomains.clear();
10722 std::map<int,int> emptyMap;
10723 std::set<int> emptySet;
10726 for (int idom = 0; idom < theElems.size(); idom++)
10729 // --- build a map (face to duplicate --> volume to modify)
10730 // with all the faces shared by 2 domains (group of elements)
10731 // and corresponding volume of this domain, for each shared face.
10732 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10734 const TIDSortedElemSet& domain = theElems[idom];
10735 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10736 for (; elemItr != domain.end(); ++elemItr)
10738 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10741 int vtkId = anElem->getVtkId();
10742 int neighborsVtkIds[NBMAXNEIGHBORS];
10743 int downIds[NBMAXNEIGHBORS];
10744 unsigned char downTypes[NBMAXNEIGHBORS];
10745 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10746 for (int n = 0; n < nbNeighbors; n++)
10748 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10749 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10750 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10752 DownIdType face(downIds[n], downTypes[n]);
10753 if (!faceDomains.count(face))
10754 faceDomains[face] = emptyMap; // create an empty entry for face
10755 if (!faceDomains[face].count(idom))
10757 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10758 celldom[vtkId] = idom;
10765 //MESSAGE("Number of shared faces " << faceDomains.size());
10766 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10768 // --- explore the shared faces domain by domain,
10769 // explore the nodes of the face and see if they belong to a cell in the domain,
10770 // which has only a node or an edge on the border (not a shared face)
10772 for (int idomain = 0; idomain < theElems.size(); idomain++)
10774 const TIDSortedElemSet& domain = theElems[idomain];
10775 itface = faceDomains.begin();
10776 for (; itface != faceDomains.end(); ++itface)
10778 std::map<int, int> domvol = itface->second;
10779 if (!domvol.count(idomain))
10781 DownIdType face = itface->first;
10782 //MESSAGE(" --- face " << face.cellId);
10783 std::set<int> oldNodes;
10785 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10786 std::set<int>::iterator itn = oldNodes.begin();
10787 for (; itn != oldNodes.end(); ++itn)
10790 //MESSAGE(" node " << oldId);
10791 std::set<int> cells;
10793 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10794 for (int i=0; i<l.ncells; i++)
10796 int vtkId = l.cells[i];
10797 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10798 if (!domain.count(anElem))
10800 int vtkType = grid->GetCellType(vtkId);
10801 int downId = grid->CellIdToDownId(vtkId);
10804 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10805 continue; // not OK at this stage of the algorithm:
10806 //no cells created after BuildDownWardConnectivity
10808 DownIdType aCell(downId, vtkType);
10809 if (celldom.count(vtkId))
10811 cellDomains[aCell][idomain] = vtkId;
10812 celldom[vtkId] = idomain;
10818 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10819 // for each shared face, get the nodes
10820 // for each node, for each domain of the face, create a clone of the node
10822 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10823 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10824 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10826 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10827 std::map<int, std::vector<int> > mutipleNodes; // nodes muti domains with domain order
10829 for (int idomain = 0; idomain < theElems.size(); idomain++)
10831 itface = faceDomains.begin();
10832 for (; itface != faceDomains.end(); ++itface)
10834 std::map<int, int> domvol = itface->second;
10835 if (!domvol.count(idomain))
10837 DownIdType face = itface->first;
10838 //MESSAGE(" --- face " << face.cellId);
10839 std::set<int> oldNodes;
10841 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10842 bool isMultipleDetected = false;
10843 std::set<int>::iterator itn = oldNodes.begin();
10844 for (; itn != oldNodes.end(); ++itn)
10847 //MESSAGE(" node " << oldId);
10848 if (!nodeDomains.count(oldId))
10849 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10850 if (nodeDomains[oldId].empty())
10851 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10852 std::map<int, int>::iterator itdom = domvol.begin();
10853 for (; itdom != domvol.end(); ++itdom)
10855 int idom = itdom->first;
10856 //MESSAGE(" domain " << idom);
10857 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10859 if (nodeDomains[oldId].size() >= 2) // a multiple node
10861 vector<int> orderedDoms;
10862 //MESSAGE("multiple node " << oldId);
10863 isMultipleDetected =true;
10864 if (mutipleNodes.count(oldId))
10865 orderedDoms = mutipleNodes[oldId];
10868 map<int,int>::iterator it = nodeDomains[oldId].begin();
10869 for (; it != nodeDomains[oldId].end(); ++it)
10870 orderedDoms.push_back(it->first);
10872 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10873 //stringstream txt;
10874 //for (int i=0; i<orderedDoms.size(); i++)
10875 // txt << orderedDoms[i] << " ";
10876 //MESSAGE("orderedDoms " << txt.str());
10877 mutipleNodes[oldId] = orderedDoms;
10879 double *coords = grid->GetPoint(oldId);
10880 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10881 int newId = newNode->getVtkId();
10882 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10883 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
10885 if (nodeDomains[oldId].size() >= 3)
10887 //MESSAGE("confirm multiple node " << oldId);
10888 isMultipleDetected =true;
10892 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
10894 //MESSAGE("multiple Nodes detected on a shared face");
10895 int downId = itface->first.cellId;
10896 unsigned char cellType = itface->first.cellType;
10897 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10898 const int *downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10899 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10900 for (int ie =0; ie < nbEdges; ie++)
10903 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10904 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10906 vector<int> vn0 = mutipleNodes[nodes[0]];
10907 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10908 sort( vn0.begin(), vn0.end() );
10909 sort( vn1.begin(), vn1.end() );
10912 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10913 double *coords = grid->GetPoint(nodes[0]);
10914 gp_Pnt p0(coords[0], coords[1], coords[2]);
10915 coords = grid->GetPoint(nodes[nbNodes - 1]);
10916 gp_Pnt p1(coords[0], coords[1], coords[2]);
10918 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10919 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10920 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10921 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10922 for (int id=0; id < vn0.size(); id++)
10924 int idom = vn0[id];
10925 for (int ivol=0; ivol<nbvol; ivol++)
10927 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10928 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10929 if (theElems[idom].count(elem))
10931 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10932 domvol[idom] = svol;
10933 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10935 vtkIdType npts = 0;
10936 vtkIdType* pts = 0;
10937 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10938 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10941 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10942 angleDom[idom] = 0;
10946 gp_Pnt g(values[0], values[1], values[2]);
10947 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10948 //MESSAGE(" angle=" << angleDom[idom]);
10954 map<double, int> sortedDom; // sort domains by angle
10955 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10956 sortedDom[ia->second] = ia->first;
10957 vector<int> vnodes;
10959 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10961 vdom.push_back(ib->second);
10962 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10964 for (int ino = 0; ino < nbNodes; ino++)
10965 vnodes.push_back(nodes[ino]);
10966 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10974 // --- iterate on shared faces (volumes to modify, face to extrude)
10975 // get node id's of the face (id SMDS = id VTK)
10976 // create flat element with old and new nodes if requested
10978 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10979 // (domain1 X domain2) = domain1 + MAXINT*domain2
10981 std::map<int, std::map<long,int> > nodeQuadDomains;
10982 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10984 if (createJointElems)
10986 itface = faceDomains.begin();
10987 for (; itface != faceDomains.end(); ++itface)
10989 DownIdType face = itface->first;
10990 std::set<int> oldNodes;
10991 std::set<int>::iterator itn;
10993 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10995 std::map<int, int> domvol = itface->second;
10996 std::map<int, int>::iterator itdom = domvol.begin();
10997 int dom1 = itdom->first;
10998 int vtkVolId = itdom->second;
11000 int dom2 = itdom->first;
11001 SMDS_MeshVolume *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11003 stringstream grpname;
11006 grpname << dom1 << "_" << dom2;
11008 grpname << dom2 << "_" << dom1;
11010 string namegrp = grpname.str();
11011 if (!mapOfJunctionGroups.count(namegrp))
11012 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11013 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11015 sgrp->Add(vol->GetID());
11019 // --- create volumes on multiple domain intersection if requested
11020 // iterate on edgesMultiDomains
11022 if (createJointElems)
11024 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11025 for (; ite != edgesMultiDomains.end(); ++ite)
11027 vector<int> nodes = ite->first;
11028 vector<int> orderDom = ite->second;
11029 vector<vtkIdType> orderedNodes;
11030 if (nodes.size() == 2)
11032 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11033 for (int ino=0; ino < nodes.size(); ino++)
11034 if (orderDom.size() == 3)
11035 for (int idom = 0; idom <orderDom.size(); idom++)
11036 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11038 for (int idom = orderDom.size()-1; idom >=0; idom--)
11039 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11040 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11042 stringstream grpname;
11044 grpname << 0 << "_" << 0;
11046 string namegrp = grpname.str();
11047 if (!mapOfJunctionGroups.count(namegrp))
11048 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11049 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11051 sgrp->Add(vol->GetID());
11055 //MESSAGE("Quadratic multiple joints not implemented");
11056 // TODO quadratic nodes
11061 // --- list the explicit faces and edges of the mesh that need to be modified,
11062 // i.e. faces and edges built with one or more duplicated nodes.
11063 // associate these faces or edges to their corresponding domain.
11064 // only the first domain found is kept when a face or edge is shared
11066 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11067 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11068 faceOrEdgeDom.clear();
11071 for (int idomain = 0; idomain < theElems.size(); idomain++)
11073 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11074 for (; itnod != nodeDomains.end(); ++itnod)
11076 int oldId = itnod->first;
11077 //MESSAGE(" node " << oldId);
11078 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11079 for (int i = 0; i < l.ncells; i++)
11081 int vtkId = l.cells[i];
11082 int vtkType = grid->GetCellType(vtkId);
11083 int downId = grid->CellIdToDownId(vtkId);
11085 continue; // new cells: not to be modified
11086 DownIdType aCell(downId, vtkType);
11087 int volParents[1000];
11088 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11089 for (int j = 0; j < nbvol; j++)
11090 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11091 if (!feDom.count(vtkId))
11093 feDom[vtkId] = idomain;
11094 faceOrEdgeDom[aCell] = emptyMap;
11095 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11096 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11097 // << " type " << vtkType << " downId " << downId);
11103 // --- iterate on shared faces (volumes to modify, face to extrude)
11104 // get node id's of the face
11105 // replace old nodes by new nodes in volumes, and update inverse connectivity
11107 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11108 for (int m=0; m<3; m++)
11110 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11111 itface = (*amap).begin();
11112 for (; itface != (*amap).end(); ++itface)
11114 DownIdType face = itface->first;
11115 std::set<int> oldNodes;
11116 std::set<int>::iterator itn;
11118 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11119 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11120 std::map<int, int> localClonedNodeIds;
11122 std::map<int, int> domvol = itface->second;
11123 std::map<int, int>::iterator itdom = domvol.begin();
11124 for (; itdom != domvol.end(); ++itdom)
11126 int idom = itdom->first;
11127 int vtkVolId = itdom->second;
11128 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11129 localClonedNodeIds.clear();
11130 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11133 if (nodeDomains[oldId].count(idom))
11135 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11136 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11139 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11144 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11145 grid->BuildLinks();
11153 * \brief Double nodes on some external faces and create flat elements.
11154 * Flat elements are mainly used by some types of mechanic calculations.
11156 * Each group of the list must be constituted of faces.
11157 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11158 * @param theElems - list of groups of faces, where a group of faces is a set of
11159 * SMDS_MeshElements sorted by Id.
11160 * @return TRUE if operation has been completed successfully, FALSE otherwise
11162 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11164 MESSAGE("-------------------------------------------------");
11165 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11166 MESSAGE("-------------------------------------------------");
11168 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11170 // --- For each group of faces
11171 // duplicate the nodes, create a flat element based on the face
11172 // replace the nodes of the faces by their clones
11174 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11175 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11176 clonedNodes.clear();
11177 intermediateNodes.clear();
11178 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11179 mapOfJunctionGroups.clear();
11181 for (int idom = 0; idom < theElems.size(); idom++)
11183 const TIDSortedElemSet& domain = theElems[idom];
11184 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11185 for (; elemItr != domain.end(); ++elemItr)
11187 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11188 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11191 // MESSAGE("aFace=" << aFace->GetID());
11192 bool isQuad = aFace->IsQuadratic();
11193 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11195 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11197 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11198 while (nodeIt->more())
11200 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11201 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11203 ln2.push_back(node);
11205 ln0.push_back(node);
11207 const SMDS_MeshNode* clone = 0;
11208 if (!clonedNodes.count(node))
11210 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11211 clonedNodes[node] = clone;
11214 clone = clonedNodes[node];
11217 ln3.push_back(clone);
11219 ln1.push_back(clone);
11221 const SMDS_MeshNode* inter = 0;
11222 if (isQuad && (!isMedium))
11224 if (!intermediateNodes.count(node))
11226 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11227 intermediateNodes[node] = inter;
11230 inter = intermediateNodes[node];
11231 ln4.push_back(inter);
11235 // --- extrude the face
11237 vector<const SMDS_MeshNode*> ln;
11238 SMDS_MeshVolume* vol = 0;
11239 vtkIdType aType = aFace->GetVtkType();
11243 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11244 // MESSAGE("vol prism " << vol->GetID());
11245 ln.push_back(ln1[0]);
11246 ln.push_back(ln1[1]);
11247 ln.push_back(ln1[2]);
11250 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11251 // MESSAGE("vol hexa " << vol->GetID());
11252 ln.push_back(ln1[0]);
11253 ln.push_back(ln1[1]);
11254 ln.push_back(ln1[2]);
11255 ln.push_back(ln1[3]);
11257 case VTK_QUADRATIC_TRIANGLE:
11258 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11259 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11260 // MESSAGE("vol quad prism " << vol->GetID());
11261 ln.push_back(ln1[0]);
11262 ln.push_back(ln1[1]);
11263 ln.push_back(ln1[2]);
11264 ln.push_back(ln3[0]);
11265 ln.push_back(ln3[1]);
11266 ln.push_back(ln3[2]);
11268 case VTK_QUADRATIC_QUAD:
11269 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11270 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11271 // ln4[0], ln4[1], ln4[2], ln4[3]);
11272 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11273 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11274 ln4[0], ln4[1], ln4[2], ln4[3]);
11275 // MESSAGE("vol quad hexa " << vol->GetID());
11276 ln.push_back(ln1[0]);
11277 ln.push_back(ln1[1]);
11278 ln.push_back(ln1[2]);
11279 ln.push_back(ln1[3]);
11280 ln.push_back(ln3[0]);
11281 ln.push_back(ln3[1]);
11282 ln.push_back(ln3[2]);
11283 ln.push_back(ln3[3]);
11293 stringstream grpname;
11297 string namegrp = grpname.str();
11298 if (!mapOfJunctionGroups.count(namegrp))
11299 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11300 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11302 sgrp->Add(vol->GetID());
11305 // --- modify the face
11307 aFace->ChangeNodes(&ln[0], ln.size());
11313 //================================================================================
11315 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11316 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11317 * \return TRUE if operation has been completed successfully, FALSE otherwise
11319 //================================================================================
11321 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11323 // iterates on volume elements and detect all free faces on them
11324 SMESHDS_Mesh* aMesh = GetMeshDS();
11327 //bool res = false;
11328 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11329 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11332 const SMDS_MeshVolume* volume = vIt->next();
11333 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11334 vTool.SetExternalNormal();
11335 //const bool isPoly = volume->IsPoly();
11336 const int iQuad = volume->IsQuadratic();
11337 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11339 if (!vTool.IsFreeFace(iface))
11342 vector<const SMDS_MeshNode *> nodes;
11343 int nbFaceNodes = vTool.NbFaceNodes(iface);
11344 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11346 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11347 nodes.push_back(faceNodes[inode]);
11348 if (iQuad) { // add medium nodes
11349 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11350 nodes.push_back(faceNodes[inode]);
11351 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11352 nodes.push_back(faceNodes[8]);
11354 // add new face based on volume nodes
11355 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11357 continue; // face already exsist
11359 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11363 return ( nbFree==(nbExisted+nbCreated) );
11368 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11370 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11372 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11375 //================================================================================
11377 * \brief Creates missing boundary elements
11378 * \param elements - elements whose boundary is to be checked
11379 * \param dimension - defines type of boundary elements to create
11380 * \param group - a group to store created boundary elements in
11381 * \param targetMesh - a mesh to store created boundary elements in
11382 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11383 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11384 * boundary elements will be copied into the targetMesh
11385 * \param toAddExistingBondary - if true, not only new but also pre-existing
11386 * boundary elements will be added into the new group
11387 * \param aroundElements - if true, elements will be created on boundary of given
11388 * elements else, on boundary of the whole mesh.
11389 * \return nb of added boundary elements
11391 //================================================================================
11393 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11394 Bnd_Dimension dimension,
11395 SMESH_Group* group/*=0*/,
11396 SMESH_Mesh* targetMesh/*=0*/,
11397 bool toCopyElements/*=false*/,
11398 bool toCopyExistingBoundary/*=false*/,
11399 bool toAddExistingBondary/*= false*/,
11400 bool aroundElements/*= false*/)
11402 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11403 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11404 // hope that all elements are of the same type, do not check them all
11405 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11406 throw SALOME_Exception(LOCALIZED("wrong element type"));
11409 toCopyElements = toCopyExistingBoundary = false;
11411 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11412 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11413 int nbAddedBnd = 0;
11415 // editor adding present bnd elements and optionally holding elements to add to the group
11416 SMESH_MeshEditor* presentEditor;
11417 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11418 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11420 SMDS_VolumeTool vTool;
11421 TIDSortedElemSet avoidSet;
11422 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11425 typedef vector<const SMDS_MeshNode*> TConnectivity;
11427 SMDS_ElemIteratorPtr eIt;
11428 if (elements.empty())
11429 eIt = aMesh->elementsIterator(elemType);
11431 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11433 while (eIt->more())
11435 const SMDS_MeshElement* elem = eIt->next();
11436 const int iQuad = elem->IsQuadratic();
11438 // ------------------------------------------------------------------------------------
11439 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11440 // ------------------------------------------------------------------------------------
11441 vector<const SMDS_MeshElement*> presentBndElems;
11442 vector<TConnectivity> missingBndElems;
11443 TConnectivity nodes;
11444 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11446 vTool.SetExternalNormal();
11447 const SMDS_MeshElement* otherVol = 0;
11448 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11450 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11451 ( !aroundElements || elements.count( otherVol )))
11453 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11454 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11455 if ( missType == SMDSAbs_Edge ) // boundary edges
11457 nodes.resize( 2+iQuad );
11458 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11460 for ( int j = 0; j < nodes.size(); ++j )
11462 if ( const SMDS_MeshElement* edge =
11463 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11464 presentBndElems.push_back( edge );
11466 missingBndElems.push_back( nodes );
11469 else // boundary face
11472 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11473 nodes.push_back( nn[inode] );
11474 if (iQuad) // add medium nodes
11475 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11476 nodes.push_back( nn[inode] );
11477 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11479 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11481 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11482 SMDSAbs_Face, /*noMedium=*/false ))
11483 presentBndElems.push_back( f );
11485 missingBndElems.push_back( nodes );
11487 if ( targetMesh != myMesh )
11489 // add 1D elements on face boundary to be added to a new mesh
11490 const SMDS_MeshElement* edge;
11491 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11494 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11496 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11497 if ( edge && avoidSet.insert( edge ).second )
11498 presentBndElems.push_back( edge );
11504 else // elem is a face ------------------------------------------
11506 avoidSet.clear(), avoidSet.insert( elem );
11507 int nbNodes = elem->NbCornerNodes();
11508 nodes.resize( 2 /*+ iQuad*/);
11509 for ( int i = 0; i < nbNodes; i++ )
11511 nodes[0] = elem->GetNode(i);
11512 nodes[1] = elem->GetNode((i+1)%nbNodes);
11513 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11514 continue; // not free link
11517 //nodes[2] = elem->GetNode( i + nbNodes );
11518 if ( const SMDS_MeshElement* edge =
11519 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11520 presentBndElems.push_back( edge );
11522 missingBndElems.push_back( nodes );
11526 // ---------------------------------
11527 // 2. Add missing boundary elements
11528 // ---------------------------------
11529 if ( targetMesh != myMesh )
11530 // instead of making a map of nodes in this mesh and targetMesh,
11531 // we create nodes with same IDs.
11532 for ( int i = 0; i < missingBndElems.size(); ++i )
11534 TConnectivity& srcNodes = missingBndElems[i];
11535 TConnectivity nodes( srcNodes.size() );
11536 for ( inode = 0; inode < nodes.size(); ++inode )
11537 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11538 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11540 /*noMedium=*/false))
11542 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11546 for ( int i = 0; i < missingBndElems.size(); ++i )
11548 TConnectivity& nodes = missingBndElems[i];
11549 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11551 /*noMedium=*/false))
11553 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11557 // ----------------------------------
11558 // 3. Copy present boundary elements
11559 // ----------------------------------
11560 if ( toCopyExistingBoundary )
11561 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11563 const SMDS_MeshElement* e = presentBndElems[i];
11564 TConnectivity nodes( e->NbNodes() );
11565 for ( inode = 0; inode < nodes.size(); ++inode )
11566 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11567 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11569 else // store present elements to add them to a group
11570 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11572 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11575 } // loop on given elements
11577 // ---------------------------------------------
11578 // 4. Fill group with boundary elements
11579 // ---------------------------------------------
11582 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11583 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11584 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11586 tgtEditor.myLastCreatedElems.Clear();
11587 tgtEditor2.myLastCreatedElems.Clear();
11589 // -----------------------
11590 // 5. Copy given elements
11591 // -----------------------
11592 if ( toCopyElements && targetMesh != myMesh )
11594 if (elements.empty())
11595 eIt = aMesh->elementsIterator(elemType);
11597 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11598 while (eIt->more())
11600 const SMDS_MeshElement* elem = eIt->next();
11601 TConnectivity nodes( elem->NbNodes() );
11602 for ( inode = 0; inode < nodes.size(); ++inode )
11603 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11604 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11606 tgtEditor.myLastCreatedElems.Clear();