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)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
85 #include <gp_Trsf.hxx>
99 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
102 using namespace SMESH::Controls;
104 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
105 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
107 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
109 //=======================================================================
110 //function : SMESH_MeshEditor
112 //=======================================================================
114 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
115 :myMesh( theMesh ) // theMesh may be NULL
119 //=======================================================================
123 //=======================================================================
126 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
127 const SMDSAbs_ElementType type,
131 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
132 SMDS_MeshElement* e = 0;
133 int nbnode = node.size();
134 SMESHDS_Mesh* mesh = GetMeshDS();
139 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
140 else e = mesh->AddFace (node[0], node[1], node[2] );
142 else if (nbnode == 4) {
143 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
144 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
146 else if (nbnode == 6) {
147 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
148 node[4], node[5], ID);
149 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
152 else if (nbnode == 8) {
153 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
154 node[4], node[5], node[6], node[7], ID);
155 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
156 node[4], node[5], node[6], node[7] );
158 else if (nbnode == 9) {
159 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
160 node[4], node[5], node[6], node[7], node[8], ID);
161 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
162 node[4], node[5], node[6], node[7], node[8] );
165 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
166 else e = mesh->AddPolygonalFace (node );
173 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
174 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
176 else if (nbnode == 5) {
177 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
179 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
182 else if (nbnode == 6) {
183 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
184 node[4], node[5], ID);
185 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
188 else if (nbnode == 8) {
189 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
190 node[4], node[5], node[6], node[7], ID);
191 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
192 node[4], node[5], node[6], node[7] );
194 else if (nbnode == 10) {
195 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
196 node[4], node[5], node[6], node[7],
197 node[8], node[9], ID);
198 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
199 node[4], node[5], node[6], node[7],
202 else if (nbnode == 12) {
203 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], node[6], node[7],
205 node[8], node[9], node[10], node[11], ID);
206 else e = mesh->AddVolume (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] );
210 else if (nbnode == 13) {
211 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], node[7],
213 node[8], node[9], node[10],node[11],
215 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7],
217 node[8], node[9], node[10],node[11],
220 else if (nbnode == 15) {
221 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7],
223 node[8], node[9], node[10],node[11],
224 node[12],node[13],node[14],ID);
225 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7],
227 node[8], node[9], node[10],node[11],
228 node[12],node[13],node[14] );
230 else if (nbnode == 20) {
231 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7],
233 node[8], node[9], node[10],node[11],
234 node[12],node[13],node[14],node[15],
235 node[16],node[17],node[18],node[19],ID);
236 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
237 node[4], node[5], node[6], node[7],
238 node[8], node[9], node[10],node[11],
239 node[12],node[13],node[14],node[15],
240 node[16],node[17],node[18],node[19] );
242 else if (nbnode == 27) {
243 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
244 node[4], node[5], node[6], node[7],
245 node[8], node[9], node[10],node[11],
246 node[12],node[13],node[14],node[15],
247 node[16],node[17],node[18],node[19],
248 node[20],node[21],node[22],node[23],
249 node[24],node[25],node[26], ID);
250 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
251 node[4], node[5], node[6], node[7],
252 node[8], node[9], node[10],node[11],
253 node[12],node[13],node[14],node[15],
254 node[16],node[17],node[18],node[19],
255 node[20],node[21],node[22],node[23],
256 node[24],node[25],node[26] );
263 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
264 else e = mesh->AddEdge (node[0], node[1] );
266 else if ( nbnode == 3 ) {
267 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
268 else e = mesh->AddEdge (node[0], node[1], node[2] );
272 case SMDSAbs_0DElement:
274 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
275 else e = mesh->Add0DElement (node[0] );
280 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
281 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
286 if ( e ) myLastCreatedElems.Append( e );
290 //=======================================================================
294 //=======================================================================
296 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
297 const SMDSAbs_ElementType type,
301 vector<const SMDS_MeshNode*> nodes;
302 nodes.reserve( nodeIDs.size() );
303 vector<int>::const_iterator id = nodeIDs.begin();
304 while ( id != nodeIDs.end() ) {
305 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
306 nodes.push_back( node );
310 return AddElement( nodes, type, isPoly, ID );
313 //=======================================================================
315 //purpose : Remove a node or an element.
316 // Modify a compute state of sub-meshes which become empty
317 //=======================================================================
319 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
322 myLastCreatedElems.Clear();
323 myLastCreatedNodes.Clear();
325 SMESHDS_Mesh* aMesh = GetMeshDS();
326 set< SMESH_subMesh *> smmap;
329 list<int>::const_iterator it = theIDs.begin();
330 for ( ; it != theIDs.end(); it++ ) {
331 const SMDS_MeshElement * elem;
333 elem = aMesh->FindNode( *it );
335 elem = aMesh->FindElement( *it );
339 // Notify VERTEX sub-meshes about modification
341 const SMDS_MeshNode* node = cast2Node( elem );
342 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
343 if ( int aShapeID = node->getshapeId() )
344 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
347 // Find sub-meshes to notify about modification
348 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
349 // while ( nodeIt->more() ) {
350 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
351 // const SMDS_PositionPtr& aPosition = node->GetPosition();
352 // if ( aPosition.get() ) {
353 // if ( int aShapeID = aPosition->GetShapeId() ) {
354 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
355 // smmap.insert( sm );
362 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
364 aMesh->RemoveElement( elem );
368 // Notify sub-meshes about modification
369 if ( !smmap.empty() ) {
370 set< SMESH_subMesh *>::iterator smIt;
371 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
372 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
375 // // Check if the whole mesh becomes empty
376 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
377 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
382 //=======================================================================
383 //function : FindShape
384 //purpose : Return an index of the shape theElem is on
385 // or zero if a shape not found
386 //=======================================================================
388 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
390 myLastCreatedElems.Clear();
391 myLastCreatedNodes.Clear();
393 SMESHDS_Mesh * aMesh = GetMeshDS();
394 if ( aMesh->ShapeToMesh().IsNull() )
397 int aShapeID = theElem->getshapeId();
401 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
402 if ( sm->Contains( theElem ))
405 if ( theElem->GetType() == SMDSAbs_Node ) {
406 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
409 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
412 TopoDS_Shape aShape; // the shape a node of theElem is on
413 if ( theElem->GetType() != SMDSAbs_Node )
415 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
416 while ( nodeIt->more() ) {
417 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
418 if ((aShapeID = node->getshapeId()) > 0) {
419 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
420 if ( sm->Contains( theElem ))
422 if ( aShape.IsNull() )
423 aShape = aMesh->IndexToShape( aShapeID );
429 // None of nodes is on a proper shape,
430 // find the shape among ancestors of aShape on which a node is
431 if ( !aShape.IsNull() ) {
432 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
433 for ( ; ancIt.More(); ancIt.Next() ) {
434 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
435 if ( sm && sm->Contains( theElem ))
436 return aMesh->ShapeToIndex( ancIt.Value() );
441 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
442 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
443 for ( ; id_sm != id2sm.end(); ++id_sm )
444 if ( id_sm->second->Contains( theElem ))
448 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
452 //=======================================================================
453 //function : IsMedium
455 //=======================================================================
457 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
458 const SMDSAbs_ElementType typeToCheck)
460 bool isMedium = false;
461 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
462 while (it->more() && !isMedium ) {
463 const SMDS_MeshElement* elem = it->next();
464 isMedium = elem->IsMediumNode(node);
469 //=======================================================================
470 //function : ShiftNodesQuadTria
472 // Shift nodes in the array corresponded to quadratic triangle
473 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
474 //=======================================================================
475 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
477 const SMDS_MeshNode* nd1 = aNodes[0];
478 aNodes[0] = aNodes[1];
479 aNodes[1] = aNodes[2];
481 const SMDS_MeshNode* nd2 = aNodes[3];
482 aNodes[3] = aNodes[4];
483 aNodes[4] = aNodes[5];
487 //=======================================================================
488 //function : edgeConnectivity
490 // return number of the edges connected with the theNode.
491 // if theEdges has connections with the other type of the
492 // elements, return -1
493 //=======================================================================
494 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
496 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
498 while(elemIt->more()) {
506 //=======================================================================
507 //function : GetNodesFromTwoTria
509 // Shift nodes in the array corresponded to quadratic triangle
510 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
511 //=======================================================================
512 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
513 const SMDS_MeshElement * theTria2,
514 const SMDS_MeshNode* N1[],
515 const SMDS_MeshNode* N2[])
517 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
520 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
523 if(it->more()) return false;
524 it = theTria2->nodesIterator();
527 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
530 if(it->more()) return false;
532 int sames[3] = {-1,-1,-1};
544 if(nbsames!=2) return false;
546 ShiftNodesQuadTria(N1);
548 ShiftNodesQuadTria(N1);
551 i = sames[0] + sames[1] + sames[2];
553 ShiftNodesQuadTria(N2);
555 // now we receive following N1 and N2 (using numeration as above image)
556 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
557 // i.e. first nodes from both arrays determ new diagonal
561 //=======================================================================
562 //function : InverseDiag
563 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
564 // but having other common link.
565 // Return False if args are improper
566 //=======================================================================
568 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
569 const SMDS_MeshElement * theTria2 )
571 MESSAGE("InverseDiag");
572 myLastCreatedElems.Clear();
573 myLastCreatedNodes.Clear();
575 if (!theTria1 || !theTria2)
578 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
579 if (!F1) return false;
580 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
581 if (!F2) return false;
582 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
583 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
585 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
586 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
590 // put nodes in array and find out indices of the same ones
591 const SMDS_MeshNode* aNodes [6];
592 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
594 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
595 while ( it->more() ) {
596 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
598 if ( i > 2 ) // theTria2
599 // find same node of theTria1
600 for ( int j = 0; j < 3; j++ )
601 if ( aNodes[ i ] == aNodes[ j ]) {
610 return false; // theTria1 is not a triangle
611 it = theTria2->nodesIterator();
613 if ( i == 6 && it->more() )
614 return false; // theTria2 is not a triangle
617 // find indices of 1,2 and of A,B in theTria1
618 int iA = 0, iB = 0, i1 = 0, i2 = 0;
619 for ( i = 0; i < 6; i++ ) {
620 if ( sameInd [ i ] == 0 ) {
629 // nodes 1 and 2 should not be the same
630 if ( aNodes[ i1 ] == aNodes[ i2 ] )
634 aNodes[ iA ] = aNodes[ i2 ];
636 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
638 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
639 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
643 } // end if(F1 && F2)
645 // check case of quadratic faces
646 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
648 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
652 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
653 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
661 const SMDS_MeshNode* N1 [6];
662 const SMDS_MeshNode* N2 [6];
663 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
665 // now we receive following N1 and N2 (using numeration as above image)
666 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
667 // i.e. first nodes from both arrays determ new diagonal
669 const SMDS_MeshNode* N1new [6];
670 const SMDS_MeshNode* N2new [6];
683 // replaces nodes in faces
684 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
685 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
690 //=======================================================================
691 //function : findTriangles
692 //purpose : find triangles sharing theNode1-theNode2 link
693 //=======================================================================
695 static bool findTriangles(const SMDS_MeshNode * theNode1,
696 const SMDS_MeshNode * theNode2,
697 const SMDS_MeshElement*& theTria1,
698 const SMDS_MeshElement*& theTria2)
700 if ( !theNode1 || !theNode2 ) return false;
702 theTria1 = theTria2 = 0;
704 set< const SMDS_MeshElement* > emap;
705 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
707 const SMDS_MeshElement* elem = it->next();
708 if ( elem->NbNodes() == 3 )
711 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
713 const SMDS_MeshElement* elem = it->next();
714 if ( emap.find( elem ) != emap.end() ) {
716 // theTria1 must be element with minimum ID
717 if( theTria1->GetID() < elem->GetID() ) {
731 return ( theTria1 && theTria2 );
734 //=======================================================================
735 //function : InverseDiag
736 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
737 // with ones built on the same 4 nodes but having other common link.
738 // Return false if proper faces not found
739 //=======================================================================
741 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
742 const SMDS_MeshNode * theNode2)
744 myLastCreatedElems.Clear();
745 myLastCreatedNodes.Clear();
747 MESSAGE( "::InverseDiag()" );
749 const SMDS_MeshElement *tr1, *tr2;
750 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
753 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
754 if (!F1) return false;
755 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
756 if (!F2) return false;
757 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
758 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
760 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
761 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
765 // put nodes in array
766 // and find indices of 1,2 and of A in tr1 and of B in tr2
767 int i, iA1 = 0, i1 = 0;
768 const SMDS_MeshNode* aNodes1 [3];
769 SMDS_ElemIteratorPtr it;
770 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
771 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
772 if ( aNodes1[ i ] == theNode1 )
773 iA1 = i; // node A in tr1
774 else if ( aNodes1[ i ] != theNode2 )
778 const SMDS_MeshNode* aNodes2 [3];
779 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
780 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
781 if ( aNodes2[ i ] == theNode2 )
782 iB2 = i; // node B in tr2
783 else if ( aNodes2[ i ] != theNode1 )
787 // nodes 1 and 2 should not be the same
788 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
792 aNodes1[ iA1 ] = aNodes2[ i2 ];
794 aNodes2[ iB2 ] = aNodes1[ i1 ];
796 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
797 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
802 // check case of quadratic faces
803 return InverseDiag(tr1,tr2);
806 //=======================================================================
807 //function : getQuadrangleNodes
808 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
809 // fusion of triangles tr1 and tr2 having shared link on
810 // theNode1 and theNode2
811 //=======================================================================
813 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
814 const SMDS_MeshNode * theNode1,
815 const SMDS_MeshNode * theNode2,
816 const SMDS_MeshElement * tr1,
817 const SMDS_MeshElement * tr2 )
819 if( tr1->NbNodes() != tr2->NbNodes() )
821 // find the 4-th node to insert into tr1
822 const SMDS_MeshNode* n4 = 0;
823 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
825 while ( !n4 && i<3 ) {
826 const SMDS_MeshNode * n = cast2Node( it->next() );
828 bool isDiag = ( n == theNode1 || n == theNode2 );
832 // Make an array of nodes to be in a quadrangle
833 int iNode = 0, iFirstDiag = -1;
834 it = tr1->nodesIterator();
837 const SMDS_MeshNode * n = cast2Node( it->next() );
839 bool isDiag = ( n == theNode1 || n == theNode2 );
841 if ( iFirstDiag < 0 )
843 else if ( iNode - iFirstDiag == 1 )
844 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
846 else if ( n == n4 ) {
847 return false; // tr1 and tr2 should not have all the same nodes
849 theQuadNodes[ iNode++ ] = n;
851 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
852 theQuadNodes[ iNode ] = n4;
857 //=======================================================================
858 //function : DeleteDiag
859 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
860 // with a quadrangle built on the same 4 nodes.
861 // Return false if proper faces not found
862 //=======================================================================
864 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
865 const SMDS_MeshNode * theNode2)
867 myLastCreatedElems.Clear();
868 myLastCreatedNodes.Clear();
870 MESSAGE( "::DeleteDiag()" );
872 const SMDS_MeshElement *tr1, *tr2;
873 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
876 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
877 if (!F1) return false;
878 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
879 if (!F2) return false;
880 SMESHDS_Mesh * aMesh = GetMeshDS();
882 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
883 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
885 const SMDS_MeshNode* aNodes [ 4 ];
886 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
889 const SMDS_MeshElement* newElem = 0;
890 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
891 myLastCreatedElems.Append(newElem);
892 AddToSameGroups( newElem, tr1, aMesh );
893 int aShapeId = tr1->getshapeId();
896 aMesh->SetMeshElementOnShape( newElem, aShapeId );
898 aMesh->RemoveElement( tr1 );
899 aMesh->RemoveElement( tr2 );
904 // check case of quadratic faces
905 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
907 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
911 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
912 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
920 const SMDS_MeshNode* N1 [6];
921 const SMDS_MeshNode* N2 [6];
922 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
924 // now we receive following N1 and N2 (using numeration as above image)
925 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
926 // i.e. first nodes from both arrays determ new diagonal
928 const SMDS_MeshNode* aNodes[8];
938 const SMDS_MeshElement* newElem = 0;
939 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
940 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
941 myLastCreatedElems.Append(newElem);
942 AddToSameGroups( newElem, tr1, aMesh );
943 int aShapeId = tr1->getshapeId();
946 aMesh->SetMeshElementOnShape( newElem, aShapeId );
948 aMesh->RemoveElement( tr1 );
949 aMesh->RemoveElement( tr2 );
951 // remove middle node (9)
952 GetMeshDS()->RemoveNode( N1[4] );
957 //=======================================================================
958 //function : Reorient
959 //purpose : Reverse theElement orientation
960 //=======================================================================
962 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
965 myLastCreatedElems.Clear();
966 myLastCreatedNodes.Clear();
970 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
971 if ( !it || !it->more() )
974 switch ( theElem->GetType() ) {
978 if(!theElem->IsQuadratic()) {
979 int i = theElem->NbNodes();
980 vector<const SMDS_MeshNode*> aNodes( i );
982 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
983 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
986 // quadratic elements
987 if(theElem->GetType()==SMDSAbs_Edge) {
988 vector<const SMDS_MeshNode*> aNodes(3);
989 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
990 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
991 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
992 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
995 int nbn = theElem->NbNodes();
996 vector<const SMDS_MeshNode*> aNodes(nbn);
997 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
999 for(; i<nbn/2; i++) {
1000 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1002 for(i=0; i<nbn/2; i++) {
1003 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1005 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1009 case SMDSAbs_Volume: {
1010 if (theElem->IsPoly()) {
1011 // TODO reorient vtk polyhedron
1012 MESSAGE("reorient vtk polyhedron ?");
1013 const SMDS_VtkVolume* aPolyedre =
1014 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1016 MESSAGE("Warning: bad volumic element");
1020 int nbFaces = aPolyedre->NbFaces();
1021 vector<const SMDS_MeshNode *> poly_nodes;
1022 vector<int> quantities (nbFaces);
1024 // reverse each face of the polyedre
1025 for (int iface = 1; iface <= nbFaces; iface++) {
1026 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1027 quantities[iface - 1] = nbFaceNodes;
1029 for (inode = nbFaceNodes; inode >= 1; inode--) {
1030 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1031 poly_nodes.push_back(curNode);
1035 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1039 SMDS_VolumeTool vTool;
1040 if ( !vTool.Set( theElem ))
1043 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1044 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1053 //=======================================================================
1054 //function : getBadRate
1056 //=======================================================================
1058 static double getBadRate (const SMDS_MeshElement* theElem,
1059 SMESH::Controls::NumericalFunctorPtr& theCrit)
1061 SMESH::Controls::TSequenceOfXYZ P;
1062 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1064 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1065 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1068 //=======================================================================
1069 //function : QuadToTri
1070 //purpose : Cut quadrangles into triangles.
1071 // theCrit is used to select a diagonal to cut
1072 //=======================================================================
1074 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1075 SMESH::Controls::NumericalFunctorPtr theCrit)
1077 myLastCreatedElems.Clear();
1078 myLastCreatedNodes.Clear();
1080 MESSAGE( "::QuadToTri()" );
1082 if ( !theCrit.get() )
1085 SMESHDS_Mesh * aMesh = GetMeshDS();
1087 Handle(Geom_Surface) surface;
1088 SMESH_MesherHelper helper( *GetMesh() );
1090 TIDSortedElemSet::iterator itElem;
1091 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1092 const SMDS_MeshElement* elem = *itElem;
1093 if ( !elem || elem->GetType() != SMDSAbs_Face )
1095 if ( elem->NbCornerNodes() != 4 )
1098 // retrieve element nodes
1099 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1101 // compare two sets of possible triangles
1102 double aBadRate1, aBadRate2; // to what extent a set is bad
1103 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1104 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1105 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1107 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1108 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1109 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1111 int aShapeId = FindShape( elem );
1112 const SMDS_MeshElement* newElem1 = 0;
1113 const SMDS_MeshElement* newElem2 = 0;
1115 if( !elem->IsQuadratic() ) {
1117 // split liner quadrangle
1119 if ( aBadRate1 <= aBadRate2 ) {
1120 // tr1 + tr2 is better
1121 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1122 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1125 // tr3 + tr4 is better
1126 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1127 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1132 // split quadratic quadrangle
1134 // get surface elem is on
1135 if ( aShapeId != helper.GetSubShapeID() ) {
1139 shape = aMesh->IndexToShape( aShapeId );
1140 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1141 TopoDS_Face face = TopoDS::Face( shape );
1142 surface = BRep_Tool::Surface( face );
1143 if ( !surface.IsNull() )
1144 helper.SetSubShape( shape );
1147 // find middle point for (0,1,2,3)
1148 // and create a node in this point;
1149 const SMDS_MeshNode* newN = 0;
1150 if ( aNodes.size() == 9 )
1152 // SMDSEntity_BiQuad_Quadrangle
1153 newN = aNodes.back();
1158 if ( surface.IsNull() )
1160 for ( int i = 0; i < 4; i++ )
1161 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1166 const SMDS_MeshNode* inFaceNode = 0;
1167 if ( helper.GetNodeUVneedInFaceNode() )
1168 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1169 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1170 inFaceNode = aNodes[ i ];
1172 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1174 for ( int i = 0; i < 4; i++ )
1175 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1177 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1179 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1180 myLastCreatedNodes.Append(newN);
1182 // create a new element
1183 if ( aBadRate1 <= aBadRate2 ) {
1184 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1185 aNodes[6], aNodes[7], newN );
1186 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1187 newN, aNodes[4], aNodes[5] );
1190 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1191 aNodes[7], aNodes[4], newN );
1192 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1193 newN, aNodes[5], aNodes[6] );
1197 // care of a new element
1199 myLastCreatedElems.Append(newElem1);
1200 myLastCreatedElems.Append(newElem2);
1201 AddToSameGroups( newElem1, elem, aMesh );
1202 AddToSameGroups( newElem2, elem, aMesh );
1204 // put a new triangle on the same shape
1207 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1208 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1210 aMesh->RemoveElement( elem );
1215 //=======================================================================
1216 //function : BestSplit
1217 //purpose : Find better diagonal for cutting.
1218 //=======================================================================
1220 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1221 SMESH::Controls::NumericalFunctorPtr theCrit)
1223 myLastCreatedElems.Clear();
1224 myLastCreatedNodes.Clear();
1229 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1232 if( theQuad->NbNodes()==4 ||
1233 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1235 // retrieve element nodes
1236 const SMDS_MeshNode* aNodes [4];
1237 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1239 //while (itN->more())
1241 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1243 // compare two sets of possible triangles
1244 double aBadRate1, aBadRate2; // to what extent a set is bad
1245 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1246 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1247 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1249 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1250 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1251 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1253 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1254 return 1; // diagonal 1-3
1256 return 2; // diagonal 2-4
1263 // Methods of splitting volumes into tetra
1265 const int theHexTo5_1[5*4+1] =
1267 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1269 const int theHexTo5_2[5*4+1] =
1271 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1273 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1275 const int theHexTo6_1[6*4+1] =
1277 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
1279 const int theHexTo6_2[6*4+1] =
1281 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
1283 const int theHexTo6_3[6*4+1] =
1285 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
1287 const int theHexTo6_4[6*4+1] =
1289 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
1291 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1293 const int thePyraTo2_1[2*4+1] =
1295 0, 1, 2, 4, 0, 2, 3, 4, -1
1297 const int thePyraTo2_2[2*4+1] =
1299 1, 2, 3, 4, 1, 3, 0, 4, -1
1301 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1303 const int thePentaTo3_1[3*4+1] =
1305 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1307 const int thePentaTo3_2[3*4+1] =
1309 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1311 const int thePentaTo3_3[3*4+1] =
1313 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1315 const int thePentaTo3_4[3*4+1] =
1317 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1319 const int thePentaTo3_5[3*4+1] =
1321 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1323 const int thePentaTo3_6[3*4+1] =
1325 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1327 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1328 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1330 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1333 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1334 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1335 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1340 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1341 bool _baryNode; //!< additional node is to be created at cell barycenter
1342 bool _ownConn; //!< to delete _connectivity in destructor
1343 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1345 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1346 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1347 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1348 bool hasFacet( const TTriangleFacet& facet ) const
1350 const int* tetConn = _connectivity;
1351 for ( ; tetConn[0] >= 0; tetConn += 4 )
1352 if (( facet.contains( tetConn[0] ) +
1353 facet.contains( tetConn[1] ) +
1354 facet.contains( tetConn[2] ) +
1355 facet.contains( tetConn[3] )) == 3 )
1361 //=======================================================================
1363 * \brief return TSplitMethod for the given element
1365 //=======================================================================
1367 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1369 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1371 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1372 // an edge and a face barycenter; tertaherdons are based on triangles and
1373 // a volume barycenter
1374 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1376 // Find out how adjacent volumes are split
1378 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1379 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1380 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1382 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1383 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1384 if ( nbNodes < 4 ) continue;
1386 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1387 const int* nInd = vol.GetFaceNodesIndices( iF );
1390 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1391 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1392 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1393 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1397 int iCom = 0; // common node of triangle faces to split into
1398 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1400 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1401 nInd[ iQ * ( (iCom+1)%nbNodes )],
1402 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1403 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1404 nInd[ iQ * ( (iCom+2)%nbNodes )],
1405 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1406 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1408 triaSplits.push_back( t012 );
1409 triaSplits.push_back( t023 );
1414 if ( !triaSplits.empty() )
1415 hasAdjacentSplits = true;
1418 // Among variants of split method select one compliant with adjacent volumes
1420 TSplitMethod method;
1421 if ( !vol.Element()->IsPoly() && !is24TetMode )
1423 int nbVariants = 2, nbTet = 0;
1424 const int** connVariants = 0;
1425 switch ( vol.Element()->GetEntityType() )
1427 case SMDSEntity_Hexa:
1428 case SMDSEntity_Quad_Hexa:
1429 case SMDSEntity_TriQuad_Hexa:
1430 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1431 connVariants = theHexTo5, nbTet = 5;
1433 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1435 case SMDSEntity_Pyramid:
1436 case SMDSEntity_Quad_Pyramid:
1437 connVariants = thePyraTo2; nbTet = 2;
1439 case SMDSEntity_Penta:
1440 case SMDSEntity_Quad_Penta:
1441 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1446 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1448 // check method compliancy with adjacent tetras,
1449 // all found splits must be among facets of tetras described by this method
1450 method = TSplitMethod( nbTet, connVariants[variant] );
1451 if ( hasAdjacentSplits && method._nbTetra > 0 )
1453 bool facetCreated = true;
1454 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1456 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1457 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1458 facetCreated = method.hasFacet( *facet );
1460 if ( !facetCreated )
1461 method = TSplitMethod(0); // incompatible method
1465 if ( method._nbTetra < 1 )
1467 // No standard method is applicable, use a generic solution:
1468 // each facet of a volume is split into triangles and
1469 // each of triangles and a volume barycenter form a tetrahedron.
1471 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1473 int* connectivity = new int[ maxTetConnSize + 1 ];
1474 method._connectivity = connectivity;
1475 method._ownConn = true;
1476 method._baryNode = !isHex27; // to create central node or not
1479 int baryCenInd = vol.NbNodes() - int( isHex27 );
1480 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1482 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1483 const int* nInd = vol.GetFaceNodesIndices( iF );
1484 // find common node of triangle facets of tetra to create
1485 int iCommon = 0; // index in linear numeration
1486 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1487 if ( !triaSplits.empty() )
1490 const TTriangleFacet* facet = &triaSplits.front();
1491 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1492 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1493 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1496 else if ( nbNodes > 3 && !is24TetMode )
1498 // find the best method of splitting into triangles by aspect ratio
1499 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1500 map< double, int > badness2iCommon;
1501 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1502 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1503 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1506 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1508 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1509 nodes[ iQ*((iLast-1)%nbNodes)],
1510 nodes[ iQ*((iLast )%nbNodes)]);
1511 badness += getBadRate( &tria, aspectRatio );
1513 badness2iCommon.insert( make_pair( badness, iCommon ));
1515 // use iCommon with lowest badness
1516 iCommon = badness2iCommon.begin()->second;
1518 if ( iCommon >= nbNodes )
1519 iCommon = 0; // something wrong
1521 // fill connectivity of tetrahedra based on a current face
1522 int nbTet = nbNodes - 2;
1523 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1528 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1529 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1533 method._faceBaryNode[ iF ] = 0;
1534 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1537 for ( int i = 0; i < nbTet; ++i )
1539 int i1 = i, i2 = (i+1) % nbNodes;
1540 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1541 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1542 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1543 connectivity[ connSize++ ] = faceBaryCenInd;
1544 connectivity[ connSize++ ] = baryCenInd;
1549 for ( int i = 0; i < nbTet; ++i )
1551 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1552 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1553 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1554 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1555 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1556 connectivity[ connSize++ ] = baryCenInd;
1559 method._nbTetra += nbTet;
1561 } // loop on volume faces
1563 connectivity[ connSize++ ] = -1;
1565 } // end of generic solution
1569 //================================================================================
1571 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1573 //================================================================================
1575 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1577 // find the tetrahedron including the three nodes of facet
1578 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1579 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1580 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1581 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1582 while ( volIt1->more() )
1584 const SMDS_MeshElement* v = volIt1->next();
1585 SMDSAbs_EntityType type = v->GetEntityType();
1586 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1588 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1589 continue; // medium node not allowed
1590 const int ind2 = v->GetNodeIndex( n2 );
1591 if ( ind2 < 0 || 3 < ind2 )
1593 const int ind3 = v->GetNodeIndex( n3 );
1594 if ( ind3 < 0 || 3 < ind3 )
1601 //=======================================================================
1603 * \brief A key of a face of volume
1605 //=======================================================================
1607 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1609 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1611 TIDSortedNodeSet sortedNodes;
1612 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1613 int nbNodes = vol.NbFaceNodes( iF );
1614 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1615 for ( int i = 0; i < nbNodes; i += iQ )
1616 sortedNodes.insert( fNodes[i] );
1617 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1618 first.first = (*(n++))->GetID();
1619 first.second = (*(n++))->GetID();
1620 second.first = (*(n++))->GetID();
1621 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1626 //=======================================================================
1627 //function : SplitVolumesIntoTetra
1628 //purpose : Split volume elements into tetrahedra.
1629 //=======================================================================
1631 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1632 const int theMethodFlags)
1634 // std-like iterator on coordinates of nodes of mesh element
1635 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1636 NXyzIterator xyzEnd;
1638 SMDS_VolumeTool volTool;
1639 SMESH_MesherHelper helper( *GetMesh());
1641 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1642 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1644 SMESH_SequenceOfElemPtr newNodes, newElems;
1646 // map face of volume to it's baricenrtic node
1647 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1650 TIDSortedElemSet::const_iterator elem = theElems.begin();
1651 for ( ; elem != theElems.end(); ++elem )
1653 if ( (*elem)->GetType() != SMDSAbs_Volume )
1655 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1656 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1659 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1661 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1662 if ( splitMethod._nbTetra < 1 ) continue;
1664 // find submesh to add new tetras to
1665 if ( !subMesh || !subMesh->Contains( *elem ))
1667 int shapeID = FindShape( *elem );
1668 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1669 subMesh = GetMeshDS()->MeshElements( shapeID );
1672 if ( (*elem)->IsQuadratic() )
1675 // add quadratic links to the helper
1676 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1678 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1679 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1680 for ( int iN = 0; iN < nbN; iN += iQ )
1681 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1683 helper.SetIsQuadratic( true );
1688 helper.SetIsQuadratic( false );
1690 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1691 helper.SetElementsOnShape( true );
1692 if ( splitMethod._baryNode )
1694 // make a node at barycenter
1695 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1696 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1697 nodes.push_back( gcNode );
1698 newNodes.Append( gcNode );
1700 if ( !splitMethod._faceBaryNode.empty() )
1702 // make or find baricentric nodes of faces
1703 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1704 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1706 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1707 volFace2BaryNode.insert
1708 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1711 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1712 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1714 nodes.push_back( iF_n->second = f_n->second );
1719 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1720 const int* tetConn = splitMethod._connectivity;
1721 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1722 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1723 nodes[ tetConn[1] ],
1724 nodes[ tetConn[2] ],
1725 nodes[ tetConn[3] ]));
1727 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1729 // Split faces on sides of the split volume
1731 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1732 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1734 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1735 if ( nbNodes < 4 ) continue;
1737 // find an existing face
1738 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1739 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1740 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1741 /*noMedium=*/false))
1744 helper.SetElementsOnShape( false );
1745 vector< const SMDS_MeshElement* > triangles;
1747 // find submesh to add new triangles in
1748 if ( !fSubMesh || !fSubMesh->Contains( face ))
1750 int shapeID = FindShape( face );
1751 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1753 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1754 if ( iF_n != splitMethod._faceBaryNode.end() )
1756 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1758 const SMDS_MeshNode* n1 = fNodes[iN];
1759 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1760 const SMDS_MeshNode *n3 = iF_n->second;
1761 if ( !volTool.IsFaceExternal( iF ))
1763 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1765 if ( fSubMesh && n3->getshapeId() < 1 )
1766 fSubMesh->AddNode( n3 );
1771 // among possible triangles create ones discribed by split method
1772 const int* nInd = volTool.GetFaceNodesIndices( iF );
1773 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1774 int iCom = 0; // common node of triangle faces to split into
1775 list< TTriangleFacet > facets;
1776 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1778 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1779 nInd[ iQ * ( (iCom+1)%nbNodes )],
1780 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1781 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1782 nInd[ iQ * ( (iCom+2)%nbNodes )],
1783 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1784 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1786 facets.push_back( t012 );
1787 facets.push_back( t023 );
1788 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1789 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1790 nInd[ iQ * ((iLast-1)%nbNodes )],
1791 nInd[ iQ * ((iLast )%nbNodes )]));
1795 list< TTriangleFacet >::iterator facet = facets.begin();
1796 for ( ; facet != facets.end(); ++facet )
1798 if ( !volTool.IsFaceExternal( iF ))
1799 swap( facet->_n2, facet->_n3 );
1800 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1801 volNodes[ facet->_n2 ],
1802 volNodes[ facet->_n3 ]));
1805 for ( int i = 0; i < triangles.size(); ++i )
1807 if ( !triangles[i] ) continue;
1809 fSubMesh->AddElement( triangles[i]);
1810 newElems.Append( triangles[i] );
1812 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1813 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1816 } // loop on volume faces to split them into triangles
1818 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1820 if ( geomType == SMDSEntity_TriQuad_Hexa )
1822 // remove medium nodes that could become free
1823 for ( int i = 20; i < volTool.NbNodes(); ++i )
1824 if ( volNodes[i]->NbInverseElements() == 0 )
1825 GetMeshDS()->RemoveNode( volNodes[i] );
1827 } // loop on volumes to split
1829 myLastCreatedNodes = newNodes;
1830 myLastCreatedElems = newElems;
1833 //=======================================================================
1834 //function : AddToSameGroups
1835 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1836 //=======================================================================
1838 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1839 const SMDS_MeshElement* elemInGroups,
1840 SMESHDS_Mesh * aMesh)
1842 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1843 if (!groups.empty()) {
1844 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1845 for ( ; grIt != groups.end(); grIt++ ) {
1846 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1847 if ( group && group->Contains( elemInGroups ))
1848 group->SMDSGroup().Add( elemToAdd );
1854 //=======================================================================
1855 //function : RemoveElemFromGroups
1856 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1857 //=======================================================================
1858 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1859 SMESHDS_Mesh * aMesh)
1861 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1862 if (!groups.empty())
1864 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1865 for (; GrIt != groups.end(); GrIt++)
1867 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1868 if (!grp || grp->IsEmpty()) continue;
1869 grp->SMDSGroup().Remove(removeelem);
1874 //================================================================================
1876 * \brief Replace elemToRm by elemToAdd in the all groups
1878 //================================================================================
1880 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1881 const SMDS_MeshElement* elemToAdd,
1882 SMESHDS_Mesh * aMesh)
1884 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1885 if (!groups.empty()) {
1886 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1887 for ( ; grIt != groups.end(); grIt++ ) {
1888 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1889 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1890 group->SMDSGroup().Add( elemToAdd );
1895 //================================================================================
1897 * \brief Replace elemToRm by elemToAdd in the all groups
1899 //================================================================================
1901 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1902 const vector<const SMDS_MeshElement*>& elemToAdd,
1903 SMESHDS_Mesh * aMesh)
1905 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1906 if (!groups.empty())
1908 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1909 for ( ; grIt != groups.end(); grIt++ ) {
1910 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1911 if ( group && group->SMDSGroup().Remove( elemToRm ) )
1912 for ( int i = 0; i < elemToAdd.size(); ++i )
1913 group->SMDSGroup().Add( elemToAdd[ i ] );
1918 //=======================================================================
1919 //function : QuadToTri
1920 //purpose : Cut quadrangles into triangles.
1921 // theCrit is used to select a diagonal to cut
1922 //=======================================================================
1924 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1925 const bool the13Diag)
1927 myLastCreatedElems.Clear();
1928 myLastCreatedNodes.Clear();
1930 MESSAGE( "::QuadToTri()" );
1932 SMESHDS_Mesh * aMesh = GetMeshDS();
1934 Handle(Geom_Surface) surface;
1935 SMESH_MesherHelper helper( *GetMesh() );
1937 TIDSortedElemSet::iterator itElem;
1938 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1939 const SMDS_MeshElement* elem = *itElem;
1940 if ( !elem || elem->GetType() != SMDSAbs_Face )
1942 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1943 if(!isquad) continue;
1945 if(elem->NbNodes()==4) {
1946 // retrieve element nodes
1947 const SMDS_MeshNode* aNodes [4];
1948 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1950 while ( itN->more() )
1951 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1953 int aShapeId = FindShape( elem );
1954 const SMDS_MeshElement* newElem1 = 0;
1955 const SMDS_MeshElement* newElem2 = 0;
1957 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1958 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1961 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1962 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1964 myLastCreatedElems.Append(newElem1);
1965 myLastCreatedElems.Append(newElem2);
1966 // put a new triangle on the same shape and add to the same groups
1969 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1970 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1972 AddToSameGroups( newElem1, elem, aMesh );
1973 AddToSameGroups( newElem2, elem, aMesh );
1974 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
1975 aMesh->RemoveElement( elem );
1978 // Quadratic quadrangle
1980 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1982 // get surface elem is on
1983 int aShapeId = FindShape( elem );
1984 if ( aShapeId != helper.GetSubShapeID() ) {
1988 shape = aMesh->IndexToShape( aShapeId );
1989 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1990 TopoDS_Face face = TopoDS::Face( shape );
1991 surface = BRep_Tool::Surface( face );
1992 if ( !surface.IsNull() )
1993 helper.SetSubShape( shape );
1997 const SMDS_MeshNode* aNodes [8];
1998 const SMDS_MeshNode* inFaceNode = 0;
1999 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2001 while ( itN->more() ) {
2002 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2003 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2004 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2006 inFaceNode = aNodes[ i-1 ];
2010 // find middle point for (0,1,2,3)
2011 // and create a node in this point;
2013 if ( surface.IsNull() ) {
2015 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2019 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2022 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2024 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2026 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2027 myLastCreatedNodes.Append(newN);
2029 // create a new element
2030 const SMDS_MeshElement* newElem1 = 0;
2031 const SMDS_MeshElement* newElem2 = 0;
2033 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2034 aNodes[6], aNodes[7], newN );
2035 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2036 newN, aNodes[4], aNodes[5] );
2039 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2040 aNodes[7], aNodes[4], newN );
2041 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2042 newN, aNodes[5], aNodes[6] );
2044 myLastCreatedElems.Append(newElem1);
2045 myLastCreatedElems.Append(newElem2);
2046 // put a new triangle on the same shape and add to the same groups
2049 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2050 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2052 AddToSameGroups( newElem1, elem, aMesh );
2053 AddToSameGroups( newElem2, elem, aMesh );
2054 aMesh->RemoveElement( elem );
2061 //=======================================================================
2062 //function : getAngle
2064 //=======================================================================
2066 double getAngle(const SMDS_MeshElement * tr1,
2067 const SMDS_MeshElement * tr2,
2068 const SMDS_MeshNode * n1,
2069 const SMDS_MeshNode * n2)
2071 double angle = 2. * M_PI; // bad angle
2074 SMESH::Controls::TSequenceOfXYZ P1, P2;
2075 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2076 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2079 if(!tr1->IsQuadratic())
2080 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2082 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2083 if ( N1.SquareMagnitude() <= gp::Resolution() )
2085 if(!tr2->IsQuadratic())
2086 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2088 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2089 if ( N2.SquareMagnitude() <= gp::Resolution() )
2092 // find the first diagonal node n1 in the triangles:
2093 // take in account a diagonal link orientation
2094 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2095 for ( int t = 0; t < 2; t++ ) {
2096 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2097 int i = 0, iDiag = -1;
2098 while ( it->more()) {
2099 const SMDS_MeshElement *n = it->next();
2100 if ( n == n1 || n == n2 ) {
2104 if ( i - iDiag == 1 )
2105 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2114 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2117 angle = N1.Angle( N2 );
2122 // =================================================
2123 // class generating a unique ID for a pair of nodes
2124 // and able to return nodes by that ID
2125 // =================================================
2129 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2130 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2133 long GetLinkID (const SMDS_MeshNode * n1,
2134 const SMDS_MeshNode * n2) const
2136 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2139 bool GetNodes (const long theLinkID,
2140 const SMDS_MeshNode* & theNode1,
2141 const SMDS_MeshNode* & theNode2) const
2143 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2144 if ( !theNode1 ) return false;
2145 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2146 if ( !theNode2 ) return false;
2152 const SMESHDS_Mesh* myMesh;
2157 //=======================================================================
2158 //function : TriToQuad
2159 //purpose : Fuse neighbour triangles into quadrangles.
2160 // theCrit is used to select a neighbour to fuse with.
2161 // theMaxAngle is a max angle between element normals at which
2162 // fusion is still performed.
2163 //=======================================================================
2165 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2166 SMESH::Controls::NumericalFunctorPtr theCrit,
2167 const double theMaxAngle)
2169 myLastCreatedElems.Clear();
2170 myLastCreatedNodes.Clear();
2172 MESSAGE( "::TriToQuad()" );
2174 if ( !theCrit.get() )
2177 SMESHDS_Mesh * aMesh = GetMeshDS();
2179 // Prepare data for algo: build
2180 // 1. map of elements with their linkIDs
2181 // 2. map of linkIDs with their elements
2183 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2184 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2185 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2186 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2188 TIDSortedElemSet::iterator itElem;
2189 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2190 const SMDS_MeshElement* elem = *itElem;
2191 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2192 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2193 if(!IsTria) continue;
2195 // retrieve element nodes
2196 const SMDS_MeshNode* aNodes [4];
2197 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2200 aNodes[ i++ ] = cast2Node( itN->next() );
2201 aNodes[ 3 ] = aNodes[ 0 ];
2204 for ( i = 0; i < 3; i++ ) {
2205 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2206 // check if elements sharing a link can be fused
2207 itLE = mapLi_listEl.find( link );
2208 if ( itLE != mapLi_listEl.end() ) {
2209 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2211 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2212 //if ( FindShape( elem ) != FindShape( elem2 ))
2213 // continue; // do not fuse triangles laying on different shapes
2214 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2215 continue; // avoid making badly shaped quads
2216 (*itLE).second.push_back( elem );
2219 mapLi_listEl[ link ].push_back( elem );
2221 mapEl_setLi [ elem ].insert( link );
2224 // Clean the maps from the links shared by a sole element, ie
2225 // links to which only one element is bound in mapLi_listEl
2227 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2228 int nbElems = (*itLE).second.size();
2229 if ( nbElems < 2 ) {
2230 const SMDS_MeshElement* elem = (*itLE).second.front();
2231 SMESH_TLink link = (*itLE).first;
2232 mapEl_setLi[ elem ].erase( link );
2233 if ( mapEl_setLi[ elem ].empty() )
2234 mapEl_setLi.erase( elem );
2238 // Algo: fuse triangles into quadrangles
2240 while ( ! mapEl_setLi.empty() ) {
2241 // Look for the start element:
2242 // the element having the least nb of shared links
2243 const SMDS_MeshElement* startElem = 0;
2245 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2246 int nbLinks = (*itEL).second.size();
2247 if ( nbLinks < minNbLinks ) {
2248 startElem = (*itEL).first;
2249 minNbLinks = nbLinks;
2250 if ( minNbLinks == 1 )
2255 // search elements to fuse starting from startElem or links of elements
2256 // fused earlyer - startLinks
2257 list< SMESH_TLink > startLinks;
2258 while ( startElem || !startLinks.empty() ) {
2259 while ( !startElem && !startLinks.empty() ) {
2260 // Get an element to start, by a link
2261 SMESH_TLink linkId = startLinks.front();
2262 startLinks.pop_front();
2263 itLE = mapLi_listEl.find( linkId );
2264 if ( itLE != mapLi_listEl.end() ) {
2265 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2266 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2267 for ( ; itE != listElem.end() ; itE++ )
2268 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2270 mapLi_listEl.erase( itLE );
2275 // Get candidates to be fused
2276 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2277 const SMESH_TLink *link12, *link13;
2279 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2280 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2281 ASSERT( !setLi.empty() );
2282 set< SMESH_TLink >::iterator itLi;
2283 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2285 const SMESH_TLink & link = (*itLi);
2286 itLE = mapLi_listEl.find( link );
2287 if ( itLE == mapLi_listEl.end() )
2290 const SMDS_MeshElement* elem = (*itLE).second.front();
2292 elem = (*itLE).second.back();
2293 mapLi_listEl.erase( itLE );
2294 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2305 // add other links of elem to list of links to re-start from
2306 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2307 set< SMESH_TLink >::iterator it;
2308 for ( it = links.begin(); it != links.end(); it++ ) {
2309 const SMESH_TLink& link2 = (*it);
2310 if ( link2 != link )
2311 startLinks.push_back( link2 );
2315 // Get nodes of possible quadrangles
2316 const SMDS_MeshNode *n12 [4], *n13 [4];
2317 bool Ok12 = false, Ok13 = false;
2318 const SMDS_MeshNode *linkNode1, *linkNode2;
2320 linkNode1 = link12->first;
2321 linkNode2 = link12->second;
2322 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2326 linkNode1 = link13->first;
2327 linkNode2 = link13->second;
2328 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2332 // Choose a pair to fuse
2333 if ( Ok12 && Ok13 ) {
2334 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2335 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2336 double aBadRate12 = getBadRate( &quad12, theCrit );
2337 double aBadRate13 = getBadRate( &quad13, theCrit );
2338 if ( aBadRate13 < aBadRate12 )
2345 // and remove fused elems and removed links from the maps
2346 mapEl_setLi.erase( tr1 );
2348 mapEl_setLi.erase( tr2 );
2349 mapLi_listEl.erase( *link12 );
2350 if(tr1->NbNodes()==3) {
2351 const SMDS_MeshElement* newElem = 0;
2352 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2353 myLastCreatedElems.Append(newElem);
2354 AddToSameGroups( newElem, tr1, aMesh );
2355 int aShapeId = tr1->getshapeId();
2358 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2360 aMesh->RemoveElement( tr1 );
2361 aMesh->RemoveElement( tr2 );
2364 const SMDS_MeshNode* N1 [6];
2365 const SMDS_MeshNode* N2 [6];
2366 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2367 // now we receive following N1 and N2 (using numeration as above image)
2368 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2369 // i.e. first nodes from both arrays determ new diagonal
2370 const SMDS_MeshNode* aNodes[8];
2379 const SMDS_MeshElement* newElem = 0;
2380 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2381 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2382 myLastCreatedElems.Append(newElem);
2383 AddToSameGroups( newElem, tr1, aMesh );
2384 int aShapeId = tr1->getshapeId();
2387 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2389 aMesh->RemoveElement( tr1 );
2390 aMesh->RemoveElement( tr2 );
2391 // remove middle node (9)
2392 GetMeshDS()->RemoveNode( N1[4] );
2396 mapEl_setLi.erase( tr3 );
2397 mapLi_listEl.erase( *link13 );
2398 if(tr1->NbNodes()==3) {
2399 const SMDS_MeshElement* newElem = 0;
2400 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2401 myLastCreatedElems.Append(newElem);
2402 AddToSameGroups( newElem, tr1, aMesh );
2403 int aShapeId = tr1->getshapeId();
2406 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2408 aMesh->RemoveElement( tr1 );
2409 aMesh->RemoveElement( tr3 );
2412 const SMDS_MeshNode* N1 [6];
2413 const SMDS_MeshNode* N2 [6];
2414 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2415 // now we receive following N1 and N2 (using numeration as above image)
2416 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2417 // i.e. first nodes from both arrays determ new diagonal
2418 const SMDS_MeshNode* aNodes[8];
2427 const SMDS_MeshElement* newElem = 0;
2428 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2429 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2430 myLastCreatedElems.Append(newElem);
2431 AddToSameGroups( newElem, tr1, aMesh );
2432 int aShapeId = tr1->getshapeId();
2435 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2437 aMesh->RemoveElement( tr1 );
2438 aMesh->RemoveElement( tr3 );
2439 // remove middle node (9)
2440 GetMeshDS()->RemoveNode( N1[4] );
2444 // Next element to fuse: the rejected one
2446 startElem = Ok12 ? tr3 : tr2;
2448 } // if ( startElem )
2449 } // while ( startElem || !startLinks.empty() )
2450 } // while ( ! mapEl_setLi.empty() )
2456 /*#define DUMPSO(txt) \
2457 // cout << txt << endl;
2458 //=============================================================================
2462 //=============================================================================
2463 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2467 int tmp = idNodes[ i1 ];
2468 idNodes[ i1 ] = idNodes[ i2 ];
2469 idNodes[ i2 ] = tmp;
2470 gp_Pnt Ptmp = P[ i1 ];
2473 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2476 //=======================================================================
2477 //function : SortQuadNodes
2478 //purpose : Set 4 nodes of a quadrangle face in a good order.
2479 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2481 //=======================================================================
2483 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2488 for ( i = 0; i < 4; i++ ) {
2489 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2491 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2494 gp_Vec V1(P[0], P[1]);
2495 gp_Vec V2(P[0], P[2]);
2496 gp_Vec V3(P[0], P[3]);
2498 gp_Vec Cross1 = V1 ^ V2;
2499 gp_Vec Cross2 = V2 ^ V3;
2502 if (Cross1.Dot(Cross2) < 0)
2507 if (Cross1.Dot(Cross2) < 0)
2511 swap ( i, i + 1, idNodes, P );
2513 // for ( int ii = 0; ii < 4; ii++ ) {
2514 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2515 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2521 //=======================================================================
2522 //function : SortHexaNodes
2523 //purpose : Set 8 nodes of a hexahedron in a good order.
2524 // Return success status
2525 //=======================================================================
2527 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2532 DUMPSO( "INPUT: ========================================");
2533 for ( i = 0; i < 8; i++ ) {
2534 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2535 if ( !n ) return false;
2536 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2537 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2539 DUMPSO( "========================================");
2542 set<int> faceNodes; // ids of bottom face nodes, to be found
2543 set<int> checkedId1; // ids of tried 2-nd nodes
2544 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2545 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2546 int iMin, iLoop1 = 0;
2548 // Loop to try the 2-nd nodes
2550 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2552 // Find not checked 2-nd node
2553 for ( i = 1; i < 8; i++ )
2554 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2555 int id1 = idNodes[i];
2556 swap ( 1, i, idNodes, P );
2557 checkedId1.insert ( id1 );
2561 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2562 // ie that all but meybe one (id3 which is on the same face) nodes
2563 // lay on the same side from the triangle plane.
2565 bool manyInPlane = false; // more than 4 nodes lay in plane
2567 while ( ++iLoop2 < 6 ) {
2569 // get 1-2-3 plane coeffs
2570 Standard_Real A, B, C, D;
2571 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2572 if ( N.SquareMagnitude() > gp::Resolution() )
2574 gp_Pln pln ( P[0], N );
2575 pln.Coefficients( A, B, C, D );
2577 // find the node (iMin) closest to pln
2578 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2580 for ( i = 3; i < 8; i++ ) {
2581 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2582 if ( fabs( dist[i] ) < minDist ) {
2583 minDist = fabs( dist[i] );
2586 if ( fabs( dist[i] ) <= tol )
2587 idInPln.insert( idNodes[i] );
2590 // there should not be more than 4 nodes in bottom plane
2591 if ( idInPln.size() > 1 )
2593 DUMPSO( "### idInPln.size() = " << idInPln.size());
2594 // idInPlane does not contain the first 3 nodes
2595 if ( manyInPlane || idInPln.size() == 5)
2596 return false; // all nodes in one plane
2599 // set the 1-st node to be not in plane
2600 for ( i = 3; i < 8; i++ ) {
2601 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2602 DUMPSO( "### Reset 0-th node");
2603 swap( 0, i, idNodes, P );
2608 // reset to re-check second nodes
2609 leastDist = DBL_MAX;
2613 break; // from iLoop2;
2616 // check that the other 4 nodes are on the same side
2617 bool sameSide = true;
2618 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2619 for ( i = 3; sameSide && i < 8; i++ ) {
2621 sameSide = ( isNeg == dist[i] <= 0.);
2624 // keep best solution
2625 if ( sameSide && minDist < leastDist ) {
2626 leastDist = minDist;
2628 faceNodes.insert( idNodes[ 1 ] );
2629 faceNodes.insert( idNodes[ 2 ] );
2630 faceNodes.insert( idNodes[ iMin ] );
2631 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2632 << " leastDist = " << leastDist);
2633 if ( leastDist <= DBL_MIN )
2638 // set next 3-d node to check
2639 int iNext = 2 + iLoop2;
2641 DUMPSO( "Try 2-nd");
2642 swap ( 2, iNext, idNodes, P );
2644 } // while ( iLoop2 < 6 )
2647 if ( faceNodes.empty() ) return false;
2649 // Put the faceNodes in proper places
2650 for ( i = 4; i < 8; i++ ) {
2651 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2652 // find a place to put
2654 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2656 DUMPSO( "Set faceNodes");
2657 swap ( iTo, i, idNodes, P );
2662 // Set nodes of the found bottom face in good order
2663 DUMPSO( " Found bottom face: ");
2664 i = SortQuadNodes( theMesh, idNodes );
2666 gp_Pnt Ptmp = P[ i ];
2671 // for ( int ii = 0; ii < 4; ii++ ) {
2672 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2673 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2676 // Gravity center of the top and bottom faces
2677 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2678 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2680 // Get direction from the bottom to the top face
2681 gp_Vec upDir ( aGCb, aGCt );
2682 Standard_Real upDirSize = upDir.Magnitude();
2683 if ( upDirSize <= gp::Resolution() ) return false;
2686 // Assure that the bottom face normal points up
2687 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2688 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2689 if ( Nb.Dot( upDir ) < 0 ) {
2690 DUMPSO( "Reverse bottom face");
2691 swap( 1, 3, idNodes, P );
2694 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2695 Standard_Real minDist = DBL_MAX;
2696 for ( i = 4; i < 8; i++ ) {
2697 // projection of P[i] to the plane defined by P[0] and upDir
2698 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2699 Standard_Real sqDist = P[0].SquareDistance( Pp );
2700 if ( sqDist < minDist ) {
2705 DUMPSO( "Set 4-th");
2706 swap ( 4, iMin, idNodes, P );
2708 // Set nodes of the top face in good order
2709 DUMPSO( "Sort top face");
2710 i = SortQuadNodes( theMesh, &idNodes[4] );
2713 gp_Pnt Ptmp = P[ i ];
2718 // Assure that direction of the top face normal is from the bottom face
2719 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2720 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2721 if ( Nt.Dot( upDir ) < 0 ) {
2722 DUMPSO( "Reverse top face");
2723 swap( 5, 7, idNodes, P );
2726 // DUMPSO( "OUTPUT: ========================================");
2727 // for ( i = 0; i < 8; i++ ) {
2728 // float *p = ugrid->GetPoint(idNodes[i]);
2729 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2735 //================================================================================
2737 * \brief Return nodes linked to the given one
2738 * \param theNode - the node
2739 * \param linkedNodes - the found nodes
2740 * \param type - the type of elements to check
2742 * Medium nodes are ignored
2744 //================================================================================
2746 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2747 TIDSortedElemSet & linkedNodes,
2748 SMDSAbs_ElementType type )
2750 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2751 while ( elemIt->more() )
2753 const SMDS_MeshElement* elem = elemIt->next();
2754 if(elem->GetType() == SMDSAbs_0DElement)
2757 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2758 if ( elem->GetType() == SMDSAbs_Volume )
2760 SMDS_VolumeTool vol( elem );
2761 while ( nodeIt->more() ) {
2762 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2763 if ( theNode != n && vol.IsLinked( theNode, n ))
2764 linkedNodes.insert( n );
2769 for ( int i = 0; nodeIt->more(); ++i ) {
2770 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2771 if ( n == theNode ) {
2772 int iBefore = i - 1;
2774 if ( elem->IsQuadratic() ) {
2775 int nb = elem->NbNodes() / 2;
2776 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2777 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2779 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2780 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2787 //=======================================================================
2788 //function : laplacianSmooth
2789 //purpose : pulls theNode toward the center of surrounding nodes directly
2790 // connected to that node along an element edge
2791 //=======================================================================
2793 void laplacianSmooth(const SMDS_MeshNode* theNode,
2794 const Handle(Geom_Surface)& theSurface,
2795 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2797 // find surrounding nodes
2799 TIDSortedElemSet nodeSet;
2800 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2802 // compute new coodrs
2804 double coord[] = { 0., 0., 0. };
2805 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2806 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2807 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2808 if ( theSurface.IsNull() ) { // smooth in 3D
2809 coord[0] += node->X();
2810 coord[1] += node->Y();
2811 coord[2] += node->Z();
2813 else { // smooth in 2D
2814 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2815 gp_XY* uv = theUVMap[ node ];
2816 coord[0] += uv->X();
2817 coord[1] += uv->Y();
2820 int nbNodes = nodeSet.size();
2823 coord[0] /= nbNodes;
2824 coord[1] /= nbNodes;
2826 if ( !theSurface.IsNull() ) {
2827 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2828 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2829 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2835 coord[2] /= nbNodes;
2839 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2842 //=======================================================================
2843 //function : centroidalSmooth
2844 //purpose : pulls theNode toward the element-area-weighted centroid of the
2845 // surrounding elements
2846 //=======================================================================
2848 void centroidalSmooth(const SMDS_MeshNode* theNode,
2849 const Handle(Geom_Surface)& theSurface,
2850 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2852 gp_XYZ aNewXYZ(0.,0.,0.);
2853 SMESH::Controls::Area anAreaFunc;
2854 double totalArea = 0.;
2859 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2860 while ( elemIt->more() )
2862 const SMDS_MeshElement* elem = elemIt->next();
2865 gp_XYZ elemCenter(0.,0.,0.);
2866 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2867 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2868 int nn = elem->NbNodes();
2869 if(elem->IsQuadratic()) nn = nn/2;
2871 //while ( itN->more() ) {
2873 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2875 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2876 aNodePoints.push_back( aP );
2877 if ( !theSurface.IsNull() ) { // smooth in 2D
2878 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2879 gp_XY* uv = theUVMap[ aNode ];
2880 aP.SetCoord( uv->X(), uv->Y(), 0. );
2884 double elemArea = anAreaFunc.GetValue( aNodePoints );
2885 totalArea += elemArea;
2887 aNewXYZ += elemCenter * elemArea;
2889 aNewXYZ /= totalArea;
2890 if ( !theSurface.IsNull() ) {
2891 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2892 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2897 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2900 //=======================================================================
2901 //function : getClosestUV
2902 //purpose : return UV of closest projection
2903 //=======================================================================
2905 static bool getClosestUV (Extrema_GenExtPS& projector,
2906 const gp_Pnt& point,
2909 projector.Perform( point );
2910 if ( projector.IsDone() ) {
2911 double u, v, minVal = DBL_MAX;
2912 for ( int i = projector.NbExt(); i > 0; i-- )
2913 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
2914 if ( projector.SquareDistance( i ) < minVal ) {
2915 minVal = projector.SquareDistance( i );
2917 if ( projector.Value( i ) < minVal ) {
2918 minVal = projector.Value( i );
2920 projector.Point( i ).Parameter( u, v );
2922 result.SetCoord( u, v );
2928 //=======================================================================
2930 //purpose : Smooth theElements during theNbIterations or until a worst
2931 // element has aspect ratio <= theTgtAspectRatio.
2932 // Aspect Ratio varies in range [1.0, inf].
2933 // If theElements is empty, the whole mesh is smoothed.
2934 // theFixedNodes contains additionally fixed nodes. Nodes built
2935 // on edges and boundary nodes are always fixed.
2936 //=======================================================================
2938 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
2939 set<const SMDS_MeshNode*> & theFixedNodes,
2940 const SmoothMethod theSmoothMethod,
2941 const int theNbIterations,
2942 double theTgtAspectRatio,
2945 myLastCreatedElems.Clear();
2946 myLastCreatedNodes.Clear();
2948 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2950 if ( theTgtAspectRatio < 1.0 )
2951 theTgtAspectRatio = 1.0;
2953 const double disttol = 1.e-16;
2955 SMESH::Controls::AspectRatio aQualityFunc;
2957 SMESHDS_Mesh* aMesh = GetMeshDS();
2959 if ( theElems.empty() ) {
2960 // add all faces to theElems
2961 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2962 while ( fIt->more() ) {
2963 const SMDS_MeshElement* face = fIt->next();
2964 theElems.insert( face );
2967 // get all face ids theElems are on
2968 set< int > faceIdSet;
2969 TIDSortedElemSet::iterator itElem;
2971 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2972 int fId = FindShape( *itElem );
2973 // check that corresponding submesh exists and a shape is face
2975 faceIdSet.find( fId ) == faceIdSet.end() &&
2976 aMesh->MeshElements( fId )) {
2977 TopoDS_Shape F = aMesh->IndexToShape( fId );
2978 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2979 faceIdSet.insert( fId );
2982 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2984 // ===============================================
2985 // smooth elements on each TopoDS_Face separately
2986 // ===============================================
2988 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2989 for ( ; fId != faceIdSet.rend(); ++fId ) {
2990 // get face surface and submesh
2991 Handle(Geom_Surface) surface;
2992 SMESHDS_SubMesh* faceSubMesh = 0;
2994 double fToler2 = 0, f,l;
2995 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2996 bool isUPeriodic = false, isVPeriodic = false;
2998 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
2999 surface = BRep_Tool::Surface( face );
3000 faceSubMesh = aMesh->MeshElements( *fId );
3001 fToler2 = BRep_Tool::Tolerance( face );
3002 fToler2 *= fToler2 * 10.;
3003 isUPeriodic = surface->IsUPeriodic();
3006 isVPeriodic = surface->IsVPeriodic();
3009 surface->Bounds( u1, u2, v1, v2 );
3011 // ---------------------------------------------------------
3012 // for elements on a face, find movable and fixed nodes and
3013 // compute UV for them
3014 // ---------------------------------------------------------
3015 bool checkBoundaryNodes = false;
3016 bool isQuadratic = false;
3017 set<const SMDS_MeshNode*> setMovableNodes;
3018 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3019 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3020 list< const SMDS_MeshElement* > elemsOnFace;
3022 Extrema_GenExtPS projector;
3023 GeomAdaptor_Surface surfAdaptor;
3024 if ( !surface.IsNull() ) {
3025 surfAdaptor.Load( surface );
3026 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3028 int nbElemOnFace = 0;
3029 itElem = theElems.begin();
3030 // loop on not yet smoothed elements: look for elems on a face
3031 while ( itElem != theElems.end() ) {
3032 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3033 break; // all elements found
3035 const SMDS_MeshElement* elem = *itElem;
3036 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3037 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3041 elemsOnFace.push_back( elem );
3042 theElems.erase( itElem++ );
3046 isQuadratic = elem->IsQuadratic();
3048 // get movable nodes of elem
3049 const SMDS_MeshNode* node;
3050 SMDS_TypeOfPosition posType;
3051 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3052 int nn = 0, nbn = elem->NbNodes();
3053 if(elem->IsQuadratic())
3055 while ( nn++ < nbn ) {
3056 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3057 const SMDS_PositionPtr& pos = node->GetPosition();
3058 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3059 if (posType != SMDS_TOP_EDGE &&
3060 posType != SMDS_TOP_VERTEX &&
3061 theFixedNodes.find( node ) == theFixedNodes.end())
3063 // check if all faces around the node are on faceSubMesh
3064 // because a node on edge may be bound to face
3065 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3067 if ( faceSubMesh ) {
3068 while ( eIt->more() && all ) {
3069 const SMDS_MeshElement* e = eIt->next();
3070 all = faceSubMesh->Contains( e );
3074 setMovableNodes.insert( node );
3076 checkBoundaryNodes = true;
3078 if ( posType == SMDS_TOP_3DSPACE )
3079 checkBoundaryNodes = true;
3082 if ( surface.IsNull() )
3085 // get nodes to check UV
3086 list< const SMDS_MeshNode* > uvCheckNodes;
3087 itN = elem->nodesIterator();
3088 nn = 0; nbn = elem->NbNodes();
3089 if(elem->IsQuadratic())
3091 while ( nn++ < nbn ) {
3092 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3093 if ( uvMap.find( node ) == uvMap.end() )
3094 uvCheckNodes.push_back( node );
3095 // add nodes of elems sharing node
3096 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3097 // while ( eIt->more() ) {
3098 // const SMDS_MeshElement* e = eIt->next();
3099 // if ( e != elem ) {
3100 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3101 // while ( nIt->more() ) {
3102 // const SMDS_MeshNode* n =
3103 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3104 // if ( uvMap.find( n ) == uvMap.end() )
3105 // uvCheckNodes.push_back( n );
3111 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3112 for ( ; n != uvCheckNodes.end(); ++n ) {
3115 const SMDS_PositionPtr& pos = node->GetPosition();
3116 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3118 switch ( posType ) {
3119 case SMDS_TOP_FACE: {
3120 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3121 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3124 case SMDS_TOP_EDGE: {
3125 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3126 Handle(Geom2d_Curve) pcurve;
3127 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3128 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3129 if ( !pcurve.IsNull() ) {
3130 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3131 uv = pcurve->Value( u ).XY();
3135 case SMDS_TOP_VERTEX: {
3136 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3137 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3138 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3143 // check existing UV
3144 bool project = true;
3145 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3146 double dist1 = DBL_MAX, dist2 = 0;
3147 if ( posType != SMDS_TOP_3DSPACE ) {
3148 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3149 project = dist1 > fToler2;
3151 if ( project ) { // compute new UV
3153 if ( !getClosestUV( projector, pNode, newUV )) {
3154 MESSAGE("Node Projection Failed " << node);
3158 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3160 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3162 if ( posType != SMDS_TOP_3DSPACE )
3163 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3164 if ( dist2 < dist1 )
3168 // store UV in the map
3169 listUV.push_back( uv );
3170 uvMap.insert( make_pair( node, &listUV.back() ));
3172 } // loop on not yet smoothed elements
3174 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3175 checkBoundaryNodes = true;
3177 // fix nodes on mesh boundary
3179 if ( checkBoundaryNodes ) {
3180 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3181 map< SMESH_TLink, int >::iterator link_nb;
3182 // put all elements links to linkNbMap
3183 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3184 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3185 const SMDS_MeshElement* elem = (*elemIt);
3186 int nbn = elem->NbCornerNodes();
3187 // loop on elem links: insert them in linkNbMap
3188 for ( int iN = 0; iN < nbn; ++iN ) {
3189 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3190 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3191 SMESH_TLink link( n1, n2 );
3192 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3196 // remove nodes that are in links encountered only once from setMovableNodes
3197 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3198 if ( link_nb->second == 1 ) {
3199 setMovableNodes.erase( link_nb->first.node1() );
3200 setMovableNodes.erase( link_nb->first.node2() );
3205 // -----------------------------------------------------
3206 // for nodes on seam edge, compute one more UV ( uvMap2 );
3207 // find movable nodes linked to nodes on seam and which
3208 // are to be smoothed using the second UV ( uvMap2 )
3209 // -----------------------------------------------------
3211 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3212 if ( !surface.IsNull() ) {
3213 TopExp_Explorer eExp( face, TopAbs_EDGE );
3214 for ( ; eExp.More(); eExp.Next() ) {
3215 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3216 if ( !BRep_Tool::IsClosed( edge, face ))
3218 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3219 if ( !sm ) continue;
3220 // find out which parameter varies for a node on seam
3223 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3224 if ( pcurve.IsNull() ) continue;
3225 uv1 = pcurve->Value( f );
3227 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3228 if ( pcurve.IsNull() ) continue;
3229 uv2 = pcurve->Value( f );
3230 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3232 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3233 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3235 // get nodes on seam and its vertices
3236 list< const SMDS_MeshNode* > seamNodes;
3237 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3238 while ( nSeamIt->more() ) {
3239 const SMDS_MeshNode* node = nSeamIt->next();
3240 if ( !isQuadratic || !IsMedium( node ))
3241 seamNodes.push_back( node );
3243 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3244 for ( ; vExp.More(); vExp.Next() ) {
3245 sm = aMesh->MeshElements( vExp.Current() );
3247 nSeamIt = sm->GetNodes();
3248 while ( nSeamIt->more() )
3249 seamNodes.push_back( nSeamIt->next() );
3252 // loop on nodes on seam
3253 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3254 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3255 const SMDS_MeshNode* nSeam = *noSeIt;
3256 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3257 if ( n_uv == uvMap.end() )
3260 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3261 // set the second UV
3262 listUV.push_back( *n_uv->second );
3263 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3264 if ( uvMap2.empty() )
3265 uvMap2 = uvMap; // copy the uvMap contents
3266 uvMap2[ nSeam ] = &listUV.back();
3268 // collect movable nodes linked to ones on seam in nodesNearSeam
3269 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3270 while ( eIt->more() ) {
3271 const SMDS_MeshElement* e = eIt->next();
3272 int nbUseMap1 = 0, nbUseMap2 = 0;
3273 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3274 int nn = 0, nbn = e->NbNodes();
3275 if(e->IsQuadratic()) nbn = nbn/2;
3276 while ( nn++ < nbn )
3278 const SMDS_MeshNode* n =
3279 static_cast<const SMDS_MeshNode*>( nIt->next() );
3281 setMovableNodes.find( n ) == setMovableNodes.end() )
3283 // add only nodes being closer to uv2 than to uv1
3284 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3285 0.5 * ( n->Y() + nSeam->Y() ),
3286 0.5 * ( n->Z() + nSeam->Z() ));
3288 getClosestUV( projector, pMid, uv );
3289 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3290 nodesNearSeam.insert( n );
3296 // for centroidalSmooth all element nodes must
3297 // be on one side of a seam
3298 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3299 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3301 while ( nn++ < nbn ) {
3302 const SMDS_MeshNode* n =
3303 static_cast<const SMDS_MeshNode*>( nIt->next() );
3304 setMovableNodes.erase( n );
3308 } // loop on nodes on seam
3309 } // loop on edge of a face
3310 } // if ( !face.IsNull() )
3312 if ( setMovableNodes.empty() ) {
3313 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3314 continue; // goto next face
3322 double maxRatio = -1., maxDisplacement = -1.;
3323 set<const SMDS_MeshNode*>::iterator nodeToMove;
3324 for ( it = 0; it < theNbIterations; it++ ) {
3325 maxDisplacement = 0.;
3326 nodeToMove = setMovableNodes.begin();
3327 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3328 const SMDS_MeshNode* node = (*nodeToMove);
3329 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3332 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3333 if ( theSmoothMethod == LAPLACIAN )
3334 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3336 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3338 // node displacement
3339 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3340 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3341 if ( aDispl > maxDisplacement )
3342 maxDisplacement = aDispl;
3344 // no node movement => exit
3345 //if ( maxDisplacement < 1.e-16 ) {
3346 if ( maxDisplacement < disttol ) {
3347 MESSAGE("-- no node movement --");
3351 // check elements quality
3353 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3354 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3355 const SMDS_MeshElement* elem = (*elemIt);
3356 if ( !elem || elem->GetType() != SMDSAbs_Face )
3358 SMESH::Controls::TSequenceOfXYZ aPoints;
3359 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3360 double aValue = aQualityFunc.GetValue( aPoints );
3361 if ( aValue > maxRatio )
3365 if ( maxRatio <= theTgtAspectRatio ) {
3366 MESSAGE("-- quality achived --");
3369 if (it+1 == theNbIterations) {
3370 MESSAGE("-- Iteration limit exceeded --");
3372 } // smoothing iterations
3374 MESSAGE(" Face id: " << *fId <<
3375 " Nb iterstions: " << it <<
3376 " Displacement: " << maxDisplacement <<
3377 " Aspect Ratio " << maxRatio);
3379 // ---------------------------------------
3380 // new nodes positions are computed,
3381 // record movement in DS and set new UV
3382 // ---------------------------------------
3383 nodeToMove = setMovableNodes.begin();
3384 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3385 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3386 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3387 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3388 if ( node_uv != uvMap.end() ) {
3389 gp_XY* uv = node_uv->second;
3391 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3395 // move medium nodes of quadratic elements
3398 SMESH_MesherHelper helper( *GetMesh() );
3399 if ( !face.IsNull() )
3400 helper.SetSubShape( face );
3401 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3402 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3403 const SMDS_VtkFace* QF =
3404 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3405 if(QF && QF->IsQuadratic()) {
3406 vector<const SMDS_MeshNode*> Ns;
3407 Ns.reserve(QF->NbNodes()+1);
3408 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3409 while ( anIter->more() )
3410 Ns.push_back( cast2Node(anIter->next()) );
3411 Ns.push_back( Ns[0] );
3413 for(int i=0; i<QF->NbNodes(); i=i+2) {
3414 if ( !surface.IsNull() ) {
3415 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3416 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3417 gp_XY uv = ( uv1 + uv2 ) / 2.;
3418 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3419 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3422 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3423 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3424 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3426 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3427 fabs( Ns[i+1]->Y() - y ) > disttol ||
3428 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3429 // we have to move i+1 node
3430 aMesh->MoveNode( Ns[i+1], x, y, z );
3437 } // loop on face ids
3441 //=======================================================================
3442 //function : isReverse
3443 //purpose : Return true if normal of prevNodes is not co-directied with
3444 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3445 // iNotSame is where prevNodes and nextNodes are different.
3446 // If result is true then future volume orientation is OK
3447 //=======================================================================
3449 static bool isReverse(const SMDS_MeshElement* face,
3450 const vector<const SMDS_MeshNode*>& prevNodes,
3451 const vector<const SMDS_MeshNode*>& nextNodes,
3455 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3456 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3457 gp_XYZ extrDir( pN - pP ), faceNorm;
3458 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3460 return faceNorm * extrDir < 0.0;
3463 //=======================================================================
3465 * \brief Create elements by sweeping an element
3466 * \param elem - element to sweep
3467 * \param newNodesItVec - nodes generated from each node of the element
3468 * \param newElems - generated elements
3469 * \param nbSteps - number of sweeping steps
3470 * \param srcElements - to append elem for each generated element
3472 //=======================================================================
3474 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3475 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3476 list<const SMDS_MeshElement*>& newElems,
3478 SMESH_SequenceOfElemPtr& srcElements)
3480 //MESSAGE("sweepElement " << nbSteps);
3481 SMESHDS_Mesh* aMesh = GetMeshDS();
3483 const int nbNodes = elem->NbNodes();
3484 const int nbCorners = elem->NbCornerNodes();
3485 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3486 polyhedron creation !!! */
3487 // Loop on elem nodes:
3488 // find new nodes and detect same nodes indices
3489 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3490 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3491 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3492 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3494 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3495 vector<int> sames(nbNodes);
3496 vector<bool> isSingleNode(nbNodes);
3498 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3499 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3500 const SMDS_MeshNode* node = nnIt->first;
3501 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3502 if ( listNewNodes.empty() )
3505 itNN [ iNode ] = listNewNodes.begin();
3506 prevNod[ iNode ] = node;
3507 nextNod[ iNode ] = listNewNodes.front();
3509 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3510 corner node of linear */
3511 if ( prevNod[ iNode ] != nextNod [ iNode ])
3512 nbDouble += !isSingleNode[iNode];
3514 if( iNode < nbCorners ) { // check corners only
3515 if ( prevNod[ iNode ] == nextNod [ iNode ])
3516 sames[nbSame++] = iNode;
3518 iNotSameNode = iNode;
3522 if ( nbSame == nbNodes || nbSame > 2) {
3523 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3527 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3529 // fix nodes order to have bottom normal external
3530 if ( baseType == SMDSEntity_Polygon )
3532 std::reverse( itNN.begin(), itNN.end() );
3533 std::reverse( prevNod.begin(), prevNod.end() );
3534 std::reverse( midlNod.begin(), midlNod.end() );
3535 std::reverse( nextNod.begin(), nextNod.end() );
3536 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3540 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3541 SMDS_MeshCell::applyInterlace( ind, itNN );
3542 SMDS_MeshCell::applyInterlace( ind, prevNod );
3543 SMDS_MeshCell::applyInterlace( ind, nextNod );
3544 SMDS_MeshCell::applyInterlace( ind, midlNod );
3545 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3548 sames[nbSame] = iNotSameNode;
3549 for ( int j = 0; j <= nbSame; ++j )
3550 for ( size_t i = 0; i < ind.size(); ++i )
3551 if ( ind[i] == sames[j] )
3556 iNotSameNode = sames[nbSame];
3561 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3563 iSameNode = sames[ nbSame-1 ];
3564 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3565 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3566 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3569 // make new elements
3570 for (int iStep = 0; iStep < nbSteps; iStep++ )
3573 for ( iNode = 0; iNode < nbNodes; iNode++ )
3575 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3576 nextNod[ iNode ] = *itNN[ iNode ]++;
3579 SMDS_MeshElement* aNewElem = 0;
3580 /*if(!elem->IsPoly())*/ {
3581 switch ( baseType ) {
3583 case SMDSEntity_Node: { // sweep NODE
3584 if ( nbSame == 0 ) {
3585 if ( isSingleNode[0] )
3586 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3588 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3594 case SMDSEntity_Edge: { // sweep EDGE
3595 if ( nbDouble == 0 )
3597 if ( nbSame == 0 ) // ---> quadrangle
3598 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3599 nextNod[ 1 ], nextNod[ 0 ] );
3600 else // ---> triangle
3601 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3602 nextNod[ iNotSameNode ] );
3604 else // ---> polygon
3606 vector<const SMDS_MeshNode*> poly_nodes;
3607 poly_nodes.push_back( prevNod[0] );
3608 poly_nodes.push_back( prevNod[1] );
3609 if ( prevNod[1] != nextNod[1] )
3611 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3612 poly_nodes.push_back( nextNod[1] );
3614 if ( prevNod[0] != nextNod[0] )
3616 poly_nodes.push_back( nextNod[0] );
3617 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3619 switch ( poly_nodes.size() ) {
3621 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3624 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3625 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3628 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3633 case SMDSEntity_Triangle: // TRIANGLE --->
3635 if ( nbDouble > 0 ) break;
3636 if ( nbSame == 0 ) // ---> pentahedron
3637 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3638 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3640 else if ( nbSame == 1 ) // ---> pyramid
3641 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3642 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3643 nextNod[ iSameNode ]);
3645 else // 2 same nodes: ---> tetrahedron
3646 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3647 nextNod[ iNotSameNode ]);
3650 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3654 if ( nbDouble+nbSame == 2 )
3656 if(nbSame==0) { // ---> quadratic quadrangle
3657 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3658 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3660 else { //(nbSame==1) // ---> quadratic triangle
3662 return; // medium node on axis
3664 else if(sames[0]==0)
3665 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3666 nextNod[2], midlNod[1], prevNod[2]);
3668 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3669 midlNod[0], nextNod[2], prevNod[2]);
3672 else if ( nbDouble == 3 )
3674 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3675 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3676 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3683 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3684 if ( nbDouble > 0 ) break;
3686 if ( nbSame == 0 ) // ---> hexahedron
3687 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3688 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3690 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3691 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3692 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3693 nextNod[ iSameNode ]);
3694 newElems.push_back( aNewElem );
3695 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3696 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3697 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3699 else if ( nbSame == 2 ) { // ---> pentahedron
3700 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3701 // iBeforeSame is same too
3702 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3703 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3704 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3706 // iAfterSame is same too
3707 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3708 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3709 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3713 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3714 if ( nbDouble+nbSame != 3 ) break;
3716 // ---> pentahedron with 15 nodes
3717 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3718 nextNod[0], nextNod[1], nextNod[2],
3719 prevNod[3], prevNod[4], prevNod[5],
3720 nextNod[3], nextNod[4], nextNod[5],
3721 midlNod[0], midlNod[1], midlNod[2]);
3723 else if(nbSame==1) {
3724 // ---> 2d order pyramid of 13 nodes
3725 int apex = iSameNode;
3726 int i0 = ( apex + 1 ) % nbCorners;
3727 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3731 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3732 nextNod[i0], nextNod[i1], prevNod[apex],
3733 prevNod[i01], midlNod[i0],
3734 nextNod[i01], midlNod[i1],
3735 prevNod[i1a], prevNod[i0a],
3736 nextNod[i0a], nextNod[i1a]);
3738 else if(nbSame==2) {
3739 // ---> 2d order tetrahedron of 10 nodes
3740 int n1 = iNotSameNode;
3741 int n2 = ( n1 + 1 ) % nbCorners;
3742 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3746 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3747 prevNod[n12], prevNod[n23], prevNod[n31],
3748 midlNod[n1], nextNod[n12], nextNod[n31]);
3752 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3753 if ( nbDouble != 4 ) break;
3755 // ---> hexahedron with 20 nodes
3756 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3757 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3758 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3759 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3760 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3762 else if(nbSame==1) {
3763 // ---> pyramid + pentahedron - can not be created since it is needed
3764 // additional middle node at the center of face
3765 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3768 else if( nbSame == 2 ) {
3769 // ---> 2d order Pentahedron with 15 nodes
3771 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3772 // iBeforeSame is same too
3779 // iAfterSame is same too
3789 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3790 prevNod[n4], prevNod[n5], nextNod[n5],
3791 prevNod[n12], midlNod[n2], nextNod[n12],
3792 prevNod[n45], midlNod[n5], nextNod[n45],
3793 prevNod[n14], prevNod[n25], nextNod[n25]);
3797 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3799 if( nbSame == 0 && nbDouble == 9 ) {
3800 // ---> tri-quadratic hexahedron with 27 nodes
3801 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3802 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3803 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3804 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3805 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3806 prevNod[8], // bottom center
3807 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3808 nextNod[8], // top center
3809 midlNod[8]);// elem center
3817 case SMDSEntity_Polygon: { // sweep POLYGON
3819 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3820 // ---> hexagonal prism
3821 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3822 prevNod[3], prevNod[4], prevNod[5],
3823 nextNod[0], nextNod[1], nextNod[2],
3824 nextNod[3], nextNod[4], nextNod[5]);
3833 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3835 if ( baseType != SMDSEntity_Polygon )
3837 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3838 SMDS_MeshCell::applyInterlace( ind, prevNod );
3839 SMDS_MeshCell::applyInterlace( ind, nextNod );
3840 SMDS_MeshCell::applyInterlace( ind, midlNod );
3841 SMDS_MeshCell::applyInterlace( ind, itNN );
3842 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3843 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3845 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3846 vector<int> quantities (nbNodes + 2);
3847 polyedre_nodes.clear();
3851 for (int inode = 0; inode < nbNodes; inode++)
3852 polyedre_nodes.push_back( prevNod[inode] );
3853 quantities.push_back( nbNodes );
3856 polyedre_nodes.push_back( nextNod[0] );
3857 for (int inode = nbNodes; inode-1; --inode )
3858 polyedre_nodes.push_back( nextNod[inode-1] );
3859 quantities.push_back( nbNodes );
3862 for (int iface = 0; iface < nbNodes; iface++)
3864 const int prevNbNodes = polyedre_nodes.size();
3865 int inextface = (iface+1) % nbNodes;
3866 polyedre_nodes.push_back( prevNod[inextface] );
3867 polyedre_nodes.push_back( prevNod[iface] );
3868 if ( prevNod[iface] != nextNod[iface] )
3870 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
3871 polyedre_nodes.push_back( nextNod[iface] );
3873 if ( prevNod[inextface] != nextNod[inextface] )
3875 polyedre_nodes.push_back( nextNod[inextface] );
3876 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
3878 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
3879 if ( nbFaceNodes > 2 )
3880 quantities.push_back( nbFaceNodes );
3881 else // degenerated face
3882 polyedre_nodes.resize( prevNbNodes );
3884 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3888 newElems.push_back( aNewElem );
3889 myLastCreatedElems.Append(aNewElem);
3890 srcElements.Append( elem );
3893 // set new prev nodes
3894 for ( iNode = 0; iNode < nbNodes; iNode++ )
3895 prevNod[ iNode ] = nextNod[ iNode ];
3900 //=======================================================================
3902 * \brief Create 1D and 2D elements around swept elements
3903 * \param mapNewNodes - source nodes and ones generated from them
3904 * \param newElemsMap - source elements and ones generated from them
3905 * \param elemNewNodesMap - nodes generated from each node of each element
3906 * \param elemSet - all swept elements
3907 * \param nbSteps - number of sweeping steps
3908 * \param srcElements - to append elem for each generated element
3910 //=======================================================================
3912 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3913 TElemOfElemListMap & newElemsMap,
3914 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3915 TIDSortedElemSet& elemSet,
3917 SMESH_SequenceOfElemPtr& srcElements)
3919 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3920 SMESHDS_Mesh* aMesh = GetMeshDS();
3922 // Find nodes belonging to only one initial element - sweep them to get edges.
3924 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3925 for ( ; nList != mapNewNodes.end(); nList++ )
3927 const SMDS_MeshNode* node =
3928 static_cast<const SMDS_MeshNode*>( nList->first );
3929 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3930 int nbInitElems = 0;
3931 const SMDS_MeshElement* el = 0;
3932 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3933 while ( eIt->more() && nbInitElems < 2 ) {
3935 SMDSAbs_ElementType type = el->GetType();
3936 if ( type == SMDSAbs_Volume || type < highType ) continue;
3937 if ( type > highType ) {
3941 nbInitElems += elemSet.count(el);
3943 if ( nbInitElems < 2 ) {
3944 bool NotCreateEdge = el && el->IsMediumNode(node);
3945 if(!NotCreateEdge) {
3946 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3947 list<const SMDS_MeshElement*> newEdges;
3948 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3953 // Make a ceiling for each element ie an equal element of last new nodes.
3954 // Find free links of faces - make edges and sweep them into faces.
3956 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3957 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3958 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
3960 const SMDS_MeshElement* elem = itElem->first;
3961 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3963 if(itElem->second.size()==0) continue;
3965 const bool isQuadratic = elem->IsQuadratic();
3967 if ( elem->GetType() == SMDSAbs_Edge ) {
3968 // create a ceiling edge
3969 if ( !isQuadratic ) {
3970 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3971 vecNewNodes[ 1 ]->second.back())) {
3972 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3973 vecNewNodes[ 1 ]->second.back()));
3974 srcElements.Append( myLastCreatedElems.Last() );
3978 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3979 vecNewNodes[ 1 ]->second.back(),
3980 vecNewNodes[ 2 ]->second.back())) {
3981 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3982 vecNewNodes[ 1 ]->second.back(),
3983 vecNewNodes[ 2 ]->second.back()));
3984 srcElements.Append( myLastCreatedElems.Last() );
3988 if ( elem->GetType() != SMDSAbs_Face )
3991 bool hasFreeLinks = false;
3993 TIDSortedElemSet avoidSet;
3994 avoidSet.insert( elem );
3996 set<const SMDS_MeshNode*> aFaceLastNodes;
3997 int iNode, nbNodes = vecNewNodes.size();
3998 if ( !isQuadratic ) {
3999 // loop on the face nodes
4000 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4001 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4002 // look for free links of the face
4003 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4004 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4005 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4006 // check if a link is free
4007 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4008 hasFreeLinks = true;
4009 // make an edge and a ceiling for a new edge
4010 if ( !aMesh->FindEdge( n1, n2 )) {
4011 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4012 srcElements.Append( myLastCreatedElems.Last() );
4014 n1 = vecNewNodes[ iNode ]->second.back();
4015 n2 = vecNewNodes[ iNext ]->second.back();
4016 if ( !aMesh->FindEdge( n1, n2 )) {
4017 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4018 srcElements.Append( myLastCreatedElems.Last() );
4023 else { // elem is quadratic face
4024 int nbn = nbNodes/2;
4025 for ( iNode = 0; iNode < nbn; iNode++ ) {
4026 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4027 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4028 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4029 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4030 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4031 // check if a link is free
4032 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4033 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4034 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4035 hasFreeLinks = true;
4036 // make an edge and a ceiling for a new edge
4038 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4039 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4040 srcElements.Append( myLastCreatedElems.Last() );
4042 n1 = vecNewNodes[ iNode ]->second.back();
4043 n2 = vecNewNodes[ iNext ]->second.back();
4044 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4045 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4046 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4047 srcElements.Append( myLastCreatedElems.Last() );
4051 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4052 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4056 // sweep free links into faces
4058 if ( hasFreeLinks ) {
4059 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4060 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4062 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4063 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4064 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4065 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4067 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4068 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4069 std::advance( v, volNb );
4070 // find indices of free faces of a volume and their source edges
4071 list< int > freeInd;
4072 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4073 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4074 int iF, nbF = vTool.NbFaces();
4075 for ( iF = 0; iF < nbF; iF ++ ) {
4076 if (vTool.IsFreeFace( iF ) &&
4077 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4078 initNodeSet != faceNodeSet) // except an initial face
4080 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4082 freeInd.push_back( iF );
4083 // find source edge of a free face iF
4084 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4085 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4086 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4087 initNodeSet.begin(), initNodeSet.end(),
4088 commonNodes.begin());
4089 if ( (*v)->IsQuadratic() )
4090 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4092 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4094 if ( !srcEdges.back() )
4096 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4097 << iF << " of volume #" << vTool.ID() << endl;
4102 if ( freeInd.empty() )
4105 // create faces for all steps;
4106 // if such a face has been already created by sweep of edge,
4107 // assure that its orientation is OK
4108 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4109 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4110 vTool.SetExternalNormal();
4111 const int nextShift = vTool.IsForward() ? +1 : -1;
4112 list< int >::iterator ind = freeInd.begin();
4113 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4114 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4116 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4117 int nbn = vTool.NbFaceNodes( *ind );
4118 const SMDS_MeshElement * f = 0;
4119 if ( nbn == 3 ) ///// triangle
4121 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4123 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4125 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4127 nodes[ 1 + nextShift ] };
4129 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4131 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4135 else if ( nbn == 4 ) ///// quadrangle
4137 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4139 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4141 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4142 nodes[ 2 ], nodes[ 2+nextShift ] };
4144 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4146 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4147 newOrder[ 2 ], newOrder[ 3 ]));
4150 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4152 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4154 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4156 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4158 nodes[2 + 2*nextShift],
4159 nodes[3 - 2*nextShift],
4161 nodes[3 + 2*nextShift]};
4163 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4165 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4173 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4175 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4176 nodes[1], nodes[3], nodes[5], nodes[7] );
4178 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4180 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4181 nodes[4 - 2*nextShift],
4183 nodes[4 + 2*nextShift],
4185 nodes[5 - 2*nextShift],
4187 nodes[5 + 2*nextShift] };
4189 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4191 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4192 newOrder[ 2 ], newOrder[ 3 ],
4193 newOrder[ 4 ], newOrder[ 5 ],
4194 newOrder[ 6 ], newOrder[ 7 ]));
4197 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4199 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4200 SMDSAbs_Face, /*noMedium=*/false);
4202 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4204 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4205 nodes[4 - 2*nextShift],
4207 nodes[4 + 2*nextShift],
4209 nodes[5 - 2*nextShift],
4211 nodes[5 + 2*nextShift],
4214 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4216 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4217 newOrder[ 2 ], newOrder[ 3 ],
4218 newOrder[ 4 ], newOrder[ 5 ],
4219 newOrder[ 6 ], newOrder[ 7 ],
4223 else //////// polygon
4225 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4226 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4228 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4230 if ( !vTool.IsForward() )
4231 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4233 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4235 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4239 while ( srcElements.Length() < myLastCreatedElems.Length() )
4240 srcElements.Append( *srcEdge );
4242 } // loop on free faces
4244 // go to the next volume
4246 while ( iVol++ < nbVolumesByStep ) v++;
4249 } // loop on volumes of one step
4250 } // sweep free links into faces
4252 // Make a ceiling face with a normal external to a volume
4254 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4256 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4258 lastVol.SetExternalNormal();
4259 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4260 int nbn = lastVol.NbFaceNodes( iF );
4262 if (!hasFreeLinks ||
4263 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4264 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4266 else if ( nbn == 4 )
4268 if (!hasFreeLinks ||
4269 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4270 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4272 else if ( nbn == 6 && isQuadratic )
4274 if (!hasFreeLinks ||
4275 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4276 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4277 nodes[1], nodes[3], nodes[5]));
4279 else if ( nbn == 8 && isQuadratic )
4281 if (!hasFreeLinks ||
4282 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4283 nodes[1], nodes[3], nodes[5], nodes[7]) )
4284 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4285 nodes[1], nodes[3], nodes[5], nodes[7]));
4287 else if ( nbn == 9 && isQuadratic )
4289 if (!hasFreeLinks ||
4290 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4291 SMDSAbs_Face, /*noMedium=*/false) )
4292 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4293 nodes[1], nodes[3], nodes[5], nodes[7],
4297 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4298 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4299 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4302 while ( srcElements.Length() < myLastCreatedElems.Length() )
4303 srcElements.Append( myLastCreatedElems.Last() );
4305 } // loop on swept elements
4308 //=======================================================================
4309 //function : RotationSweep
4311 //=======================================================================
4313 SMESH_MeshEditor::PGroupIDs
4314 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4315 const gp_Ax1& theAxis,
4316 const double theAngle,
4317 const int theNbSteps,
4318 const double theTol,
4319 const bool theMakeGroups,
4320 const bool theMakeWalls)
4322 myLastCreatedElems.Clear();
4323 myLastCreatedNodes.Clear();
4325 // source elements for each generated one
4326 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4328 MESSAGE( "RotationSweep()");
4330 aTrsf.SetRotation( theAxis, theAngle );
4332 aTrsf2.SetRotation( theAxis, theAngle/2. );
4334 gp_Lin aLine( theAxis );
4335 double aSqTol = theTol * theTol;
4337 SMESHDS_Mesh* aMesh = GetMeshDS();
4339 TNodeOfNodeListMap mapNewNodes;
4340 TElemOfVecOfNnlmiMap mapElemNewNodes;
4341 TElemOfElemListMap newElemsMap;
4343 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4344 myMesh->NbFaces(ORDER_QUADRATIC) +
4345 myMesh->NbVolumes(ORDER_QUADRATIC) );
4347 TIDSortedElemSet::iterator itElem;
4348 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4349 const SMDS_MeshElement* elem = *itElem;
4350 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4352 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4353 newNodesItVec.reserve( elem->NbNodes() );
4355 // loop on elem nodes
4356 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4357 while ( itN->more() )
4359 // check if a node has been already sweeped
4360 const SMDS_MeshNode* node = cast2Node( itN->next() );
4362 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4364 aXYZ.Coord( coord[0], coord[1], coord[2] );
4365 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4367 TNodeOfNodeListMapItr nIt =
4368 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4369 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4370 if ( listNewNodes.empty() )
4372 // check if we are to create medium nodes between corner ones
4373 bool needMediumNodes = false;
4374 if ( isQuadraticMesh )
4376 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4377 while (it->more() && !needMediumNodes )
4379 const SMDS_MeshElement* invElem = it->next();
4380 if ( invElem != elem && !theElems.count( invElem )) continue;
4381 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4382 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4383 needMediumNodes = true;
4388 const SMDS_MeshNode * newNode = node;
4389 for ( int i = 0; i < theNbSteps; i++ ) {
4391 if ( needMediumNodes ) // create a medium node
4393 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4394 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4395 myLastCreatedNodes.Append(newNode);
4396 srcNodes.Append( node );
4397 listNewNodes.push_back( newNode );
4398 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4401 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4403 // create a corner node
4404 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4405 myLastCreatedNodes.Append(newNode);
4406 srcNodes.Append( node );
4407 listNewNodes.push_back( newNode );
4410 listNewNodes.push_back( newNode );
4411 // if ( needMediumNodes )
4412 // listNewNodes.push_back( newNode );
4416 newNodesItVec.push_back( nIt );
4418 // make new elements
4419 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4423 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4425 PGroupIDs newGroupIDs;
4426 if ( theMakeGroups )
4427 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4433 //=======================================================================
4434 //function : CreateNode
4436 //=======================================================================
4437 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4440 const double tolnode,
4441 SMESH_SequenceOfNode& aNodes)
4443 // myLastCreatedElems.Clear();
4444 // myLastCreatedNodes.Clear();
4447 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4449 // try to search in sequence of existing nodes
4450 // if aNodes.Length()>0 we 'nave to use given sequence
4451 // else - use all nodes of mesh
4452 if(aNodes.Length()>0) {
4454 for(i=1; i<=aNodes.Length(); i++) {
4455 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4456 if(P1.Distance(P2)<tolnode)
4457 return aNodes.Value(i);
4461 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4462 while(itn->more()) {
4463 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4464 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4465 if(P1.Distance(P2)<tolnode)
4470 // create new node and return it
4471 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4472 //myLastCreatedNodes.Append(NewNode);
4477 //=======================================================================
4478 //function : ExtrusionSweep
4480 //=======================================================================
4482 SMESH_MeshEditor::PGroupIDs
4483 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4484 const gp_Vec& theStep,
4485 const int theNbSteps,
4486 TElemOfElemListMap& newElemsMap,
4487 const bool theMakeGroups,
4489 const double theTolerance)
4491 ExtrusParam aParams;
4492 aParams.myDir = gp_Dir(theStep);
4493 aParams.myNodes.Clear();
4494 aParams.mySteps = new TColStd_HSequenceOfReal;
4496 for(i=1; i<=theNbSteps; i++)
4497 aParams.mySteps->Append(theStep.Magnitude());
4500 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4504 //=======================================================================
4505 //function : ExtrusionSweep
4507 //=======================================================================
4509 SMESH_MeshEditor::PGroupIDs
4510 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4511 ExtrusParam& theParams,
4512 TElemOfElemListMap& newElemsMap,
4513 const bool theMakeGroups,
4515 const double theTolerance)
4517 myLastCreatedElems.Clear();
4518 myLastCreatedNodes.Clear();
4520 // source elements for each generated one
4521 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4523 SMESHDS_Mesh* aMesh = GetMeshDS();
4525 int nbsteps = theParams.mySteps->Length();
4527 TNodeOfNodeListMap mapNewNodes;
4528 //TNodeOfNodeVecMap mapNewNodes;
4529 TElemOfVecOfNnlmiMap mapElemNewNodes;
4530 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4532 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4533 myMesh->NbFaces(ORDER_QUADRATIC) +
4534 myMesh->NbVolumes(ORDER_QUADRATIC) );
4536 TIDSortedElemSet::iterator itElem;
4537 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4538 // check element type
4539 const SMDS_MeshElement* elem = *itElem;
4540 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4543 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4544 newNodesItVec.reserve( elem->NbNodes() );
4546 // loop on elem nodes
4547 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4548 while ( itN->more() )
4550 // check if a node has been already sweeped
4551 const SMDS_MeshNode* node = cast2Node( itN->next() );
4552 TNodeOfNodeListMap::iterator nIt =
4553 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4554 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4555 if ( listNewNodes.empty() )
4559 // check if we are to create medium nodes between corner ones
4560 bool needMediumNodes = false;
4561 if ( isQuadraticMesh )
4563 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4564 while (it->more() && !needMediumNodes )
4566 const SMDS_MeshElement* invElem = it->next();
4567 if ( invElem != elem && !theElems.count( invElem )) continue;
4568 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4569 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4570 needMediumNodes = true;
4574 double coord[] = { node->X(), node->Y(), node->Z() };
4575 for ( int i = 0; i < nbsteps; i++ )
4577 if ( needMediumNodes ) // create a medium node
4579 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4580 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4581 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4582 if( theFlags & EXTRUSION_FLAG_SEW ) {
4583 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4584 theTolerance, theParams.myNodes);
4585 listNewNodes.push_back( newNode );
4588 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4589 myLastCreatedNodes.Append(newNode);
4590 srcNodes.Append( node );
4591 listNewNodes.push_back( newNode );
4594 // create a corner node
4595 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4596 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4597 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4598 if( theFlags & EXTRUSION_FLAG_SEW ) {
4599 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4600 theTolerance, theParams.myNodes);
4601 listNewNodes.push_back( newNode );
4604 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4605 myLastCreatedNodes.Append(newNode);
4606 srcNodes.Append( node );
4607 listNewNodes.push_back( newNode );
4611 newNodesItVec.push_back( nIt );
4613 // make new elements
4614 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4617 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4618 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4620 PGroupIDs newGroupIDs;
4621 if ( theMakeGroups )
4622 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4627 //=======================================================================
4628 //function : ExtrusionAlongTrack
4630 //=======================================================================
4631 SMESH_MeshEditor::Extrusion_Error
4632 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4633 SMESH_subMesh* theTrack,
4634 const SMDS_MeshNode* theN1,
4635 const bool theHasAngles,
4636 list<double>& theAngles,
4637 const bool theLinearVariation,
4638 const bool theHasRefPoint,
4639 const gp_Pnt& theRefPoint,
4640 const bool theMakeGroups)
4642 MESSAGE("ExtrusionAlongTrack");
4643 myLastCreatedElems.Clear();
4644 myLastCreatedNodes.Clear();
4647 std::list<double> aPrms;
4648 TIDSortedElemSet::iterator itElem;
4651 TopoDS_Edge aTrackEdge;
4652 TopoDS_Vertex aV1, aV2;
4654 SMDS_ElemIteratorPtr aItE;
4655 SMDS_NodeIteratorPtr aItN;
4656 SMDSAbs_ElementType aTypeE;
4658 TNodeOfNodeListMap mapNewNodes;
4661 aNbE = theElements.size();
4664 return EXTR_NO_ELEMENTS;
4666 // 1.1 Track Pattern
4669 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4671 aItE = pSubMeshDS->GetElements();
4672 while ( aItE->more() ) {
4673 const SMDS_MeshElement* pE = aItE->next();
4674 aTypeE = pE->GetType();
4675 // Pattern must contain links only
4676 if ( aTypeE != SMDSAbs_Edge )
4677 return EXTR_PATH_NOT_EDGE;
4680 list<SMESH_MeshEditor_PathPoint> fullList;
4682 const TopoDS_Shape& aS = theTrack->GetSubShape();
4683 // Sub-shape for the Pattern must be an Edge or Wire
4684 if( aS.ShapeType() == TopAbs_EDGE ) {
4685 aTrackEdge = TopoDS::Edge( aS );
4686 // the Edge must not be degenerated
4687 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4688 return EXTR_BAD_PATH_SHAPE;
4689 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4690 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4691 const SMDS_MeshNode* aN1 = aItN->next();
4692 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4693 const SMDS_MeshNode* aN2 = aItN->next();
4694 // starting node must be aN1 or aN2
4695 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4696 return EXTR_BAD_STARTING_NODE;
4697 aItN = pSubMeshDS->GetNodes();
4698 while ( aItN->more() ) {
4699 const SMDS_MeshNode* pNode = aItN->next();
4700 const SMDS_EdgePosition* pEPos =
4701 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4702 double aT = pEPos->GetUParameter();
4703 aPrms.push_back( aT );
4705 //Extrusion_Error err =
4706 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4707 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4708 list< SMESH_subMesh* > LSM;
4709 TopTools_SequenceOfShape Edges;
4710 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4711 while(itSM->more()) {
4712 SMESH_subMesh* SM = itSM->next();
4714 const TopoDS_Shape& aS = SM->GetSubShape();
4717 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4718 int startNid = theN1->GetID();
4719 TColStd_MapOfInteger UsedNums;
4721 int NbEdges = Edges.Length();
4723 for(; i<=NbEdges; i++) {
4725 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4726 for(; itLSM!=LSM.end(); itLSM++) {
4728 if(UsedNums.Contains(k)) continue;
4729 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4730 SMESH_subMesh* locTrack = *itLSM;
4731 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4732 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4733 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4734 const SMDS_MeshNode* aN1 = aItN->next();
4735 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4736 const SMDS_MeshNode* aN2 = aItN->next();
4737 // starting node must be aN1 or aN2
4738 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4739 // 2. Collect parameters on the track edge
4741 aItN = locMeshDS->GetNodes();
4742 while ( aItN->more() ) {
4743 const SMDS_MeshNode* pNode = aItN->next();
4744 const SMDS_EdgePosition* pEPos =
4745 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4746 double aT = pEPos->GetUParameter();
4747 aPrms.push_back( aT );
4749 list<SMESH_MeshEditor_PathPoint> LPP;
4750 //Extrusion_Error err =
4751 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4752 LLPPs.push_back(LPP);
4754 // update startN for search following egde
4755 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4756 else startNid = aN1->GetID();
4760 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4761 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4762 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4763 for(; itPP!=firstList.end(); itPP++) {
4764 fullList.push_back( *itPP );
4766 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4767 fullList.pop_back();
4769 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4770 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4771 itPP = currList.begin();
4772 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4773 gp_Dir D1 = PP1.Tangent();
4774 gp_Dir D2 = PP2.Tangent();
4775 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4776 (D1.Z()+D2.Z())/2 ) );
4777 PP1.SetTangent(Dnew);
4778 fullList.push_back(PP1);
4780 for(; itPP!=firstList.end(); itPP++) {
4781 fullList.push_back( *itPP );
4783 PP1 = fullList.back();
4784 fullList.pop_back();
4786 // if wire not closed
4787 fullList.push_back(PP1);
4791 return EXTR_BAD_PATH_SHAPE;
4794 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4795 theHasRefPoint, theRefPoint, theMakeGroups);
4799 //=======================================================================
4800 //function : ExtrusionAlongTrack
4802 //=======================================================================
4803 SMESH_MeshEditor::Extrusion_Error
4804 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4805 SMESH_Mesh* theTrack,
4806 const SMDS_MeshNode* theN1,
4807 const bool theHasAngles,
4808 list<double>& theAngles,
4809 const bool theLinearVariation,
4810 const bool theHasRefPoint,
4811 const gp_Pnt& theRefPoint,
4812 const bool theMakeGroups)
4814 myLastCreatedElems.Clear();
4815 myLastCreatedNodes.Clear();
4818 std::list<double> aPrms;
4819 TIDSortedElemSet::iterator itElem;
4822 TopoDS_Edge aTrackEdge;
4823 TopoDS_Vertex aV1, aV2;
4825 SMDS_ElemIteratorPtr aItE;
4826 SMDS_NodeIteratorPtr aItN;
4827 SMDSAbs_ElementType aTypeE;
4829 TNodeOfNodeListMap mapNewNodes;
4832 aNbE = theElements.size();
4835 return EXTR_NO_ELEMENTS;
4837 // 1.1 Track Pattern
4840 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4842 aItE = pMeshDS->elementsIterator();
4843 while ( aItE->more() ) {
4844 const SMDS_MeshElement* pE = aItE->next();
4845 aTypeE = pE->GetType();
4846 // Pattern must contain links only
4847 if ( aTypeE != SMDSAbs_Edge )
4848 return EXTR_PATH_NOT_EDGE;
4851 list<SMESH_MeshEditor_PathPoint> fullList;
4853 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4855 if( aS == SMESH_Mesh::PseudoShape() ) {
4856 //Mesh without shape
4857 const SMDS_MeshNode* currentNode = NULL;
4858 const SMDS_MeshNode* prevNode = theN1;
4859 std::vector<const SMDS_MeshNode*> aNodesList;
4860 aNodesList.push_back(theN1);
4861 int nbEdges = 0, conn=0;
4862 const SMDS_MeshElement* prevElem = NULL;
4863 const SMDS_MeshElement* currentElem = NULL;
4864 int totalNbEdges = theTrack->NbEdges();
4865 SMDS_ElemIteratorPtr nIt;
4866 bool isClosed = false;
4869 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4870 return EXTR_BAD_STARTING_NODE;
4873 conn = nbEdgeConnectivity(theN1);
4875 return EXTR_PATH_NOT_EDGE;
4877 aItE = theN1->GetInverseElementIterator();
4878 prevElem = aItE->next();
4879 currentElem = prevElem;
4881 if(totalNbEdges == 1 ) {
4882 nIt = currentElem->nodesIterator();
4883 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4884 if(currentNode == prevNode)
4885 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4886 aNodesList.push_back(currentNode);
4888 nIt = currentElem->nodesIterator();
4889 while( nIt->more() ) {
4890 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4891 if(currentNode == prevNode)
4892 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4893 aNodesList.push_back(currentNode);
4895 //case of the closed mesh
4896 if(currentNode == theN1) {
4902 conn = nbEdgeConnectivity(currentNode);
4904 return EXTR_PATH_NOT_EDGE;
4905 }else if( conn == 1 && nbEdges > 0 ) {
4910 prevNode = currentNode;
4911 aItE = currentNode->GetInverseElementIterator();
4912 currentElem = aItE->next();
4913 if( currentElem == prevElem)
4914 currentElem = aItE->next();
4915 nIt = currentElem->nodesIterator();
4916 prevElem = currentElem;
4922 if(nbEdges != totalNbEdges)
4923 return EXTR_PATH_NOT_EDGE;
4925 TopTools_SequenceOfShape Edges;
4926 double x1,x2,y1,y2,z1,z2;
4927 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4928 int startNid = theN1->GetID();
4929 for(int i = 1; i < aNodesList.size(); i++) {
4930 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
4931 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
4932 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
4933 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
4934 list<SMESH_MeshEditor_PathPoint> LPP;
4936 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
4937 LLPPs.push_back(LPP);
4938 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
4939 else startNid = aNodesList[i-1]->GetID();
4943 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4944 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4945 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4946 for(; itPP!=firstList.end(); itPP++) {
4947 fullList.push_back( *itPP );
4950 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4951 SMESH_MeshEditor_PathPoint PP2;
4952 fullList.pop_back();
4954 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4955 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4956 itPP = currList.begin();
4957 PP2 = currList.front();
4958 gp_Dir D1 = PP1.Tangent();
4959 gp_Dir D2 = PP2.Tangent();
4960 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4961 (D1.Z()+D2.Z())/2 ) );
4962 PP1.SetTangent(Dnew);
4963 fullList.push_back(PP1);
4965 for(; itPP!=currList.end(); itPP++) {
4966 fullList.push_back( *itPP );
4968 PP1 = fullList.back();
4969 fullList.pop_back();
4971 fullList.push_back(PP1);
4973 } // Sub-shape for the Pattern must be an Edge or Wire
4974 else if( aS.ShapeType() == TopAbs_EDGE ) {
4975 aTrackEdge = TopoDS::Edge( aS );
4976 // the Edge must not be degenerated
4977 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4978 return EXTR_BAD_PATH_SHAPE;
4979 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4980 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4981 const SMDS_MeshNode* aN1 = aItN->next();
4982 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4983 const SMDS_MeshNode* aN2 = aItN->next();
4984 // starting node must be aN1 or aN2
4985 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4986 return EXTR_BAD_STARTING_NODE;
4987 aItN = pMeshDS->nodesIterator();
4988 while ( aItN->more() ) {
4989 const SMDS_MeshNode* pNode = aItN->next();
4990 if( pNode==aN1 || pNode==aN2 ) continue;
4991 const SMDS_EdgePosition* pEPos =
4992 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4993 double aT = pEPos->GetUParameter();
4994 aPrms.push_back( aT );
4996 //Extrusion_Error err =
4997 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4999 else if( aS.ShapeType() == TopAbs_WIRE ) {
5000 list< SMESH_subMesh* > LSM;
5001 TopTools_SequenceOfShape Edges;
5002 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5003 for(; eExp.More(); eExp.Next()) {
5004 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5005 if( BRep_Tool::Degenerated(E) ) continue;
5006 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5012 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5013 int startNid = theN1->GetID();
5014 TColStd_MapOfInteger UsedNums;
5015 int NbEdges = Edges.Length();
5017 for(; i<=NbEdges; i++) {
5019 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5020 for(; itLSM!=LSM.end(); itLSM++) {
5022 if(UsedNums.Contains(k)) continue;
5023 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5024 SMESH_subMesh* locTrack = *itLSM;
5025 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5026 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5027 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5028 const SMDS_MeshNode* aN1 = aItN->next();
5029 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5030 const SMDS_MeshNode* aN2 = aItN->next();
5031 // starting node must be aN1 or aN2
5032 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5033 // 2. Collect parameters on the track edge
5035 aItN = locMeshDS->GetNodes();
5036 while ( aItN->more() ) {
5037 const SMDS_MeshNode* pNode = aItN->next();
5038 const SMDS_EdgePosition* pEPos =
5039 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5040 double aT = pEPos->GetUParameter();
5041 aPrms.push_back( aT );
5043 list<SMESH_MeshEditor_PathPoint> LPP;
5044 //Extrusion_Error err =
5045 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5046 LLPPs.push_back(LPP);
5048 // update startN for search following egde
5049 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5050 else startNid = aN1->GetID();
5054 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5055 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5056 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5057 for(; itPP!=firstList.end(); itPP++) {
5058 fullList.push_back( *itPP );
5060 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5061 fullList.pop_back();
5063 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5064 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5065 itPP = currList.begin();
5066 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5067 gp_Dir D1 = PP1.Tangent();
5068 gp_Dir D2 = PP2.Tangent();
5069 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5070 (D1.Z()+D2.Z())/2 ) );
5071 PP1.SetTangent(Dnew);
5072 fullList.push_back(PP1);
5074 for(; itPP!=currList.end(); itPP++) {
5075 fullList.push_back( *itPP );
5077 PP1 = fullList.back();
5078 fullList.pop_back();
5080 // if wire not closed
5081 fullList.push_back(PP1);
5085 return EXTR_BAD_PATH_SHAPE;
5088 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5089 theHasRefPoint, theRefPoint, theMakeGroups);
5093 //=======================================================================
5094 //function : MakeEdgePathPoints
5095 //purpose : auxilary for ExtrusionAlongTrack
5096 //=======================================================================
5097 SMESH_MeshEditor::Extrusion_Error
5098 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5099 const TopoDS_Edge& aTrackEdge,
5101 list<SMESH_MeshEditor_PathPoint>& LPP)
5103 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5105 aTolVec2=aTolVec*aTolVec;
5107 TopoDS_Vertex aV1, aV2;
5108 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5109 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5110 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5111 // 2. Collect parameters on the track edge
5112 aPrms.push_front( aT1 );
5113 aPrms.push_back( aT2 );
5116 if( FirstIsStart ) {
5127 SMESH_MeshEditor_PathPoint aPP;
5128 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5129 std::list<double>::iterator aItD = aPrms.begin();
5130 for(; aItD != aPrms.end(); ++aItD) {
5134 aC3D->D1( aT, aP3D, aVec );
5135 aL2 = aVec.SquareMagnitude();
5136 if ( aL2 < aTolVec2 )
5137 return EXTR_CANT_GET_TANGENT;
5138 gp_Dir aTgt( aVec );
5140 aPP.SetTangent( aTgt );
5141 aPP.SetParameter( aT );
5148 //=======================================================================
5149 //function : MakeExtrElements
5150 //purpose : auxilary for ExtrusionAlongTrack
5151 //=======================================================================
5152 SMESH_MeshEditor::Extrusion_Error
5153 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5154 list<SMESH_MeshEditor_PathPoint>& fullList,
5155 const bool theHasAngles,
5156 list<double>& theAngles,
5157 const bool theLinearVariation,
5158 const bool theHasRefPoint,
5159 const gp_Pnt& theRefPoint,
5160 const bool theMakeGroups)
5162 MESSAGE("MakeExtrElements");
5163 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5164 int aNbTP = fullList.size();
5165 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5167 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5168 LinearAngleVariation(aNbTP-1, theAngles);
5170 vector<double> aAngles( aNbTP );
5172 for(; j<aNbTP; ++j) {
5175 if ( theHasAngles ) {
5177 std::list<double>::iterator aItD = theAngles.begin();
5178 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5180 aAngles[j] = anAngle;
5183 // fill vector of path points with angles
5184 //aPPs.resize(fullList.size());
5186 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5187 for(; itPP!=fullList.end(); itPP++) {
5189 SMESH_MeshEditor_PathPoint PP = *itPP;
5190 PP.SetAngle(aAngles[j]);
5194 TNodeOfNodeListMap mapNewNodes;
5195 TElemOfVecOfNnlmiMap mapElemNewNodes;
5196 TElemOfElemListMap newElemsMap;
5197 TIDSortedElemSet::iterator itElem;
5200 SMDSAbs_ElementType aTypeE;
5201 // source elements for each generated one
5202 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5204 // 3. Center of rotation aV0
5205 gp_Pnt aV0 = theRefPoint;
5207 if ( !theHasRefPoint ) {
5209 aGC.SetCoord( 0.,0.,0. );
5211 itElem = theElements.begin();
5212 for ( ; itElem != theElements.end(); itElem++ ) {
5213 const SMDS_MeshElement* elem = *itElem;
5215 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5216 while ( itN->more() ) {
5217 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5222 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5223 list<const SMDS_MeshNode*> aLNx;
5224 mapNewNodes[node] = aLNx;
5226 gp_XYZ aXYZ( aX, aY, aZ );
5234 } // if (!theHasRefPoint) {
5235 mapNewNodes.clear();
5237 // 4. Processing the elements
5238 SMESHDS_Mesh* aMesh = GetMeshDS();
5240 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5241 // check element type
5242 const SMDS_MeshElement* elem = *itElem;
5243 aTypeE = elem->GetType();
5244 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5247 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5248 newNodesItVec.reserve( elem->NbNodes() );
5250 // loop on elem nodes
5252 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5253 while ( itN->more() )
5256 // check if a node has been already processed
5257 const SMDS_MeshNode* node =
5258 static_cast<const SMDS_MeshNode*>( itN->next() );
5259 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5260 if ( nIt == mapNewNodes.end() ) {
5261 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5262 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5265 aX = node->X(); aY = node->Y(); aZ = node->Z();
5267 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5268 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5269 gp_Ax1 anAx1, anAxT1T0;
5270 gp_Dir aDT1x, aDT0x, aDT1T0;
5275 aPN0.SetCoord(aX, aY, aZ);
5277 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5279 aDT0x= aPP0.Tangent();
5280 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5282 for ( j = 1; j < aNbTP; ++j ) {
5283 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5285 aDT1x = aPP1.Tangent();
5286 aAngle1x = aPP1.Angle();
5288 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5290 gp_Vec aV01x( aP0x, aP1x );
5291 aTrsf.SetTranslation( aV01x );
5294 aV1x = aV0x.Transformed( aTrsf );
5295 aPN1 = aPN0.Transformed( aTrsf );
5297 // rotation 1 [ T1,T0 ]
5298 aAngleT1T0=-aDT1x.Angle( aDT0x );
5299 if (fabs(aAngleT1T0) > aTolAng) {
5301 anAxT1T0.SetLocation( aV1x );
5302 anAxT1T0.SetDirection( aDT1T0 );
5303 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5305 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5309 if ( theHasAngles ) {
5310 anAx1.SetLocation( aV1x );
5311 anAx1.SetDirection( aDT1x );
5312 aTrsfRot.SetRotation( anAx1, aAngle1x );
5314 aPN1 = aPN1.Transformed( aTrsfRot );
5318 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5319 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5320 // create additional node
5321 double x = ( aPN1.X() + aPN0.X() )/2.;
5322 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5323 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5324 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5325 myLastCreatedNodes.Append(newNode);
5326 srcNodes.Append( node );
5327 listNewNodes.push_back( newNode );
5332 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5333 myLastCreatedNodes.Append(newNode);
5334 srcNodes.Append( node );
5335 listNewNodes.push_back( newNode );
5345 // if current elem is quadratic and current node is not medium
5346 // we have to check - may be it is needed to insert additional nodes
5347 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5348 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5349 if(listNewNodes.size()==aNbTP-1) {
5350 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5351 gp_XYZ P(node->X(), node->Y(), node->Z());
5352 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5354 for(i=0; i<aNbTP-1; i++) {
5355 const SMDS_MeshNode* N = *it;
5356 double x = ( N->X() + P.X() )/2.;
5357 double y = ( N->Y() + P.Y() )/2.;
5358 double z = ( N->Z() + P.Z() )/2.;
5359 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5360 srcNodes.Append( node );
5361 myLastCreatedNodes.Append(newN);
5364 P = gp_XYZ(N->X(),N->Y(),N->Z());
5366 listNewNodes.clear();
5367 for(i=0; i<2*(aNbTP-1); i++) {
5368 listNewNodes.push_back(aNodes[i]);
5374 newNodesItVec.push_back( nIt );
5376 // make new elements
5377 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5378 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5379 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5382 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5384 if ( theMakeGroups )
5385 generateGroups( srcNodes, srcElems, "extruded");
5391 //=======================================================================
5392 //function : LinearAngleVariation
5393 //purpose : auxilary for ExtrusionAlongTrack
5394 //=======================================================================
5395 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5396 list<double>& Angles)
5398 int nbAngles = Angles.size();
5399 if( nbSteps > nbAngles ) {
5400 vector<double> theAngles(nbAngles);
5401 list<double>::iterator it = Angles.begin();
5403 for(; it!=Angles.end(); it++) {
5405 theAngles[i] = (*it);
5408 double rAn2St = double( nbAngles ) / double( nbSteps );
5409 double angPrev = 0, angle;
5410 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5411 double angCur = rAn2St * ( iSt+1 );
5412 double angCurFloor = floor( angCur );
5413 double angPrevFloor = floor( angPrev );
5414 if ( angPrevFloor == angCurFloor )
5415 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5417 int iP = int( angPrevFloor );
5418 double angPrevCeil = ceil(angPrev);
5419 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5421 int iC = int( angCurFloor );
5422 if ( iC < nbAngles )
5423 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5425 iP = int( angPrevCeil );
5427 angle += theAngles[ iC ];
5429 res.push_back(angle);
5434 for(; it!=res.end(); it++)
5435 Angles.push_back( *it );
5440 //================================================================================
5442 * \brief Move or copy theElements applying theTrsf to their nodes
5443 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5444 * \param theTrsf - transformation to apply
5445 * \param theCopy - if true, create translated copies of theElems
5446 * \param theMakeGroups - if true and theCopy, create translated groups
5447 * \param theTargetMesh - mesh to copy translated elements into
5448 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5450 //================================================================================
5452 SMESH_MeshEditor::PGroupIDs
5453 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5454 const gp_Trsf& theTrsf,
5456 const bool theMakeGroups,
5457 SMESH_Mesh* theTargetMesh)
5459 myLastCreatedElems.Clear();
5460 myLastCreatedNodes.Clear();
5462 bool needReverse = false;
5463 string groupPostfix;
5464 switch ( theTrsf.Form() ) {
5466 MESSAGE("gp_PntMirror");
5468 groupPostfix = "mirrored";
5471 MESSAGE("gp_Ax1Mirror");
5472 groupPostfix = "mirrored";
5475 MESSAGE("gp_Ax2Mirror");
5477 groupPostfix = "mirrored";
5480 MESSAGE("gp_Rotation");
5481 groupPostfix = "rotated";
5483 case gp_Translation:
5484 MESSAGE("gp_Translation");
5485 groupPostfix = "translated";
5488 MESSAGE("gp_Scale");
5489 groupPostfix = "scaled";
5491 case gp_CompoundTrsf: // different scale by axis
5492 MESSAGE("gp_CompoundTrsf");
5493 groupPostfix = "scaled";
5497 needReverse = false;
5498 groupPostfix = "transformed";
5501 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5502 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5503 SMESHDS_Mesh* aMesh = GetMeshDS();
5506 // map old node to new one
5507 TNodeNodeMap nodeMap;
5509 // elements sharing moved nodes; those of them which have all
5510 // nodes mirrored but are not in theElems are to be reversed
5511 TIDSortedElemSet inverseElemSet;
5513 // source elements for each generated one
5514 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5516 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5517 TIDSortedElemSet orphanNode;
5519 if ( theElems.empty() ) // transform the whole mesh
5522 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5523 while ( eIt->more() ) theElems.insert( eIt->next() );
5525 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5526 while ( nIt->more() )
5528 const SMDS_MeshNode* node = nIt->next();
5529 if ( node->NbInverseElements() == 0)
5530 orphanNode.insert( node );
5534 // loop on elements to transform nodes : first orphan nodes then elems
5535 TIDSortedElemSet::iterator itElem;
5536 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5537 for (int i=0; i<2; i++)
5538 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5539 const SMDS_MeshElement* elem = *itElem;
5543 // loop on elem nodes
5544 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5545 while ( itN->more() ) {
5547 const SMDS_MeshNode* node = cast2Node( itN->next() );
5548 // check if a node has been already transformed
5549 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5550 nodeMap.insert( make_pair ( node, node ));
5551 if ( !n2n_isnew.second )
5555 coord[0] = node->X();
5556 coord[1] = node->Y();
5557 coord[2] = node->Z();
5558 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5559 if ( theTargetMesh ) {
5560 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5561 n2n_isnew.first->second = newNode;
5562 myLastCreatedNodes.Append(newNode);
5563 srcNodes.Append( node );
5565 else if ( theCopy ) {
5566 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5567 n2n_isnew.first->second = newNode;
5568 myLastCreatedNodes.Append(newNode);
5569 srcNodes.Append( node );
5572 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5573 // node position on shape becomes invalid
5574 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5575 ( SMDS_SpacePosition::originSpacePosition() );
5578 // keep inverse elements
5579 if ( !theCopy && !theTargetMesh && needReverse ) {
5580 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5581 while ( invElemIt->more() ) {
5582 const SMDS_MeshElement* iel = invElemIt->next();
5583 inverseElemSet.insert( iel );
5589 // either create new elements or reverse mirrored ones
5590 if ( !theCopy && !needReverse && !theTargetMesh )
5593 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5594 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5595 theElems.insert( *invElemIt );
5597 // Replicate or reverse elements
5599 std::vector<int> iForw;
5600 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5602 const SMDS_MeshElement* elem = *itElem;
5603 if ( !elem || elem->GetType() == SMDSAbs_Node )
5606 int nbNodes = elem->NbNodes();
5607 int elemType = elem->GetType();
5609 if (elem->IsPoly()) {
5611 // polygon or polyhedral volume
5612 switch ( elemType ) {
5615 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5617 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5618 while (itN->more()) {
5619 const SMDS_MeshNode* node =
5620 static_cast<const SMDS_MeshNode*>(itN->next());
5621 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5622 if (nodeMapIt == nodeMap.end())
5623 break; // not all nodes transformed
5625 // reverse mirrored faces and volumes
5626 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5628 poly_nodes[iNode] = (*nodeMapIt).second;
5632 if ( iNode != nbNodes )
5633 continue; // not all nodes transformed
5635 if ( theTargetMesh ) {
5636 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5637 srcElems.Append( elem );
5639 else if ( theCopy ) {
5640 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5641 srcElems.Append( elem );
5644 aMesh->ChangePolygonNodes(elem, poly_nodes);
5648 case SMDSAbs_Volume:
5650 const SMDS_VtkVolume* aPolyedre =
5651 dynamic_cast<const SMDS_VtkVolume*>( elem );
5653 MESSAGE("Warning: bad volumic element");
5657 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5658 vector<int> quantities;
5660 bool allTransformed = true;
5661 int nbFaces = aPolyedre->NbFaces();
5662 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5663 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5664 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5665 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5666 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5667 if (nodeMapIt == nodeMap.end()) {
5668 allTransformed = false; // not all nodes transformed
5670 poly_nodes.push_back((*nodeMapIt).second);
5672 if ( needReverse && allTransformed )
5673 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5675 quantities.push_back(nbFaceNodes);
5677 if ( !allTransformed )
5678 continue; // not all nodes transformed
5680 if ( theTargetMesh ) {
5681 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5682 srcElems.Append( elem );
5684 else if ( theCopy ) {
5685 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5686 srcElems.Append( elem );
5689 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5701 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5702 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5703 const std::vector<int>& i = needReverse ? iRev : iForw;
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
7149 curNodes[ iCur ] = n;
7150 bool isUnique = nodeSet.insert( n ).second;
7152 uniqueNodes[ iUnique++ ] = n;
7154 iRepl[ nbRepl++ ] = iCur;
7158 // Analyse element topology after replacement
7161 int nbUniqueNodes = nodeSet.size();
7162 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7163 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7164 // Polygons and Polyhedral volumes
7165 if (elem->IsPoly()) {
7167 if (elem->GetType() == SMDSAbs_Face) {
7169 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7171 for (; inode < nbNodes; inode++) {
7172 face_nodes[inode] = curNodes[inode];
7175 vector<const SMDS_MeshNode *> polygons_nodes;
7176 vector<int> quantities;
7177 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7180 for (int iface = 0; iface < nbNew; iface++) {
7181 int nbNodes = quantities[iface];
7182 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7183 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7184 poly_nodes[ii] = polygons_nodes[inode];
7186 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7187 myLastCreatedElems.Append(newElem);
7189 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7192 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7193 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7194 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7196 if (nbNew > 0) quid = nbNew - 1;
7197 vector<int> newquant(quantities.begin()+quid, quantities.end());
7198 const SMDS_MeshElement* newElem = 0;
7199 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7200 myLastCreatedElems.Append(newElem);
7201 if ( aShapeId && newElem )
7202 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7203 rmElemIds.push_back(elem->GetID());
7206 rmElemIds.push_back(elem->GetID());
7210 else if (elem->GetType() == SMDSAbs_Volume) {
7211 // Polyhedral volume
7212 if (nbUniqueNodes < 4) {
7213 rmElemIds.push_back(elem->GetID());
7216 // each face has to be analyzed in order to check volume validity
7217 const SMDS_VtkVolume* aPolyedre =
7218 dynamic_cast<const SMDS_VtkVolume*>( elem );
7220 int nbFaces = aPolyedre->NbFaces();
7222 vector<const SMDS_MeshNode *> poly_nodes;
7223 vector<int> quantities;
7225 for (int iface = 1; iface <= nbFaces; iface++) {
7226 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7227 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7229 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7230 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7231 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7232 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7233 faceNode = (*nnIt).second;
7235 faceNodes[inode - 1] = faceNode;
7238 SimplifyFace(faceNodes, poly_nodes, quantities);
7241 if (quantities.size() > 3) {
7242 // to be done: remove coincident faces
7245 if (quantities.size() > 3)
7247 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7248 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7249 const SMDS_MeshElement* newElem = 0;
7250 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7251 myLastCreatedElems.Append(newElem);
7252 if ( aShapeId && newElem )
7253 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7254 rmElemIds.push_back(elem->GetID());
7258 rmElemIds.push_back(elem->GetID());
7269 // TODO not all the possible cases are solved. Find something more generic?
7270 switch ( nbNodes ) {
7271 case 2: ///////////////////////////////////// EDGE
7272 isOk = false; break;
7273 case 3: ///////////////////////////////////// TRIANGLE
7274 isOk = false; break;
7276 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7278 else { //////////////////////////////////// QUADRANGLE
7279 if ( nbUniqueNodes < 3 )
7281 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7282 isOk = false; // opposite nodes stick
7283 //MESSAGE("isOk " << isOk);
7286 case 6: ///////////////////////////////////// PENTAHEDRON
7287 if ( nbUniqueNodes == 4 ) {
7288 // ---------------------------------> tetrahedron
7290 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7291 // all top nodes stick: reverse a bottom
7292 uniqueNodes[ 0 ] = curNodes [ 1 ];
7293 uniqueNodes[ 1 ] = curNodes [ 0 ];
7295 else if (nbRepl == 3 &&
7296 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7297 // all bottom nodes stick: set a top before
7298 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7299 uniqueNodes[ 0 ] = curNodes [ 3 ];
7300 uniqueNodes[ 1 ] = curNodes [ 4 ];
7301 uniqueNodes[ 2 ] = curNodes [ 5 ];
7303 else if (nbRepl == 4 &&
7304 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7305 // a lateral face turns into a line: reverse a bottom
7306 uniqueNodes[ 0 ] = curNodes [ 1 ];
7307 uniqueNodes[ 1 ] = curNodes [ 0 ];
7312 else if ( nbUniqueNodes == 5 ) {
7313 // PENTAHEDRON --------------------> 2 tetrahedrons
7314 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7315 // a bottom node sticks with a linked top one
7317 SMDS_MeshElement* newElem =
7318 aMesh->AddVolume(curNodes[ 3 ],
7321 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7322 myLastCreatedElems.Append(newElem);
7324 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7325 // 2. : reverse a bottom
7326 uniqueNodes[ 0 ] = curNodes [ 1 ];
7327 uniqueNodes[ 1 ] = curNodes [ 0 ];
7337 if(elem->IsQuadratic()) { // Quadratic quadrangle
7349 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7352 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7354 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7355 uniqueNodes[0] = curNodes[0];
7356 uniqueNodes[1] = curNodes[2];
7357 uniqueNodes[2] = curNodes[3];
7358 uniqueNodes[3] = curNodes[5];
7359 uniqueNodes[4] = curNodes[6];
7360 uniqueNodes[5] = curNodes[7];
7363 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7364 uniqueNodes[0] = curNodes[0];
7365 uniqueNodes[1] = curNodes[1];
7366 uniqueNodes[2] = curNodes[2];
7367 uniqueNodes[3] = curNodes[4];
7368 uniqueNodes[4] = curNodes[5];
7369 uniqueNodes[5] = curNodes[6];
7372 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7373 uniqueNodes[0] = curNodes[1];
7374 uniqueNodes[1] = curNodes[2];
7375 uniqueNodes[2] = curNodes[3];
7376 uniqueNodes[3] = curNodes[5];
7377 uniqueNodes[4] = curNodes[6];
7378 uniqueNodes[5] = curNodes[0];
7381 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7382 uniqueNodes[0] = curNodes[0];
7383 uniqueNodes[1] = curNodes[1];
7384 uniqueNodes[2] = curNodes[3];
7385 uniqueNodes[3] = curNodes[4];
7386 uniqueNodes[4] = curNodes[6];
7387 uniqueNodes[5] = curNodes[7];
7390 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7391 uniqueNodes[0] = curNodes[0];
7392 uniqueNodes[1] = curNodes[2];
7393 uniqueNodes[2] = curNodes[3];
7394 uniqueNodes[3] = curNodes[1];
7395 uniqueNodes[4] = curNodes[6];
7396 uniqueNodes[5] = curNodes[7];
7399 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7400 uniqueNodes[0] = curNodes[0];
7401 uniqueNodes[1] = curNodes[1];
7402 uniqueNodes[2] = curNodes[2];
7403 uniqueNodes[3] = curNodes[4];
7404 uniqueNodes[4] = curNodes[5];
7405 uniqueNodes[5] = curNodes[7];
7408 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7409 uniqueNodes[0] = curNodes[0];
7410 uniqueNodes[1] = curNodes[1];
7411 uniqueNodes[2] = curNodes[3];
7412 uniqueNodes[3] = curNodes[4];
7413 uniqueNodes[4] = curNodes[2];
7414 uniqueNodes[5] = curNodes[7];
7417 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7418 uniqueNodes[0] = curNodes[0];
7419 uniqueNodes[1] = curNodes[1];
7420 uniqueNodes[2] = curNodes[2];
7421 uniqueNodes[3] = curNodes[4];
7422 uniqueNodes[4] = curNodes[5];
7423 uniqueNodes[5] = curNodes[3];
7428 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7431 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7435 //////////////////////////////////// HEXAHEDRON
7437 SMDS_VolumeTool hexa (elem);
7438 hexa.SetExternalNormal();
7439 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7440 //////////////////////// HEX ---> 1 tetrahedron
7441 for ( int iFace = 0; iFace < 6; iFace++ ) {
7442 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7443 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7444 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7445 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7446 // one face turns into a point ...
7447 int iOppFace = hexa.GetOppFaceIndex( iFace );
7448 ind = hexa.GetFaceNodesIndices( iOppFace );
7450 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7451 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7454 if ( nbStick == 1 ) {
7455 // ... and the opposite one - into a triangle.
7457 ind = hexa.GetFaceNodesIndices( iFace );
7458 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7465 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7466 //////////////////////// HEX ---> 1 prism
7467 int nbTria = 0, iTria[3];
7468 const int *ind; // indices of face nodes
7469 // look for triangular faces
7470 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7471 ind = hexa.GetFaceNodesIndices( iFace );
7472 TIDSortedNodeSet faceNodes;
7473 for ( iCur = 0; iCur < 4; iCur++ )
7474 faceNodes.insert( curNodes[ind[iCur]] );
7475 if ( faceNodes.size() == 3 )
7476 iTria[ nbTria++ ] = iFace;
7478 // check if triangles are opposite
7479 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7482 // set nodes of the bottom triangle
7483 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7485 for ( iCur = 0; iCur < 4; iCur++ )
7486 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7487 indB.push_back( ind[iCur] );
7488 if ( !hexa.IsForward() )
7489 std::swap( indB[0], indB[2] );
7490 for ( iCur = 0; iCur < 3; iCur++ )
7491 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7492 // set nodes of the top triangle
7493 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7494 for ( iCur = 0; iCur < 3; ++iCur )
7495 for ( int j = 0; j < 4; ++j )
7496 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7498 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7504 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7505 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7506 for ( int iFace = 0; iFace < 6; iFace++ ) {
7507 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7508 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7509 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7510 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7511 // one face turns into a point ...
7512 int iOppFace = hexa.GetOppFaceIndex( iFace );
7513 ind = hexa.GetFaceNodesIndices( iOppFace );
7515 iUnique = 2; // reverse a tetrahedron 1 bottom
7516 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7517 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7519 else if ( iUnique >= 0 )
7520 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7522 if ( nbStick == 0 ) {
7523 // ... and the opposite one is a quadrangle
7525 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7526 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7529 SMDS_MeshElement* newElem =
7530 aMesh->AddVolume(curNodes[ind[ 0 ]],
7533 curNodes[indTop[ 0 ]]);
7534 myLastCreatedElems.Append(newElem);
7536 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7543 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7544 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7545 // find indices of quad and tri faces
7546 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7547 for ( iFace = 0; iFace < 6; iFace++ ) {
7548 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7550 for ( iCur = 0; iCur < 4; iCur++ )
7551 nodeSet.insert( curNodes[ind[ iCur ]] );
7552 nbUniqueNodes = nodeSet.size();
7553 if ( nbUniqueNodes == 3 )
7554 iTriFace[ nbTri++ ] = iFace;
7555 else if ( nbUniqueNodes == 4 )
7556 iQuadFace[ nbQuad++ ] = iFace;
7558 if (nbQuad == 2 && nbTri == 4 &&
7559 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7560 // 2 opposite quadrangles stuck with a diagonal;
7561 // sample groups of merged indices: (0-4)(2-6)
7562 // --------------------------------------------> 2 tetrahedrons
7563 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7564 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7565 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7566 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7567 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7568 // stuck with 0-2 diagonal
7576 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7577 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7578 // stuck with 1-3 diagonal
7590 uniqueNodes[ 0 ] = curNodes [ i0 ];
7591 uniqueNodes[ 1 ] = curNodes [ i1d ];
7592 uniqueNodes[ 2 ] = curNodes [ i3d ];
7593 uniqueNodes[ 3 ] = curNodes [ i0t ];
7596 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7600 myLastCreatedElems.Append(newElem);
7602 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7605 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7606 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7607 // --------------------------------------------> prism
7608 // find 2 opposite triangles
7610 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7611 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7612 // find indices of kept and replaced nodes
7613 // and fill unique nodes of 2 opposite triangles
7614 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7615 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7616 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7617 // fill unique nodes
7620 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7621 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7622 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7624 // iCur of a linked node of the opposite face (make normals co-directed):
7625 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7626 // check that correspondent corners of triangles are linked
7627 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7630 uniqueNodes[ iUnique ] = n;
7631 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7640 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7643 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7650 } // switch ( nbNodes )
7652 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7654 if ( isOk ) { // the elem remains valid after sticking nodes
7655 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7657 // Change nodes of polyedre
7658 const SMDS_VtkVolume* aPolyedre =
7659 dynamic_cast<const SMDS_VtkVolume*>( elem );
7661 int nbFaces = aPolyedre->NbFaces();
7663 vector<const SMDS_MeshNode *> poly_nodes;
7664 vector<int> quantities (nbFaces);
7666 for (int iface = 1; iface <= nbFaces; iface++) {
7667 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7668 quantities[iface - 1] = nbFaceNodes;
7670 for (inode = 1; inode <= nbFaceNodes; inode++) {
7671 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7673 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7674 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7675 curNode = (*nnIt).second;
7677 poly_nodes.push_back(curNode);
7680 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7683 else // replace non-polyhedron elements
7685 const SMDSAbs_ElementType etyp = elem->GetType();
7686 const int elemId = elem->GetID();
7687 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7688 uniqueNodes.resize(nbUniqueNodes);
7690 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7692 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7693 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7694 if ( sm && newElem )
7695 sm->AddElement( newElem );
7696 if ( elem != newElem )
7697 ReplaceElemInGroups( elem, newElem, aMesh );
7701 // Remove invalid regular element or invalid polygon
7702 rmElemIds.push_back( elem->GetID() );
7705 } // loop on elements
7707 // Remove bad elements, then equal nodes (order important)
7709 Remove( rmElemIds, false );
7710 Remove( rmNodeIds, true );
7715 // ========================================================
7716 // class : SortableElement
7717 // purpose : allow sorting elements basing on their nodes
7718 // ========================================================
7719 class SortableElement : public set <const SMDS_MeshElement*>
7723 SortableElement( const SMDS_MeshElement* theElem )
7726 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7727 while ( nodeIt->more() )
7728 this->insert( nodeIt->next() );
7731 const SMDS_MeshElement* Get() const
7734 void Set(const SMDS_MeshElement* e) const
7739 mutable const SMDS_MeshElement* myElem;
7742 //=======================================================================
7743 //function : FindEqualElements
7744 //purpose : Return list of group of elements built on the same nodes.
7745 // Search among theElements or in the whole mesh if theElements is empty
7746 //=======================================================================
7747 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7748 TListOfListOfElementsID & theGroupsOfElementsID)
7750 myLastCreatedElems.Clear();
7751 myLastCreatedNodes.Clear();
7753 typedef set<const SMDS_MeshElement*> TElemsSet;
7754 typedef map< SortableElement, int > TMapOfNodeSet;
7755 typedef list<int> TGroupOfElems;
7758 if ( theElements.empty() )
7759 { // get all elements in the mesh
7760 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7761 while ( eIt->more() )
7762 elems.insert( elems.end(), eIt->next());
7765 elems = theElements;
7767 vector< TGroupOfElems > arrayOfGroups;
7768 TGroupOfElems groupOfElems;
7769 TMapOfNodeSet mapOfNodeSet;
7771 TElemsSet::iterator elemIt = elems.begin();
7772 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
7773 const SMDS_MeshElement* curElem = *elemIt;
7774 SortableElement SE(curElem);
7777 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7778 if( !(pp.second) ) {
7779 TMapOfNodeSet::iterator& itSE = pp.first;
7780 ind = (*itSE).second;
7781 arrayOfGroups[ind].push_back(curElem->GetID());
7784 groupOfElems.clear();
7785 groupOfElems.push_back(curElem->GetID());
7786 arrayOfGroups.push_back(groupOfElems);
7791 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7792 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7793 groupOfElems = *groupIt;
7794 if ( groupOfElems.size() > 1 ) {
7795 groupOfElems.sort();
7796 theGroupsOfElementsID.push_back(groupOfElems);
7801 //=======================================================================
7802 //function : MergeElements
7803 //purpose : In each given group, substitute all elements by the first one.
7804 //=======================================================================
7806 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7808 myLastCreatedElems.Clear();
7809 myLastCreatedNodes.Clear();
7811 typedef list<int> TListOfIDs;
7812 TListOfIDs rmElemIds; // IDs of elems to remove
7814 SMESHDS_Mesh* aMesh = GetMeshDS();
7816 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7817 while ( groupsIt != theGroupsOfElementsID.end() ) {
7818 TListOfIDs& aGroupOfElemID = *groupsIt;
7819 aGroupOfElemID.sort();
7820 int elemIDToKeep = aGroupOfElemID.front();
7821 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7822 aGroupOfElemID.pop_front();
7823 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7824 while ( idIt != aGroupOfElemID.end() ) {
7825 int elemIDToRemove = *idIt;
7826 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7827 // add the kept element in groups of removed one (PAL15188)
7828 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7829 rmElemIds.push_back( elemIDToRemove );
7835 Remove( rmElemIds, false );
7838 //=======================================================================
7839 //function : MergeEqualElements
7840 //purpose : Remove all but one of elements built on the same nodes.
7841 //=======================================================================
7843 void SMESH_MeshEditor::MergeEqualElements()
7845 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
7846 to merge equal elements in the whole mesh */
7847 TListOfListOfElementsID aGroupsOfElementsID;
7848 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7849 MergeElements(aGroupsOfElementsID);
7852 //=======================================================================
7853 //function : FindFaceInSet
7854 //purpose : Return a face having linked nodes n1 and n2 and which is
7855 // - not in avoidSet,
7856 // - in elemSet provided that !elemSet.empty()
7857 // i1 and i2 optionally returns indices of n1 and n2
7858 //=======================================================================
7860 const SMDS_MeshElement*
7861 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
7862 const SMDS_MeshNode* n2,
7863 const TIDSortedElemSet& elemSet,
7864 const TIDSortedElemSet& avoidSet,
7870 const SMDS_MeshElement* face = 0;
7872 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
7873 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
7874 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
7876 //MESSAGE("in while ( invElemIt->more() && !face )");
7877 const SMDS_MeshElement* elem = invElemIt->next();
7878 if (avoidSet.count( elem ))
7880 if ( !elemSet.empty() && !elemSet.count( elem ))
7883 i1 = elem->GetNodeIndex( n1 );
7884 // find a n2 linked to n1
7885 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
7886 for ( int di = -1; di < 2 && !face; di += 2 )
7888 i2 = (i1+di+nbN) % nbN;
7889 if ( elem->GetNode( i2 ) == n2 )
7892 if ( !face && elem->IsQuadratic())
7894 // analysis for quadratic elements using all nodes
7895 const SMDS_VtkFace* F =
7896 dynamic_cast<const SMDS_VtkFace*>(elem);
7897 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7898 // use special nodes iterator
7899 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7900 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
7901 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
7903 const SMDS_MeshNode* n = cast2Node( anIter->next() );
7904 if ( n1 == prevN && n2 == n )
7908 else if ( n2 == prevN && n1 == n )
7910 face = elem; swap( i1, i2 );
7916 if ( n1ind ) *n1ind = i1;
7917 if ( n2ind ) *n2ind = i2;
7921 //=======================================================================
7922 //function : findAdjacentFace
7924 //=======================================================================
7926 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7927 const SMDS_MeshNode* n2,
7928 const SMDS_MeshElement* elem)
7930 TIDSortedElemSet elemSet, avoidSet;
7932 avoidSet.insert ( elem );
7933 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
7936 //=======================================================================
7937 //function : FindFreeBorder
7939 //=======================================================================
7941 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7943 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7944 const SMDS_MeshNode* theSecondNode,
7945 const SMDS_MeshNode* theLastNode,
7946 list< const SMDS_MeshNode* > & theNodes,
7947 list< const SMDS_MeshElement* >& theFaces)
7949 if ( !theFirstNode || !theSecondNode )
7951 // find border face between theFirstNode and theSecondNode
7952 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7956 theFaces.push_back( curElem );
7957 theNodes.push_back( theFirstNode );
7958 theNodes.push_back( theSecondNode );
7960 //vector<const SMDS_MeshNode*> nodes;
7961 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7962 TIDSortedElemSet foundElems;
7963 bool needTheLast = ( theLastNode != 0 );
7965 while ( nStart != theLastNode ) {
7966 if ( nStart == theFirstNode )
7967 return !needTheLast;
7969 // find all free border faces sharing form nStart
7971 list< const SMDS_MeshElement* > curElemList;
7972 list< const SMDS_MeshNode* > nStartList;
7973 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7974 while ( invElemIt->more() ) {
7975 const SMDS_MeshElement* e = invElemIt->next();
7976 if ( e == curElem || foundElems.insert( e ).second ) {
7978 int iNode = 0, nbNodes = e->NbNodes();
7979 //const SMDS_MeshNode* nodes[nbNodes+1];
7980 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7982 if(e->IsQuadratic()) {
7983 const SMDS_VtkFace* F =
7984 dynamic_cast<const SMDS_VtkFace*>(e);
7985 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7986 // use special nodes iterator
7987 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7988 while( anIter->more() ) {
7989 nodes[ iNode++ ] = cast2Node(anIter->next());
7993 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7994 while ( nIt->more() )
7995 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7997 nodes[ iNode ] = nodes[ 0 ];
7999 for ( iNode = 0; iNode < nbNodes; iNode++ )
8000 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8001 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8002 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8004 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8005 curElemList.push_back( e );
8009 // analyse the found
8011 int nbNewBorders = curElemList.size();
8012 if ( nbNewBorders == 0 ) {
8013 // no free border furthermore
8014 return !needTheLast;
8016 else if ( nbNewBorders == 1 ) {
8017 // one more element found
8019 nStart = nStartList.front();
8020 curElem = curElemList.front();
8021 theFaces.push_back( curElem );
8022 theNodes.push_back( nStart );
8025 // several continuations found
8026 list< const SMDS_MeshElement* >::iterator curElemIt;
8027 list< const SMDS_MeshNode* >::iterator nStartIt;
8028 // check if one of them reached the last node
8029 if ( needTheLast ) {
8030 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8031 curElemIt!= curElemList.end();
8032 curElemIt++, nStartIt++ )
8033 if ( *nStartIt == theLastNode ) {
8034 theFaces.push_back( *curElemIt );
8035 theNodes.push_back( *nStartIt );
8039 // find the best free border by the continuations
8040 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8041 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8042 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8043 curElemIt!= curElemList.end();
8044 curElemIt++, nStartIt++ )
8046 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8047 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8048 // find one more free border
8049 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8053 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8054 // choice: clear a worse one
8055 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8056 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8057 contNodes[ iWorse ].clear();
8058 contFaces[ iWorse ].clear();
8061 if ( contNodes[0].empty() && contNodes[1].empty() )
8064 // append the best free border
8065 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8066 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8067 theNodes.pop_back(); // remove nIgnore
8068 theNodes.pop_back(); // remove nStart
8069 theFaces.pop_back(); // remove curElem
8070 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8071 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8072 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8073 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8076 } // several continuations found
8077 } // while ( nStart != theLastNode )
8082 //=======================================================================
8083 //function : CheckFreeBorderNodes
8084 //purpose : Return true if the tree nodes are on a free border
8085 //=======================================================================
8087 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8088 const SMDS_MeshNode* theNode2,
8089 const SMDS_MeshNode* theNode3)
8091 list< const SMDS_MeshNode* > nodes;
8092 list< const SMDS_MeshElement* > faces;
8093 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8096 //=======================================================================
8097 //function : SewFreeBorder
8099 //=======================================================================
8101 SMESH_MeshEditor::Sew_Error
8102 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8103 const SMDS_MeshNode* theBordSecondNode,
8104 const SMDS_MeshNode* theBordLastNode,
8105 const SMDS_MeshNode* theSideFirstNode,
8106 const SMDS_MeshNode* theSideSecondNode,
8107 const SMDS_MeshNode* theSideThirdNode,
8108 const bool theSideIsFreeBorder,
8109 const bool toCreatePolygons,
8110 const bool toCreatePolyedrs)
8112 myLastCreatedElems.Clear();
8113 myLastCreatedNodes.Clear();
8115 MESSAGE("::SewFreeBorder()");
8116 Sew_Error aResult = SEW_OK;
8118 // ====================================
8119 // find side nodes and elements
8120 // ====================================
8122 list< const SMDS_MeshNode* > nSide[ 2 ];
8123 list< const SMDS_MeshElement* > eSide[ 2 ];
8124 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8125 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8129 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8130 nSide[0], eSide[0])) {
8131 MESSAGE(" Free Border 1 not found " );
8132 aResult = SEW_BORDER1_NOT_FOUND;
8134 if (theSideIsFreeBorder) {
8137 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8138 nSide[1], eSide[1])) {
8139 MESSAGE(" Free Border 2 not found " );
8140 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8143 if ( aResult != SEW_OK )
8146 if (!theSideIsFreeBorder) {
8150 // -------------------------------------------------------------------------
8152 // 1. If nodes to merge are not coincident, move nodes of the free border
8153 // from the coord sys defined by the direction from the first to last
8154 // nodes of the border to the correspondent sys of the side 2
8155 // 2. On the side 2, find the links most co-directed with the correspondent
8156 // links of the free border
8157 // -------------------------------------------------------------------------
8159 // 1. Since sewing may break if there are volumes to split on the side 2,
8160 // we wont move nodes but just compute new coordinates for them
8161 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8162 TNodeXYZMap nBordXYZ;
8163 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8164 list< const SMDS_MeshNode* >::iterator nBordIt;
8166 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8167 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8168 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8169 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8170 double tol2 = 1.e-8;
8171 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8172 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8173 // Need node movement.
8175 // find X and Z axes to create trsf
8176 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8178 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8180 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8183 gp_Ax3 toBordAx( Pb1, Zb, X );
8184 gp_Ax3 fromSideAx( Ps1, Zs, X );
8185 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8187 gp_Trsf toBordSys, fromSide2Sys;
8188 toBordSys.SetTransformation( toBordAx );
8189 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8190 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8193 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8194 const SMDS_MeshNode* n = *nBordIt;
8195 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8196 toBordSys.Transforms( xyz );
8197 fromSide2Sys.Transforms( xyz );
8198 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8202 // just insert nodes XYZ in the nBordXYZ map
8203 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8204 const SMDS_MeshNode* n = *nBordIt;
8205 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8209 // 2. On the side 2, find the links most co-directed with the correspondent
8210 // links of the free border
8212 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8213 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8214 sideNodes.push_back( theSideFirstNode );
8216 bool hasVolumes = false;
8217 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8218 set<long> foundSideLinkIDs, checkedLinkIDs;
8219 SMDS_VolumeTool volume;
8220 //const SMDS_MeshNode* faceNodes[ 4 ];
8222 const SMDS_MeshNode* sideNode;
8223 const SMDS_MeshElement* sideElem;
8224 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8225 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8226 nBordIt = bordNodes.begin();
8228 // border node position and border link direction to compare with
8229 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8230 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8231 // choose next side node by link direction or by closeness to
8232 // the current border node:
8233 bool searchByDir = ( *nBordIt != theBordLastNode );
8235 // find the next node on the Side 2
8237 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8239 checkedLinkIDs.clear();
8240 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8242 // loop on inverse elements of current node (prevSideNode) on the Side 2
8243 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8244 while ( invElemIt->more() )
8246 const SMDS_MeshElement* elem = invElemIt->next();
8247 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8248 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8249 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8250 bool isVolume = volume.Set( elem );
8251 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8252 if ( isVolume ) // --volume
8254 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8255 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8256 if(elem->IsQuadratic()) {
8257 const SMDS_VtkFace* F =
8258 dynamic_cast<const SMDS_VtkFace*>(elem);
8259 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8260 // use special nodes iterator
8261 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8262 while( anIter->more() ) {
8263 nodes[ iNode ] = cast2Node(anIter->next());
8264 if ( nodes[ iNode++ ] == prevSideNode )
8265 iPrevNode = iNode - 1;
8269 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8270 while ( nIt->more() ) {
8271 nodes[ iNode ] = cast2Node( nIt->next() );
8272 if ( nodes[ iNode++ ] == prevSideNode )
8273 iPrevNode = iNode - 1;
8276 // there are 2 links to check
8281 // loop on links, to be precise, on the second node of links
8282 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8283 const SMDS_MeshNode* n = nodes[ iNode ];
8285 if ( !volume.IsLinked( n, prevSideNode ))
8289 if ( iNode ) // a node before prevSideNode
8290 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8291 else // a node after prevSideNode
8292 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8294 // check if this link was already used
8295 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8296 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8297 if (!isJustChecked &&
8298 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8300 // test a link geometrically
8301 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8302 bool linkIsBetter = false;
8303 double dot = 0.0, dist = 0.0;
8304 if ( searchByDir ) { // choose most co-directed link
8305 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8306 linkIsBetter = ( dot > maxDot );
8308 else { // choose link with the node closest to bordPos
8309 dist = ( nextXYZ - bordPos ).SquareModulus();
8310 linkIsBetter = ( dist < minDist );
8312 if ( linkIsBetter ) {
8321 } // loop on inverse elements of prevSideNode
8324 MESSAGE(" Cant find path by links of the Side 2 ");
8325 return SEW_BAD_SIDE_NODES;
8327 sideNodes.push_back( sideNode );
8328 sideElems.push_back( sideElem );
8329 foundSideLinkIDs.insert ( linkID );
8330 prevSideNode = sideNode;
8332 if ( *nBordIt == theBordLastNode )
8333 searchByDir = false;
8335 // find the next border link to compare with
8336 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8337 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8338 // move to next border node if sideNode is before forward border node (bordPos)
8339 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8340 prevBordNode = *nBordIt;
8342 bordPos = nBordXYZ[ *nBordIt ];
8343 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8344 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8348 while ( sideNode != theSideSecondNode );
8350 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8351 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8352 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8354 } // end nodes search on the side 2
8356 // ============================
8357 // sew the border to the side 2
8358 // ============================
8360 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8361 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8363 TListOfListOfNodes nodeGroupsToMerge;
8364 if ( nbNodes[0] == nbNodes[1] ||
8365 ( theSideIsFreeBorder && !theSideThirdNode)) {
8367 // all nodes are to be merged
8369 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8370 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8371 nIt[0]++, nIt[1]++ )
8373 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8374 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8375 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8380 // insert new nodes into the border and the side to get equal nb of segments
8382 // get normalized parameters of nodes on the borders
8383 //double param[ 2 ][ maxNbNodes ];
8385 param[0] = new double [ maxNbNodes ];
8386 param[1] = new double [ maxNbNodes ];
8388 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8389 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8390 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8391 const SMDS_MeshNode* nPrev = *nIt;
8392 double bordLength = 0;
8393 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8394 const SMDS_MeshNode* nCur = *nIt;
8395 gp_XYZ segment (nCur->X() - nPrev->X(),
8396 nCur->Y() - nPrev->Y(),
8397 nCur->Z() - nPrev->Z());
8398 double segmentLen = segment.Modulus();
8399 bordLength += segmentLen;
8400 param[ iBord ][ iNode ] = bordLength;
8403 // normalize within [0,1]
8404 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8405 param[ iBord ][ iNode ] /= bordLength;
8409 // loop on border segments
8410 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8411 int i[ 2 ] = { 0, 0 };
8412 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8413 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8415 TElemOfNodeListMap insertMap;
8416 TElemOfNodeListMap::iterator insertMapIt;
8418 // key: elem to insert nodes into
8419 // value: 2 nodes to insert between + nodes to be inserted
8421 bool next[ 2 ] = { false, false };
8423 // find min adjacent segment length after sewing
8424 double nextParam = 10., prevParam = 0;
8425 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8426 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8427 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8428 if ( i[ iBord ] > 0 )
8429 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8431 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8432 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8433 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8435 // choose to insert or to merge nodes
8436 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8437 if ( Abs( du ) <= minSegLen * 0.2 ) {
8440 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8441 const SMDS_MeshNode* n0 = *nIt[0];
8442 const SMDS_MeshNode* n1 = *nIt[1];
8443 nodeGroupsToMerge.back().push_back( n1 );
8444 nodeGroupsToMerge.back().push_back( n0 );
8445 // position of node of the border changes due to merge
8446 param[ 0 ][ i[0] ] += du;
8447 // move n1 for the sake of elem shape evaluation during insertion.
8448 // n1 will be removed by MergeNodes() anyway
8449 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8450 next[0] = next[1] = true;
8455 int intoBord = ( du < 0 ) ? 0 : 1;
8456 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8457 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8458 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8459 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8460 if ( intoBord == 1 ) {
8461 // move node of the border to be on a link of elem of the side
8462 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8463 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8464 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8465 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8466 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8468 insertMapIt = insertMap.find( elem );
8469 bool notFound = ( insertMapIt == insertMap.end() );
8470 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8472 // insert into another link of the same element:
8473 // 1. perform insertion into the other link of the elem
8474 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8475 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8476 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8477 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8478 // 2. perform insertion into the link of adjacent faces
8480 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8482 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8486 if (toCreatePolyedrs) {
8487 // perform insertion into the links of adjacent volumes
8488 UpdateVolumes(n12, n22, nodeList);
8490 // 3. find an element appeared on n1 and n2 after the insertion
8491 insertMap.erase( elem );
8492 elem = findAdjacentFace( n1, n2, 0 );
8494 if ( notFound || otherLink ) {
8495 // add element and nodes of the side into the insertMap
8496 insertMapIt = insertMap.insert
8497 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8498 (*insertMapIt).second.push_back( n1 );
8499 (*insertMapIt).second.push_back( n2 );
8501 // add node to be inserted into elem
8502 (*insertMapIt).second.push_back( nIns );
8503 next[ 1 - intoBord ] = true;
8506 // go to the next segment
8507 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8508 if ( next[ iBord ] ) {
8509 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8511 nPrev[ iBord ] = *nIt[ iBord ];
8512 nIt[ iBord ]++; i[ iBord ]++;
8516 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8518 // perform insertion of nodes into elements
8520 for (insertMapIt = insertMap.begin();
8521 insertMapIt != insertMap.end();
8524 const SMDS_MeshElement* elem = (*insertMapIt).first;
8525 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8526 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8527 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8529 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8531 if ( !theSideIsFreeBorder ) {
8532 // look for and insert nodes into the faces adjacent to elem
8534 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8536 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8541 if (toCreatePolyedrs) {
8542 // perform insertion into the links of adjacent volumes
8543 UpdateVolumes(n1, n2, nodeList);
8549 } // end: insert new nodes
8551 MergeNodes ( nodeGroupsToMerge );
8556 //=======================================================================
8557 //function : InsertNodesIntoLink
8558 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8559 // and theBetweenNode2 and split theElement
8560 //=======================================================================
8562 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8563 const SMDS_MeshNode* theBetweenNode1,
8564 const SMDS_MeshNode* theBetweenNode2,
8565 list<const SMDS_MeshNode*>& theNodesToInsert,
8566 const bool toCreatePoly)
8568 if ( theFace->GetType() != SMDSAbs_Face ) return;
8570 // find indices of 2 link nodes and of the rest nodes
8571 int iNode = 0, il1, il2, i3, i4;
8572 il1 = il2 = i3 = i4 = -1;
8573 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8574 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8576 if(theFace->IsQuadratic()) {
8577 const SMDS_VtkFace* F =
8578 dynamic_cast<const SMDS_VtkFace*>(theFace);
8579 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8580 // use special nodes iterator
8581 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8582 while( anIter->more() ) {
8583 const SMDS_MeshNode* n = cast2Node(anIter->next());
8584 if ( n == theBetweenNode1 )
8586 else if ( n == theBetweenNode2 )
8592 nodes[ iNode++ ] = n;
8596 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8597 while ( nodeIt->more() ) {
8598 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8599 if ( n == theBetweenNode1 )
8601 else if ( n == theBetweenNode2 )
8607 nodes[ iNode++ ] = n;
8610 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8613 // arrange link nodes to go one after another regarding the face orientation
8614 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8615 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8620 aNodesToInsert.reverse();
8622 // check that not link nodes of a quadrangles are in good order
8623 int nbFaceNodes = theFace->NbNodes();
8624 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8630 if (toCreatePoly || theFace->IsPoly()) {
8633 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8635 // add nodes of face up to first node of link
8638 if(theFace->IsQuadratic()) {
8639 const SMDS_VtkFace* F =
8640 dynamic_cast<const SMDS_VtkFace*>(theFace);
8641 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8642 // use special nodes iterator
8643 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8644 while( anIter->more() && !isFLN ) {
8645 const SMDS_MeshNode* n = cast2Node(anIter->next());
8646 poly_nodes[iNode++] = n;
8647 if (n == nodes[il1]) {
8651 // add nodes to insert
8652 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8653 for (; nIt != aNodesToInsert.end(); nIt++) {
8654 poly_nodes[iNode++] = *nIt;
8656 // add nodes of face starting from last node of link
8657 while ( anIter->more() ) {
8658 poly_nodes[iNode++] = cast2Node(anIter->next());
8662 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8663 while ( nodeIt->more() && !isFLN ) {
8664 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8665 poly_nodes[iNode++] = n;
8666 if (n == nodes[il1]) {
8670 // add nodes to insert
8671 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8672 for (; nIt != aNodesToInsert.end(); nIt++) {
8673 poly_nodes[iNode++] = *nIt;
8675 // add nodes of face starting from last node of link
8676 while ( nodeIt->more() ) {
8677 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8678 poly_nodes[iNode++] = n;
8682 // edit or replace the face
8683 SMESHDS_Mesh *aMesh = GetMeshDS();
8685 if (theFace->IsPoly()) {
8686 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8689 int aShapeId = FindShape( theFace );
8691 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8692 myLastCreatedElems.Append(newElem);
8693 if ( aShapeId && newElem )
8694 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8696 aMesh->RemoveElement(theFace);
8701 SMESHDS_Mesh *aMesh = GetMeshDS();
8702 if( !theFace->IsQuadratic() ) {
8704 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8705 int nbLinkNodes = 2 + aNodesToInsert.size();
8706 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8707 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8708 linkNodes[ 0 ] = nodes[ il1 ];
8709 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8710 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8711 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8712 linkNodes[ iNode++ ] = *nIt;
8714 // decide how to split a quadrangle: compare possible variants
8715 // and choose which of splits to be a quadrangle
8716 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8717 if ( nbFaceNodes == 3 ) {
8718 iBestQuad = nbSplits;
8721 else if ( nbFaceNodes == 4 ) {
8722 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8723 double aBestRate = DBL_MAX;
8724 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8726 double aBadRate = 0;
8727 // evaluate elements quality
8728 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8729 if ( iSplit == iQuad ) {
8730 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8734 aBadRate += getBadRate( &quad, aCrit );
8737 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8739 nodes[ iSplit < iQuad ? i4 : i3 ]);
8740 aBadRate += getBadRate( &tria, aCrit );
8744 if ( aBadRate < aBestRate ) {
8746 aBestRate = aBadRate;
8751 // create new elements
8752 int aShapeId = FindShape( theFace );
8755 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8756 SMDS_MeshElement* newElem = 0;
8757 if ( iSplit == iBestQuad )
8758 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8763 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8765 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8766 myLastCreatedElems.Append(newElem);
8767 if ( aShapeId && newElem )
8768 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8771 // change nodes of theFace
8772 const SMDS_MeshNode* newNodes[ 4 ];
8773 newNodes[ 0 ] = linkNodes[ i1 ];
8774 newNodes[ 1 ] = linkNodes[ i2 ];
8775 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8776 newNodes[ 3 ] = nodes[ i4 ];
8777 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8778 const SMDS_MeshElement* newElem = 0;
8779 if (iSplit == iBestQuad)
8780 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8782 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8783 myLastCreatedElems.Append(newElem);
8784 if ( aShapeId && newElem )
8785 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8786 } // end if(!theFace->IsQuadratic())
8787 else { // theFace is quadratic
8788 // we have to split theFace on simple triangles and one simple quadrangle
8790 int nbshift = tmp*2;
8791 // shift nodes in nodes[] by nbshift
8793 for(i=0; i<nbshift; i++) {
8794 const SMDS_MeshNode* n = nodes[0];
8795 for(j=0; j<nbFaceNodes-1; j++) {
8796 nodes[j] = nodes[j+1];
8798 nodes[nbFaceNodes-1] = n;
8800 il1 = il1 - nbshift;
8801 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8802 // n0 n1 n2 n0 n1 n2
8803 // +-----+-----+ +-----+-----+
8812 // create new elements
8813 int aShapeId = FindShape( theFace );
8816 if(nbFaceNodes==6) { // quadratic triangle
8817 SMDS_MeshElement* newElem =
8818 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8819 myLastCreatedElems.Append(newElem);
8820 if ( aShapeId && newElem )
8821 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8822 if(theFace->IsMediumNode(nodes[il1])) {
8823 // create quadrangle
8824 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8825 myLastCreatedElems.Append(newElem);
8826 if ( aShapeId && newElem )
8827 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8833 // create quadrangle
8834 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8835 myLastCreatedElems.Append(newElem);
8836 if ( aShapeId && newElem )
8837 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8843 else { // nbFaceNodes==8 - quadratic quadrangle
8844 SMDS_MeshElement* newElem =
8845 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8846 myLastCreatedElems.Append(newElem);
8847 if ( aShapeId && newElem )
8848 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8849 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8850 myLastCreatedElems.Append(newElem);
8851 if ( aShapeId && newElem )
8852 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8853 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8854 myLastCreatedElems.Append(newElem);
8855 if ( aShapeId && newElem )
8856 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8857 if(theFace->IsMediumNode(nodes[il1])) {
8858 // create quadrangle
8859 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8860 myLastCreatedElems.Append(newElem);
8861 if ( aShapeId && newElem )
8862 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8868 // create quadrangle
8869 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8870 myLastCreatedElems.Append(newElem);
8871 if ( aShapeId && newElem )
8872 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8878 // create needed triangles using n1,n2,n3 and inserted nodes
8879 int nbn = 2 + aNodesToInsert.size();
8880 //const SMDS_MeshNode* aNodes[nbn];
8881 vector<const SMDS_MeshNode*> aNodes(nbn);
8882 aNodes[0] = nodes[n1];
8883 aNodes[nbn-1] = nodes[n2];
8884 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8885 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8886 aNodes[iNode++] = *nIt;
8888 for(i=1; i<nbn; i++) {
8889 SMDS_MeshElement* newElem =
8890 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8891 myLastCreatedElems.Append(newElem);
8892 if ( aShapeId && newElem )
8893 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8897 aMesh->RemoveElement(theFace);
8900 //=======================================================================
8901 //function : UpdateVolumes
8903 //=======================================================================
8904 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8905 const SMDS_MeshNode* theBetweenNode2,
8906 list<const SMDS_MeshNode*>& theNodesToInsert)
8908 myLastCreatedElems.Clear();
8909 myLastCreatedNodes.Clear();
8911 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8912 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8913 const SMDS_MeshElement* elem = invElemIt->next();
8915 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8916 SMDS_VolumeTool aVolume (elem);
8917 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8920 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8921 int iface, nbFaces = aVolume.NbFaces();
8922 vector<const SMDS_MeshNode *> poly_nodes;
8923 vector<int> quantities (nbFaces);
8925 for (iface = 0; iface < nbFaces; iface++) {
8926 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8927 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8928 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8930 for (int inode = 0; inode < nbFaceNodes; inode++) {
8931 poly_nodes.push_back(faceNodes[inode]);
8933 if (nbInserted == 0) {
8934 if (faceNodes[inode] == theBetweenNode1) {
8935 if (faceNodes[inode + 1] == theBetweenNode2) {
8936 nbInserted = theNodesToInsert.size();
8938 // add nodes to insert
8939 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8940 for (; nIt != theNodesToInsert.end(); nIt++) {
8941 poly_nodes.push_back(*nIt);
8945 else if (faceNodes[inode] == theBetweenNode2) {
8946 if (faceNodes[inode + 1] == theBetweenNode1) {
8947 nbInserted = theNodesToInsert.size();
8949 // add nodes to insert in reversed order
8950 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8952 for (; nIt != theNodesToInsert.begin(); nIt--) {
8953 poly_nodes.push_back(*nIt);
8955 poly_nodes.push_back(*nIt);
8962 quantities[iface] = nbFaceNodes + nbInserted;
8965 // Replace or update the volume
8966 SMESHDS_Mesh *aMesh = GetMeshDS();
8968 if (elem->IsPoly()) {
8969 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8973 int aShapeId = FindShape( elem );
8975 SMDS_MeshElement* newElem =
8976 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8977 myLastCreatedElems.Append(newElem);
8978 if (aShapeId && newElem)
8979 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8981 aMesh->RemoveElement(elem);
8988 //================================================================================
8990 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8992 //================================================================================
8994 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8995 vector<const SMDS_MeshNode *> & nodes,
8996 vector<int> & nbNodeInFaces )
8999 nbNodeInFaces.clear();
9000 SMDS_VolumeTool vTool ( elem );
9001 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9003 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9004 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9005 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9010 //=======================================================================
9012 * \brief Convert elements contained in a submesh to quadratic
9013 * \return int - nb of checked elements
9015 //=======================================================================
9017 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9018 SMESH_MesherHelper& theHelper,
9019 const bool theForce3d)
9022 if( !theSm ) return nbElem;
9024 vector<int> nbNodeInFaces;
9025 vector<const SMDS_MeshNode *> nodes;
9026 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9027 while(ElemItr->more())
9030 const SMDS_MeshElement* elem = ElemItr->next();
9031 if( !elem || elem->IsQuadratic() ) continue;
9033 // get elem data needed to re-create it
9035 const int id = elem->GetID();
9036 const int nbNodes = elem->NbNodes();
9037 const SMDSAbs_ElementType aType = elem->GetType();
9038 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9039 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9040 if ( aGeomType == SMDSEntity_Polyhedra )
9041 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9042 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9043 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9045 // remove a linear element
9046 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9048 const SMDS_MeshElement* NewElem = 0;
9054 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9062 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9065 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9068 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9073 case SMDSAbs_Volume :
9077 case SMDSEntity_Tetra:
9078 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9080 case SMDSEntity_Pyramid:
9081 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9083 case SMDSEntity_Penta:
9084 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9086 case SMDSEntity_Hexa:
9087 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9088 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9090 case SMDSEntity_Hexagonal_Prism:
9092 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9099 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9101 theSm->AddElement( NewElem );
9106 //=======================================================================
9107 //function : ConvertToQuadratic
9109 //=======================================================================
9111 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9113 SMESHDS_Mesh* meshDS = GetMeshDS();
9115 SMESH_MesherHelper aHelper(*myMesh);
9116 aHelper.SetIsQuadratic( true );
9118 int nbCheckedElems = 0;
9119 if ( myMesh->HasShapeToMesh() )
9121 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9123 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9124 while ( smIt->more() ) {
9125 SMESH_subMesh* sm = smIt->next();
9126 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9127 aHelper.SetSubShape( sm->GetSubShape() );
9128 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9133 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9134 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9136 SMESHDS_SubMesh *smDS = 0;
9137 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9138 while(aEdgeItr->more())
9140 const SMDS_MeshEdge* edge = aEdgeItr->next();
9141 if(edge && !edge->IsQuadratic())
9143 int id = edge->GetID();
9144 //MESSAGE("edge->GetID() " << id);
9145 const SMDS_MeshNode* n1 = edge->GetNode(0);
9146 const SMDS_MeshNode* n2 = edge->GetNode(1);
9148 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9150 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9151 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9154 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9155 while(aFaceItr->more())
9157 const SMDS_MeshFace* face = aFaceItr->next();
9158 if(!face || face->IsQuadratic() ) continue;
9160 const int id = face->GetID();
9161 const SMDSAbs_EntityType type = face->GetEntityType();
9162 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9164 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9166 SMDS_MeshFace * NewFace = 0;
9169 case SMDSEntity_Triangle:
9170 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9172 case SMDSEntity_Quadrangle:
9173 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9176 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9178 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9180 vector<int> nbNodeInFaces;
9181 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9182 while(aVolumeItr->more())
9184 const SMDS_MeshVolume* volume = aVolumeItr->next();
9185 if(!volume || volume->IsQuadratic() ) continue;
9187 const int id = volume->GetID();
9188 const SMDSAbs_EntityType type = volume->GetEntityType();
9189 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9190 if ( type == SMDSEntity_Polyhedra )
9191 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9192 else if ( type == SMDSEntity_Hexagonal_Prism )
9193 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9195 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9197 SMDS_MeshVolume * NewVolume = 0;
9200 case SMDSEntity_Tetra:
9201 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9203 case SMDSEntity_Hexa:
9204 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9205 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9207 case SMDSEntity_Pyramid:
9208 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9209 nodes[3], nodes[4], id, theForce3d);
9211 case SMDSEntity_Penta:
9212 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9213 nodes[3], nodes[4], nodes[5], id, theForce3d);
9215 case SMDSEntity_Hexagonal_Prism:
9217 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9219 ReplaceElemInGroups(volume, NewVolume, meshDS);
9224 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9225 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9226 aHelper.FixQuadraticElements();
9230 //================================================================================
9232 * \brief Makes given elements quadratic
9233 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9234 * \param theElements - elements to make quadratic
9236 //================================================================================
9238 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9239 TIDSortedElemSet& theElements)
9241 if ( theElements.empty() ) return;
9243 // we believe that all theElements are of the same type
9244 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9246 // get all nodes shared by theElements
9247 TIDSortedNodeSet allNodes;
9248 TIDSortedElemSet::iterator eIt = theElements.begin();
9249 for ( ; eIt != theElements.end(); ++eIt )
9250 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9252 // complete theElements with elements of lower dim whose all nodes are in allNodes
9254 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9255 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9256 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9257 for ( ; nIt != allNodes.end(); ++nIt )
9259 const SMDS_MeshNode* n = *nIt;
9260 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9261 while ( invIt->more() )
9263 const SMDS_MeshElement* e = invIt->next();
9264 if ( e->IsQuadratic() )
9266 quadAdjacentElems[ e->GetType() ].insert( e );
9269 if ( e->GetType() >= elemType )
9271 continue; // same type of more complex linear element
9274 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9275 continue; // e is already checked
9279 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9280 while ( nodeIt->more() && allIn )
9281 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9283 theElements.insert(e );
9287 SMESH_MesherHelper helper(*myMesh);
9288 helper.SetIsQuadratic( true );
9290 // add links of quadratic adjacent elements to the helper
9292 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9293 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9294 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9296 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9298 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9299 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9300 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9302 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9304 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9305 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9306 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9308 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9311 // make quadratic elements instead of linear ones
9313 SMESHDS_Mesh* meshDS = GetMeshDS();
9314 SMESHDS_SubMesh* smDS = 0;
9315 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9317 const SMDS_MeshElement* elem = *eIt;
9318 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9321 const int id = elem->GetID();
9322 const SMDSAbs_ElementType type = elem->GetType();
9323 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9325 if ( !smDS || !smDS->Contains( elem ))
9326 smDS = meshDS->MeshElements( elem->getshapeId() );
9327 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9329 SMDS_MeshElement * newElem = 0;
9330 switch( nodes.size() )
9332 case 4: // cases for most frequently used element types go first (for optimization)
9333 if ( type == SMDSAbs_Volume )
9334 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9336 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9339 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9340 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9343 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9346 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9349 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9350 nodes[4], id, theForce3d);
9353 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9354 nodes[4], nodes[5], id, theForce3d);
9358 ReplaceElemInGroups( elem, newElem, meshDS);
9359 if( newElem && smDS )
9360 smDS->AddElement( newElem );
9363 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9364 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9365 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9366 helper.FixQuadraticElements();
9370 //=======================================================================
9372 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9373 * \return int - nb of checked elements
9375 //=======================================================================
9377 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9378 SMDS_ElemIteratorPtr theItr,
9379 const int theShapeID)
9382 SMESHDS_Mesh* meshDS = GetMeshDS();
9384 while( theItr->more() )
9386 const SMDS_MeshElement* elem = theItr->next();
9388 if( elem && elem->IsQuadratic())
9390 int id = elem->GetID();
9391 int nbCornerNodes = elem->NbCornerNodes();
9392 SMDSAbs_ElementType aType = elem->GetType();
9394 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9396 //remove a quadratic element
9397 if ( !theSm || !theSm->Contains( elem ))
9398 theSm = meshDS->MeshElements( elem->getshapeId() );
9399 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9401 // remove medium nodes
9402 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9403 if ( nodes[i]->NbInverseElements() == 0 )
9404 meshDS->RemoveFreeNode( nodes[i], theSm );
9406 // add a linear element
9407 nodes.resize( nbCornerNodes );
9408 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9409 ReplaceElemInGroups(elem, newElem, meshDS);
9410 if( theSm && newElem )
9411 theSm->AddElement( newElem );
9417 //=======================================================================
9418 //function : ConvertFromQuadratic
9420 //=======================================================================
9422 bool SMESH_MeshEditor::ConvertFromQuadratic()
9424 int nbCheckedElems = 0;
9425 if ( myMesh->HasShapeToMesh() )
9427 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9429 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9430 while ( smIt->more() ) {
9431 SMESH_subMesh* sm = smIt->next();
9432 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9433 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9439 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9440 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9442 SMESHDS_SubMesh *aSM = 0;
9443 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9451 //================================================================================
9453 * \brief Return true if all medium nodes of the element are in the node set
9455 //================================================================================
9457 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9459 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9460 if ( !nodeSet.count( elem->GetNode(i) ))
9466 //================================================================================
9468 * \brief Makes given elements linear
9470 //================================================================================
9472 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9474 if ( theElements.empty() ) return;
9476 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9477 set<int> mediumNodeIDs;
9478 TIDSortedElemSet::iterator eIt = theElements.begin();
9479 for ( ; eIt != theElements.end(); ++eIt )
9481 const SMDS_MeshElement* e = *eIt;
9482 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9483 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9486 // replace given elements by linear ones
9487 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9488 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9489 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9491 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9492 // except those elements sharing medium nodes of quadratic element whose medium nodes
9493 // are not all in mediumNodeIDs
9495 // get remaining medium nodes
9496 TIDSortedNodeSet mediumNodes;
9497 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9498 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9499 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9500 mediumNodes.insert( mediumNodes.end(), n );
9502 // find more quadratic elements to convert
9503 TIDSortedElemSet moreElemsToConvert;
9504 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9505 for ( ; nIt != mediumNodes.end(); ++nIt )
9507 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9508 while ( invIt->more() )
9510 const SMDS_MeshElement* e = invIt->next();
9511 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9513 // find a more complex element including e and
9514 // whose medium nodes are not in mediumNodes
9515 bool complexFound = false;
9516 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9518 SMDS_ElemIteratorPtr invIt2 =
9519 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9520 while ( invIt2->more() )
9522 const SMDS_MeshElement* eComplex = invIt2->next();
9523 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9525 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9526 if ( nbCommonNodes == e->NbNodes())
9528 complexFound = true;
9529 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9535 if ( !complexFound )
9536 moreElemsToConvert.insert( e );
9540 elemIt = SMDS_ElemIteratorPtr
9541 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9542 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9545 //=======================================================================
9546 //function : SewSideElements
9548 //=======================================================================
9550 SMESH_MeshEditor::Sew_Error
9551 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9552 TIDSortedElemSet& theSide2,
9553 const SMDS_MeshNode* theFirstNode1,
9554 const SMDS_MeshNode* theFirstNode2,
9555 const SMDS_MeshNode* theSecondNode1,
9556 const SMDS_MeshNode* theSecondNode2)
9558 myLastCreatedElems.Clear();
9559 myLastCreatedNodes.Clear();
9561 MESSAGE ("::::SewSideElements()");
9562 if ( theSide1.size() != theSide2.size() )
9563 return SEW_DIFF_NB_OF_ELEMENTS;
9565 Sew_Error aResult = SEW_OK;
9567 // 1. Build set of faces representing each side
9568 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9569 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9571 // =======================================================================
9572 // 1. Build set of faces representing each side:
9573 // =======================================================================
9574 // a. build set of nodes belonging to faces
9575 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9576 // c. create temporary faces representing side of volumes if correspondent
9577 // face does not exist
9579 SMESHDS_Mesh* aMesh = GetMeshDS();
9580 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9581 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9582 set<const SMDS_MeshElement*> faceSet1, faceSet2;
9583 set<const SMDS_MeshElement*> volSet1, volSet2;
9584 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9585 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9586 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9587 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9588 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9589 int iSide, iFace, iNode;
9591 list<const SMDS_MeshElement* > tempFaceList;
9592 for ( iSide = 0; iSide < 2; iSide++ ) {
9593 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9594 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9595 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9596 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9597 set<const SMDS_MeshElement*>::iterator vIt;
9598 TIDSortedElemSet::iterator eIt;
9599 set<const SMDS_MeshNode*>::iterator nIt;
9601 // check that given nodes belong to given elements
9602 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9603 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9604 int firstIndex = -1, secondIndex = -1;
9605 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9606 const SMDS_MeshElement* elem = *eIt;
9607 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9608 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9609 if ( firstIndex > -1 && secondIndex > -1 ) break;
9611 if ( firstIndex < 0 || secondIndex < 0 ) {
9612 // we can simply return until temporary faces created
9613 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9616 // -----------------------------------------------------------
9617 // 1a. Collect nodes of existing faces
9618 // and build set of face nodes in order to detect missing
9619 // faces corresponding to sides of volumes
9620 // -----------------------------------------------------------
9622 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9624 // loop on the given element of a side
9625 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9626 //const SMDS_MeshElement* elem = *eIt;
9627 const SMDS_MeshElement* elem = *eIt;
9628 if ( elem->GetType() == SMDSAbs_Face ) {
9629 faceSet->insert( elem );
9630 set <const SMDS_MeshNode*> faceNodeSet;
9631 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9632 while ( nodeIt->more() ) {
9633 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9634 nodeSet->insert( n );
9635 faceNodeSet.insert( n );
9637 setOfFaceNodeSet.insert( faceNodeSet );
9639 else if ( elem->GetType() == SMDSAbs_Volume )
9640 volSet->insert( elem );
9642 // ------------------------------------------------------------------------------
9643 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9644 // ------------------------------------------------------------------------------
9646 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9647 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9648 while ( fIt->more() ) { // loop on faces sharing a node
9649 const SMDS_MeshElement* f = fIt->next();
9650 if ( faceSet->find( f ) == faceSet->end() ) {
9651 // check if all nodes are in nodeSet and
9652 // complete setOfFaceNodeSet if they are
9653 set <const SMDS_MeshNode*> faceNodeSet;
9654 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9655 bool allInSet = true;
9656 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9657 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9658 if ( nodeSet->find( n ) == nodeSet->end() )
9661 faceNodeSet.insert( n );
9664 faceSet->insert( f );
9665 setOfFaceNodeSet.insert( faceNodeSet );
9671 // -------------------------------------------------------------------------
9672 // 1c. Create temporary faces representing sides of volumes if correspondent
9673 // face does not exist
9674 // -------------------------------------------------------------------------
9676 if ( !volSet->empty() ) {
9677 //int nodeSetSize = nodeSet->size();
9679 // loop on given volumes
9680 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9681 SMDS_VolumeTool vol (*vIt);
9682 // loop on volume faces: find free faces
9683 // --------------------------------------
9684 list<const SMDS_MeshElement* > freeFaceList;
9685 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9686 if ( !vol.IsFreeFace( iFace ))
9688 // check if there is already a face with same nodes in a face set
9689 const SMDS_MeshElement* aFreeFace = 0;
9690 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9691 int nbNodes = vol.NbFaceNodes( iFace );
9692 set <const SMDS_MeshNode*> faceNodeSet;
9693 vol.GetFaceNodes( iFace, faceNodeSet );
9694 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9696 // no such a face is given but it still can exist, check it
9697 if ( nbNodes == 3 ) {
9698 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9700 else if ( nbNodes == 4 ) {
9701 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9704 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9705 aFreeFace = aMesh->FindFace(poly_nodes);
9709 // create a temporary face
9710 if ( nbNodes == 3 ) {
9711 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9712 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9714 else if ( nbNodes == 4 ) {
9715 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9716 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9719 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9720 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9721 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9725 freeFaceList.push_back( aFreeFace );
9726 tempFaceList.push_back( aFreeFace );
9729 } // loop on faces of a volume
9731 // choose one of several free faces
9732 // --------------------------------------
9733 if ( freeFaceList.size() > 1 ) {
9734 // choose a face having max nb of nodes shared by other elems of a side
9735 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9736 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9737 while ( fIt != freeFaceList.end() ) { // loop on free faces
9738 int nbSharedNodes = 0;
9739 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9740 while ( nodeIt->more() ) { // loop on free face nodes
9741 const SMDS_MeshNode* n =
9742 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9743 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9744 while ( invElemIt->more() ) {
9745 const SMDS_MeshElement* e = invElemIt->next();
9746 if ( faceSet->find( e ) != faceSet->end() )
9748 if ( elemSet->find( e ) != elemSet->end() )
9752 if ( nbSharedNodes >= maxNbNodes ) {
9753 maxNbNodes = nbSharedNodes;
9757 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9759 if ( freeFaceList.size() > 1 )
9761 // could not choose one face, use another way
9762 // choose a face most close to the bary center of the opposite side
9763 gp_XYZ aBC( 0., 0., 0. );
9764 set <const SMDS_MeshNode*> addedNodes;
9765 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9766 eIt = elemSet2->begin();
9767 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9768 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9769 while ( nodeIt->more() ) { // loop on free face nodes
9770 const SMDS_MeshNode* n =
9771 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9772 if ( addedNodes.insert( n ).second )
9773 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9776 aBC /= addedNodes.size();
9777 double minDist = DBL_MAX;
9778 fIt = freeFaceList.begin();
9779 while ( fIt != freeFaceList.end() ) { // loop on free faces
9781 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9782 while ( nodeIt->more() ) { // loop on free face nodes
9783 const SMDS_MeshNode* n =
9784 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9785 gp_XYZ p( n->X(),n->Y(),n->Z() );
9786 dist += ( aBC - p ).SquareModulus();
9788 if ( dist < minDist ) {
9790 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9793 fIt = freeFaceList.erase( fIt++ );
9796 } // choose one of several free faces of a volume
9798 if ( freeFaceList.size() == 1 ) {
9799 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9800 faceSet->insert( aFreeFace );
9801 // complete a node set with nodes of a found free face
9802 // for ( iNode = 0; iNode < ; iNode++ )
9803 // nodeSet->insert( fNodes[ iNode ] );
9806 } // loop on volumes of a side
9808 // // complete a set of faces if new nodes in a nodeSet appeared
9809 // // ----------------------------------------------------------
9810 // if ( nodeSetSize != nodeSet->size() ) {
9811 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9812 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9813 // while ( fIt->more() ) { // loop on faces sharing a node
9814 // const SMDS_MeshElement* f = fIt->next();
9815 // if ( faceSet->find( f ) == faceSet->end() ) {
9816 // // check if all nodes are in nodeSet and
9817 // // complete setOfFaceNodeSet if they are
9818 // set <const SMDS_MeshNode*> faceNodeSet;
9819 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9820 // bool allInSet = true;
9821 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9822 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9823 // if ( nodeSet->find( n ) == nodeSet->end() )
9824 // allInSet = false;
9826 // faceNodeSet.insert( n );
9828 // if ( allInSet ) {
9829 // faceSet->insert( f );
9830 // setOfFaceNodeSet.insert( faceNodeSet );
9836 } // Create temporary faces, if there are volumes given
9839 if ( faceSet1.size() != faceSet2.size() ) {
9840 // delete temporary faces: they are in reverseElements of actual nodes
9841 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9842 // while ( tmpFaceIt->more() )
9843 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9844 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9845 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9846 // aMesh->RemoveElement(*tmpFaceIt);
9847 MESSAGE("Diff nb of faces");
9848 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9851 // ============================================================
9852 // 2. Find nodes to merge:
9853 // bind a node to remove to a node to put instead
9854 // ============================================================
9856 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9857 if ( theFirstNode1 != theFirstNode2 )
9858 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
9859 if ( theSecondNode1 != theSecondNode2 )
9860 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
9862 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9863 set< long > linkIdSet; // links to process
9864 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9866 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9867 list< NLink > linkList[2];
9868 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9869 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9870 // loop on links in linkList; find faces by links and append links
9871 // of the found faces to linkList
9872 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9873 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9874 NLink link[] = { *linkIt[0], *linkIt[1] };
9875 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9876 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
9879 // by links, find faces in the face sets,
9880 // and find indices of link nodes in the found faces;
9881 // in a face set, there is only one or no face sharing a link
9882 // ---------------------------------------------------------------
9884 const SMDS_MeshElement* face[] = { 0, 0 };
9885 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
9886 vector<const SMDS_MeshNode*> fnodes1(9);
9887 vector<const SMDS_MeshNode*> fnodes2(9);
9888 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
9889 vector<const SMDS_MeshNode*> notLinkNodes1(6);
9890 vector<const SMDS_MeshNode*> notLinkNodes2(6);
9891 int iLinkNode[2][2];
9892 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9893 const SMDS_MeshNode* n1 = link[iSide].first;
9894 const SMDS_MeshNode* n2 = link[iSide].second;
9895 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9896 set< const SMDS_MeshElement* > fMap;
9897 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
9898 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
9899 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9900 while ( fIt->more() ) { // loop on faces sharing a node
9901 const SMDS_MeshElement* f = fIt->next();
9902 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9903 ! fMap.insert( f ).second ) // f encounters twice
9905 if ( face[ iSide ] ) {
9906 MESSAGE( "2 faces per link " );
9907 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
9911 faceSet->erase( f );
9912 // get face nodes and find ones of a link
9917 fnodes1.resize(f->NbNodes()+1);
9918 notLinkNodes1.resize(f->NbNodes()-2);
9921 fnodes2.resize(f->NbNodes()+1);
9922 notLinkNodes2.resize(f->NbNodes()-2);
9925 if(!f->IsQuadratic()) {
9926 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
9927 while ( nIt->more() ) {
9928 const SMDS_MeshNode* n =
9929 static_cast<const SMDS_MeshNode*>( nIt->next() );
9931 iLinkNode[ iSide ][ 0 ] = iNode;
9933 else if ( n == n2 ) {
9934 iLinkNode[ iSide ][ 1 ] = iNode;
9936 //else if ( notLinkNodes[ iSide ][ 0 ] )
9937 // notLinkNodes[ iSide ][ 1 ] = n;
9939 // notLinkNodes[ iSide ][ 0 ] = n;
9943 notLinkNodes1[nbl] = n;
9944 //notLinkNodes1.push_back(n);
9946 notLinkNodes2[nbl] = n;
9947 //notLinkNodes2.push_back(n);
9949 //faceNodes[ iSide ][ iNode++ ] = n;
9951 fnodes1[iNode++] = n;
9954 fnodes2[iNode++] = n;
9958 else { // f->IsQuadratic()
9959 const SMDS_VtkFace* F =
9960 dynamic_cast<const SMDS_VtkFace*>(f);
9961 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9962 // use special nodes iterator
9963 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9964 while ( anIter->more() ) {
9965 const SMDS_MeshNode* n =
9966 static_cast<const SMDS_MeshNode*>( anIter->next() );
9968 iLinkNode[ iSide ][ 0 ] = iNode;
9970 else if ( n == n2 ) {
9971 iLinkNode[ iSide ][ 1 ] = iNode;
9976 notLinkNodes1[nbl] = n;
9979 notLinkNodes2[nbl] = n;
9983 fnodes1[iNode++] = n;
9986 fnodes2[iNode++] = n;
9990 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
9992 fnodes1[iNode] = fnodes1[0];
9995 fnodes2[iNode] = fnodes1[0];
10002 // check similarity of elements of the sides
10003 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10004 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10005 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10006 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10009 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10011 break; // do not return because it s necessary to remove tmp faces
10014 // set nodes to merge
10015 // -------------------
10017 if ( face[0] && face[1] ) {
10018 int nbNodes = face[0]->NbNodes();
10019 if ( nbNodes != face[1]->NbNodes() ) {
10020 MESSAGE("Diff nb of face nodes");
10021 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10022 break; // do not return because it s necessary to remove tmp faces
10024 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
10025 if ( nbNodes == 3 ) {
10026 //nReplaceMap.insert( TNodeNodeMap::value_type
10027 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10028 nReplaceMap.insert( TNodeNodeMap::value_type
10029 ( notLinkNodes1[0], notLinkNodes2[0] ));
10032 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10033 // analyse link orientation in faces
10034 int i1 = iLinkNode[ iSide ][ 0 ];
10035 int i2 = iLinkNode[ iSide ][ 1 ];
10036 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10037 // if notLinkNodes are the first and the last ones, then
10038 // their order does not correspond to the link orientation
10039 if (( i1 == 1 && i2 == 2 ) ||
10040 ( i1 == 2 && i2 == 1 ))
10041 reverse[ iSide ] = !reverse[ iSide ];
10043 if ( reverse[0] == reverse[1] ) {
10044 //nReplaceMap.insert( TNodeNodeMap::value_type
10045 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10046 //nReplaceMap.insert( TNodeNodeMap::value_type
10047 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
10048 for(int nn=0; nn<nbNodes-2; nn++) {
10049 nReplaceMap.insert( TNodeNodeMap::value_type
10050 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
10054 //nReplaceMap.insert( TNodeNodeMap::value_type
10055 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
10056 //nReplaceMap.insert( TNodeNodeMap::value_type
10057 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
10058 for(int nn=0; nn<nbNodes-2; nn++) {
10059 nReplaceMap.insert( TNodeNodeMap::value_type
10060 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
10065 // add other links of the faces to linkList
10066 // -----------------------------------------
10068 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
10069 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10070 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
10071 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
10072 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10073 if ( !iter_isnew.second ) { // already in a set: no need to process
10074 linkIdSet.erase( iter_isnew.first );
10076 else // new in set == encountered for the first time: add
10078 //const SMDS_MeshNode* n1 = nodes[ iNode ];
10079 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
10080 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
10081 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
10082 linkList[0].push_back ( NLink( n1, n2 ));
10083 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10087 } // loop on link lists
10089 if ( aResult == SEW_OK &&
10090 ( linkIt[0] != linkList[0].end() ||
10091 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10092 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10093 " " << (faceSetPtr[1]->empty()));
10094 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10097 // ====================================================================
10098 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10099 // ====================================================================
10101 // delete temporary faces: they are in reverseElements of actual nodes
10102 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10103 // while ( tmpFaceIt->more() )
10104 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10105 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10106 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10107 // aMesh->RemoveElement(*tmpFaceIt);
10109 if ( aResult != SEW_OK)
10112 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10113 // loop on nodes replacement map
10114 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10115 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10116 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10117 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10118 nodeIDsToRemove.push_back( nToRemove->GetID() );
10119 // loop on elements sharing nToRemove
10120 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10121 while ( invElemIt->more() ) {
10122 const SMDS_MeshElement* e = invElemIt->next();
10123 // get a new suite of nodes: make replacement
10124 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10125 vector< const SMDS_MeshNode*> nodes( nbNodes );
10126 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10127 while ( nIt->more() ) {
10128 const SMDS_MeshNode* n =
10129 static_cast<const SMDS_MeshNode*>( nIt->next() );
10130 nnIt = nReplaceMap.find( n );
10131 if ( nnIt != nReplaceMap.end() ) {
10133 n = (*nnIt).second;
10137 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10138 // elemIDsToRemove.push_back( e->GetID() );
10142 SMDSAbs_ElementType etyp = e->GetType();
10143 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10146 myLastCreatedElems.Append(newElem);
10147 AddToSameGroups(newElem, e, aMesh);
10148 int aShapeId = e->getshapeId();
10151 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10154 aMesh->RemoveElement(e);
10159 Remove( nodeIDsToRemove, true );
10164 //================================================================================
10166 * \brief Find corresponding nodes in two sets of faces
10167 * \param theSide1 - first face set
10168 * \param theSide2 - second first face
10169 * \param theFirstNode1 - a boundary node of set 1
10170 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10171 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10172 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10173 * \param nReplaceMap - output map of corresponding nodes
10174 * \return bool - is a success or not
10176 //================================================================================
10179 //#define DEBUG_MATCHING_NODES
10182 SMESH_MeshEditor::Sew_Error
10183 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10184 set<const SMDS_MeshElement*>& theSide2,
10185 const SMDS_MeshNode* theFirstNode1,
10186 const SMDS_MeshNode* theFirstNode2,
10187 const SMDS_MeshNode* theSecondNode1,
10188 const SMDS_MeshNode* theSecondNode2,
10189 TNodeNodeMap & nReplaceMap)
10191 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10193 nReplaceMap.clear();
10194 if ( theFirstNode1 != theFirstNode2 )
10195 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10196 if ( theSecondNode1 != theSecondNode2 )
10197 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10199 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10200 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10202 list< NLink > linkList[2];
10203 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10204 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10206 // loop on links in linkList; find faces by links and append links
10207 // of the found faces to linkList
10208 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10209 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10210 NLink link[] = { *linkIt[0], *linkIt[1] };
10211 if ( linkSet.find( link[0] ) == linkSet.end() )
10214 // by links, find faces in the face sets,
10215 // and find indices of link nodes in the found faces;
10216 // in a face set, there is only one or no face sharing a link
10217 // ---------------------------------------------------------------
10219 const SMDS_MeshElement* face[] = { 0, 0 };
10220 list<const SMDS_MeshNode*> notLinkNodes[2];
10221 //bool reverse[] = { false, false }; // order of notLinkNodes
10223 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10225 const SMDS_MeshNode* n1 = link[iSide].first;
10226 const SMDS_MeshNode* n2 = link[iSide].second;
10227 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10228 set< const SMDS_MeshElement* > facesOfNode1;
10229 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10231 // during a loop of the first node, we find all faces around n1,
10232 // during a loop of the second node, we find one face sharing both n1 and n2
10233 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10234 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10235 while ( fIt->more() ) { // loop on faces sharing a node
10236 const SMDS_MeshElement* f = fIt->next();
10237 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10238 ! facesOfNode1.insert( f ).second ) // f encounters twice
10240 if ( face[ iSide ] ) {
10241 MESSAGE( "2 faces per link " );
10242 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10245 faceSet->erase( f );
10247 // get not link nodes
10248 int nbN = f->NbNodes();
10249 if ( f->IsQuadratic() )
10251 nbNodes[ iSide ] = nbN;
10252 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10253 int i1 = f->GetNodeIndex( n1 );
10254 int i2 = f->GetNodeIndex( n2 );
10255 int iEnd = nbN, iBeg = -1, iDelta = 1;
10256 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10258 std::swap( iEnd, iBeg ); iDelta = -1;
10263 if ( i == iEnd ) i = iBeg + iDelta;
10264 if ( i == i1 ) break;
10265 nodes.push_back ( f->GetNode( i ) );
10271 // check similarity of elements of the sides
10272 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10273 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10274 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10275 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10278 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10282 // set nodes to merge
10283 // -------------------
10285 if ( face[0] && face[1] ) {
10286 if ( nbNodes[0] != nbNodes[1] ) {
10287 MESSAGE("Diff nb of face nodes");
10288 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10290 #ifdef DEBUG_MATCHING_NODES
10291 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10292 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10293 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10295 int nbN = nbNodes[0];
10297 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10298 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10299 for ( int i = 0 ; i < nbN - 2; ++i ) {
10300 #ifdef DEBUG_MATCHING_NODES
10301 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10303 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10307 // add other links of the face 1 to linkList
10308 // -----------------------------------------
10310 const SMDS_MeshElement* f0 = face[0];
10311 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10312 for ( int i = 0; i < nbN; i++ )
10314 const SMDS_MeshNode* n2 = f0->GetNode( i );
10315 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10316 linkSet.insert( SMESH_TLink( n1, n2 ));
10317 if ( !iter_isnew.second ) { // already in a set: no need to process
10318 linkSet.erase( iter_isnew.first );
10320 else // new in set == encountered for the first time: add
10322 #ifdef DEBUG_MATCHING_NODES
10323 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10324 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10326 linkList[0].push_back ( NLink( n1, n2 ));
10327 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10332 } // loop on link lists
10337 //================================================================================
10339 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10340 \param theElems - the list of elements (edges or faces) to be replicated
10341 The nodes for duplication could be found from these elements
10342 \param theNodesNot - list of nodes to NOT replicate
10343 \param theAffectedElems - the list of elements (cells and edges) to which the
10344 replicated nodes should be associated to.
10345 \return TRUE if operation has been completed successfully, FALSE otherwise
10347 //================================================================================
10349 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10350 const TIDSortedElemSet& theNodesNot,
10351 const TIDSortedElemSet& theAffectedElems )
10353 myLastCreatedElems.Clear();
10354 myLastCreatedNodes.Clear();
10356 if ( theElems.size() == 0 )
10359 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10364 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10365 // duplicate elements and nodes
10366 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10367 // replce nodes by duplications
10368 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10372 //================================================================================
10374 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10375 \param theMeshDS - mesh instance
10376 \param theElems - the elements replicated or modified (nodes should be changed)
10377 \param theNodesNot - nodes to NOT replicate
10378 \param theNodeNodeMap - relation of old node to new created node
10379 \param theIsDoubleElem - flag os to replicate element or modify
10380 \return TRUE if operation has been completed successfully, FALSE otherwise
10382 //================================================================================
10384 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10385 const TIDSortedElemSet& theElems,
10386 const TIDSortedElemSet& theNodesNot,
10387 std::map< const SMDS_MeshNode*,
10388 const SMDS_MeshNode* >& theNodeNodeMap,
10389 const bool theIsDoubleElem )
10391 MESSAGE("doubleNodes");
10392 // iterate on through element and duplicate them (by nodes duplication)
10394 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10395 for ( ; elemItr != theElems.end(); ++elemItr )
10397 const SMDS_MeshElement* anElem = *elemItr;
10401 bool isDuplicate = false;
10402 // duplicate nodes to duplicate element
10403 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10404 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10406 while ( anIter->more() )
10409 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10410 SMDS_MeshNode* aNewNode = aCurrNode;
10411 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10412 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10413 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10416 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10417 theNodeNodeMap[ aCurrNode ] = aNewNode;
10418 myLastCreatedNodes.Append( aNewNode );
10420 isDuplicate |= (aCurrNode != aNewNode);
10421 newNodes[ ind++ ] = aNewNode;
10423 if ( !isDuplicate )
10426 if ( theIsDoubleElem )
10427 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10430 MESSAGE("ChangeElementNodes");
10431 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10438 //================================================================================
10440 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10441 \param theNodes - identifiers of nodes to be doubled
10442 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10443 nodes. If list of element identifiers is empty then nodes are doubled but
10444 they not assigned to elements
10445 \return TRUE if operation has been completed successfully, FALSE otherwise
10447 //================================================================================
10449 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10450 const std::list< int >& theListOfModifiedElems )
10452 MESSAGE("DoubleNodes");
10453 myLastCreatedElems.Clear();
10454 myLastCreatedNodes.Clear();
10456 if ( theListOfNodes.size() == 0 )
10459 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10463 // iterate through nodes and duplicate them
10465 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10467 std::list< int >::const_iterator aNodeIter;
10468 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10470 int aCurr = *aNodeIter;
10471 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10477 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10480 anOldNodeToNewNode[ aNode ] = aNewNode;
10481 myLastCreatedNodes.Append( aNewNode );
10485 // Create map of new nodes for modified elements
10487 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10489 std::list< int >::const_iterator anElemIter;
10490 for ( anElemIter = theListOfModifiedElems.begin();
10491 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10493 int aCurr = *anElemIter;
10494 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10498 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10500 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10502 while ( anIter->more() )
10504 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10505 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10507 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10508 aNodeArr[ ind++ ] = aNewNode;
10511 aNodeArr[ ind++ ] = aCurrNode;
10513 anElemToNodes[ anElem ] = aNodeArr;
10516 // Change nodes of elements
10518 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10519 anElemToNodesIter = anElemToNodes.begin();
10520 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10522 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10523 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10526 MESSAGE("ChangeElementNodes");
10527 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10536 //================================================================================
10538 \brief Check if element located inside shape
10539 \return TRUE if IN or ON shape, FALSE otherwise
10541 //================================================================================
10543 template<class Classifier>
10544 bool isInside(const SMDS_MeshElement* theElem,
10545 Classifier& theClassifier,
10546 const double theTol)
10548 gp_XYZ centerXYZ (0, 0, 0);
10549 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10550 while (aNodeItr->more())
10551 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10553 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10554 theClassifier.Perform(aPnt, theTol);
10555 TopAbs_State aState = theClassifier.State();
10556 return (aState == TopAbs_IN || aState == TopAbs_ON );
10559 //================================================================================
10561 * \brief Classifier of the 3D point on the TopoDS_Face
10562 * with interaface suitable for isInside()
10564 //================================================================================
10566 struct _FaceClassifier
10568 Extrema_ExtPS _extremum;
10569 BRepAdaptor_Surface _surface;
10570 TopAbs_State _state;
10572 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10574 _extremum.Initialize( _surface,
10575 _surface.FirstUParameter(), _surface.LastUParameter(),
10576 _surface.FirstVParameter(), _surface.LastVParameter(),
10577 _surface.Tolerance(), _surface.Tolerance() );
10579 void Perform(const gp_Pnt& aPnt, double theTol)
10581 _state = TopAbs_OUT;
10582 _extremum.Perform(aPnt);
10583 if ( _extremum.IsDone() )
10584 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10585 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10586 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10588 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10591 TopAbs_State State() const
10598 //================================================================================
10600 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10601 \param theElems - group of of elements (edges or faces) to be replicated
10602 \param theNodesNot - group of nodes not to replicate
10603 \param theShape - shape to detect affected elements (element which geometric center
10604 located on or inside shape).
10605 The replicated nodes should be associated to affected elements.
10606 \return TRUE if operation has been completed successfully, FALSE otherwise
10608 //================================================================================
10610 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10611 const TIDSortedElemSet& theNodesNot,
10612 const TopoDS_Shape& theShape )
10614 if ( theShape.IsNull() )
10617 const double aTol = Precision::Confusion();
10618 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10619 auto_ptr<_FaceClassifier> aFaceClassifier;
10620 if ( theShape.ShapeType() == TopAbs_SOLID )
10622 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10623 bsc3d->PerformInfinitePoint(aTol);
10625 else if (theShape.ShapeType() == TopAbs_FACE )
10627 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10630 // iterates on indicated elements and get elements by back references from their nodes
10631 TIDSortedElemSet anAffected;
10632 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10633 for ( ; elemItr != theElems.end(); ++elemItr )
10635 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10639 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10640 while ( nodeItr->more() )
10642 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10643 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10645 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10646 while ( backElemItr->more() )
10648 const SMDS_MeshElement* curElem = backElemItr->next();
10649 if ( curElem && theElems.find(curElem) == theElems.end() &&
10651 isInside( curElem, *bsc3d, aTol ) :
10652 isInside( curElem, *aFaceClassifier, aTol )))
10653 anAffected.insert( curElem );
10657 return DoubleNodes( theElems, theNodesNot, anAffected );
10661 * \brief compute an oriented angle between two planes defined by four points.
10662 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10663 * @param p0 base of the rotation axe
10664 * @param p1 extremity of the rotation axe
10665 * @param g1 belongs to the first plane
10666 * @param g2 belongs to the second plane
10668 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10670 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10671 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10672 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10673 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10674 gp_Vec vref(p0, p1);
10677 gp_Vec n1 = vref.Crossed(v1);
10678 gp_Vec n2 = vref.Crossed(v2);
10679 return n2.AngleWithRef(n1, vref);
10683 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10684 * The list of groups must describe a partition of the mesh volumes.
10685 * The nodes of the internal faces at the boundaries of the groups are doubled.
10686 * In option, the internal faces are replaced by flat elements.
10687 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10688 * The flat elements are stored in groups of volumes.
10689 * @param theElems - list of groups of volumes, where a group of volume is a set of
10690 * SMDS_MeshElements sorted by Id.
10691 * @param createJointElems - if TRUE, create the elements
10692 * @return TRUE if operation has been completed successfully, FALSE otherwise
10694 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10695 bool createJointElems)
10697 MESSAGE("----------------------------------------------");
10698 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10699 MESSAGE("----------------------------------------------");
10701 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10702 meshDS->BuildDownWardConnectivity(true);
10704 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10706 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10707 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10708 // build the list of nodes shared by 2 or more domains, with their domain indexes
10710 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10711 std::map<int,int>celldom; // cell vtkId --> domain
10712 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10713 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10714 faceDomains.clear();
10716 cellDomains.clear();
10717 nodeDomains.clear();
10718 std::map<int,int> emptyMap;
10719 std::set<int> emptySet;
10722 for (int idom = 0; idom < theElems.size(); idom++)
10725 // --- build a map (face to duplicate --> volume to modify)
10726 // with all the faces shared by 2 domains (group of elements)
10727 // and corresponding volume of this domain, for each shared face.
10728 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10730 const TIDSortedElemSet& domain = theElems[idom];
10731 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10732 for (; elemItr != domain.end(); ++elemItr)
10734 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10737 int vtkId = anElem->getVtkId();
10738 int neighborsVtkIds[NBMAXNEIGHBORS];
10739 int downIds[NBMAXNEIGHBORS];
10740 unsigned char downTypes[NBMAXNEIGHBORS];
10741 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10742 for (int n = 0; n < nbNeighbors; n++)
10744 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10745 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10746 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10748 DownIdType face(downIds[n], downTypes[n]);
10749 if (!faceDomains.count(face))
10750 faceDomains[face] = emptyMap; // create an empty entry for face
10751 if (!faceDomains[face].count(idom))
10753 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10754 celldom[vtkId] = idom;
10761 //MESSAGE("Number of shared faces " << faceDomains.size());
10762 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10764 // --- explore the shared faces domain by domain,
10765 // explore the nodes of the face and see if they belong to a cell in the domain,
10766 // which has only a node or an edge on the border (not a shared face)
10768 for (int idomain = 0; idomain < theElems.size(); idomain++)
10770 const TIDSortedElemSet& domain = theElems[idomain];
10771 itface = faceDomains.begin();
10772 for (; itface != faceDomains.end(); ++itface)
10774 std::map<int, int> domvol = itface->second;
10775 if (!domvol.count(idomain))
10777 DownIdType face = itface->first;
10778 //MESSAGE(" --- face " << face.cellId);
10779 std::set<int> oldNodes;
10781 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10782 std::set<int>::iterator itn = oldNodes.begin();
10783 for (; itn != oldNodes.end(); ++itn)
10786 //MESSAGE(" node " << oldId);
10787 std::set<int> cells;
10789 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10790 for (int i=0; i<l.ncells; i++)
10792 int vtkId = l.cells[i];
10793 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10794 if (!domain.count(anElem))
10796 int vtkType = grid->GetCellType(vtkId);
10797 int downId = grid->CellIdToDownId(vtkId);
10800 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10801 continue; // not OK at this stage of the algorithm:
10802 //no cells created after BuildDownWardConnectivity
10804 DownIdType aCell(downId, vtkType);
10805 if (celldom.count(vtkId))
10807 cellDomains[aCell][idomain] = vtkId;
10808 celldom[vtkId] = idomain;
10814 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10815 // for each shared face, get the nodes
10816 // for each node, for each domain of the face, create a clone of the node
10818 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10819 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10820 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10822 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10823 std::map<int, std::vector<int> > mutipleNodes; // nodes muti domains with domain order
10825 for (int idomain = 0; idomain < theElems.size(); idomain++)
10827 itface = faceDomains.begin();
10828 for (; itface != faceDomains.end(); ++itface)
10830 std::map<int, int> domvol = itface->second;
10831 if (!domvol.count(idomain))
10833 DownIdType face = itface->first;
10834 //MESSAGE(" --- face " << face.cellId);
10835 std::set<int> oldNodes;
10837 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10838 bool isMultipleDetected = false;
10839 std::set<int>::iterator itn = oldNodes.begin();
10840 for (; itn != oldNodes.end(); ++itn)
10843 //MESSAGE(" node " << oldId);
10844 if (!nodeDomains.count(oldId))
10845 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10846 if (nodeDomains[oldId].empty())
10847 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10848 std::map<int, int>::iterator itdom = domvol.begin();
10849 for (; itdom != domvol.end(); ++itdom)
10851 int idom = itdom->first;
10852 //MESSAGE(" domain " << idom);
10853 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10855 if (nodeDomains[oldId].size() >= 2) // a multiple node
10857 vector<int> orderedDoms;
10858 //MESSAGE("multiple node " << oldId);
10859 isMultipleDetected =true;
10860 if (mutipleNodes.count(oldId))
10861 orderedDoms = mutipleNodes[oldId];
10864 map<int,int>::iterator it = nodeDomains[oldId].begin();
10865 for (; it != nodeDomains[oldId].end(); ++it)
10866 orderedDoms.push_back(it->first);
10868 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10869 //stringstream txt;
10870 //for (int i=0; i<orderedDoms.size(); i++)
10871 // txt << orderedDoms[i] << " ";
10872 //MESSAGE("orderedDoms " << txt.str());
10873 mutipleNodes[oldId] = orderedDoms;
10875 double *coords = grid->GetPoint(oldId);
10876 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10877 int newId = newNode->getVtkId();
10878 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10879 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
10881 if (nodeDomains[oldId].size() >= 3)
10883 //MESSAGE("confirm multiple node " << oldId);
10884 isMultipleDetected =true;
10888 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
10890 //MESSAGE("multiple Nodes detected on a shared face");
10891 int downId = itface->first.cellId;
10892 unsigned char cellType = itface->first.cellType;
10893 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10894 const int *downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10895 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10896 for (int ie =0; ie < nbEdges; ie++)
10899 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10900 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10902 vector<int> vn0 = mutipleNodes[nodes[0]];
10903 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10904 sort( vn0.begin(), vn0.end() );
10905 sort( vn1.begin(), vn1.end() );
10908 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10909 double *coords = grid->GetPoint(nodes[0]);
10910 gp_Pnt p0(coords[0], coords[1], coords[2]);
10911 coords = grid->GetPoint(nodes[nbNodes - 1]);
10912 gp_Pnt p1(coords[0], coords[1], coords[2]);
10914 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10915 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10916 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10917 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10918 for (int id=0; id < vn0.size(); id++)
10920 int idom = vn0[id];
10921 for (int ivol=0; ivol<nbvol; ivol++)
10923 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10924 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10925 if (theElems[idom].count(elem))
10927 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10928 domvol[idom] = svol;
10929 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10931 vtkIdType npts = 0;
10932 vtkIdType* pts = 0;
10933 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10934 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10937 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10938 angleDom[idom] = 0;
10942 gp_Pnt g(values[0], values[1], values[2]);
10943 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10944 //MESSAGE(" angle=" << angleDom[idom]);
10950 map<double, int> sortedDom; // sort domains by angle
10951 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10952 sortedDom[ia->second] = ia->first;
10953 vector<int> vnodes;
10955 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10957 vdom.push_back(ib->second);
10958 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10960 for (int ino = 0; ino < nbNodes; ino++)
10961 vnodes.push_back(nodes[ino]);
10962 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10970 // --- iterate on shared faces (volumes to modify, face to extrude)
10971 // get node id's of the face (id SMDS = id VTK)
10972 // create flat element with old and new nodes if requested
10974 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10975 // (domain1 X domain2) = domain1 + MAXINT*domain2
10977 std::map<int, std::map<long,int> > nodeQuadDomains;
10978 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10980 if (createJointElems)
10982 itface = faceDomains.begin();
10983 for (; itface != faceDomains.end(); ++itface)
10985 DownIdType face = itface->first;
10986 std::set<int> oldNodes;
10987 std::set<int>::iterator itn;
10989 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10991 std::map<int, int> domvol = itface->second;
10992 std::map<int, int>::iterator itdom = domvol.begin();
10993 int dom1 = itdom->first;
10994 int vtkVolId = itdom->second;
10996 int dom2 = itdom->first;
10997 SMDS_MeshVolume *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10999 stringstream grpname;
11002 grpname << dom1 << "_" << dom2;
11004 grpname << dom2 << "_" << dom1;
11006 string namegrp = grpname.str();
11007 if (!mapOfJunctionGroups.count(namegrp))
11008 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11009 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11011 sgrp->Add(vol->GetID());
11015 // --- create volumes on multiple domain intersection if requested
11016 // iterate on edgesMultiDomains
11018 if (createJointElems)
11020 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11021 for (; ite != edgesMultiDomains.end(); ++ite)
11023 vector<int> nodes = ite->first;
11024 vector<int> orderDom = ite->second;
11025 vector<vtkIdType> orderedNodes;
11026 if (nodes.size() == 2)
11028 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11029 for (int ino=0; ino < nodes.size(); ino++)
11030 if (orderDom.size() == 3)
11031 for (int idom = 0; idom <orderDom.size(); idom++)
11032 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11034 for (int idom = orderDom.size()-1; idom >=0; idom--)
11035 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11036 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11038 stringstream grpname;
11040 grpname << 0 << "_" << 0;
11042 string namegrp = grpname.str();
11043 if (!mapOfJunctionGroups.count(namegrp))
11044 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11045 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11047 sgrp->Add(vol->GetID());
11051 //MESSAGE("Quadratic multiple joints not implemented");
11052 // TODO quadratic nodes
11057 // --- list the explicit faces and edges of the mesh that need to be modified,
11058 // i.e. faces and edges built with one or more duplicated nodes.
11059 // associate these faces or edges to their corresponding domain.
11060 // only the first domain found is kept when a face or edge is shared
11062 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11063 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11064 faceOrEdgeDom.clear();
11067 for (int idomain = 0; idomain < theElems.size(); idomain++)
11069 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11070 for (; itnod != nodeDomains.end(); ++itnod)
11072 int oldId = itnod->first;
11073 //MESSAGE(" node " << oldId);
11074 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11075 for (int i = 0; i < l.ncells; i++)
11077 int vtkId = l.cells[i];
11078 int vtkType = grid->GetCellType(vtkId);
11079 int downId = grid->CellIdToDownId(vtkId);
11081 continue; // new cells: not to be modified
11082 DownIdType aCell(downId, vtkType);
11083 int volParents[1000];
11084 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11085 for (int j = 0; j < nbvol; j++)
11086 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11087 if (!feDom.count(vtkId))
11089 feDom[vtkId] = idomain;
11090 faceOrEdgeDom[aCell] = emptyMap;
11091 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11092 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11093 // << " type " << vtkType << " downId " << downId);
11099 // --- iterate on shared faces (volumes to modify, face to extrude)
11100 // get node id's of the face
11101 // replace old nodes by new nodes in volumes, and update inverse connectivity
11103 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11104 for (int m=0; m<3; m++)
11106 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11107 itface = (*amap).begin();
11108 for (; itface != (*amap).end(); ++itface)
11110 DownIdType face = itface->first;
11111 std::set<int> oldNodes;
11112 std::set<int>::iterator itn;
11114 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11115 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11116 std::map<int, int> localClonedNodeIds;
11118 std::map<int, int> domvol = itface->second;
11119 std::map<int, int>::iterator itdom = domvol.begin();
11120 for (; itdom != domvol.end(); ++itdom)
11122 int idom = itdom->first;
11123 int vtkVolId = itdom->second;
11124 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11125 localClonedNodeIds.clear();
11126 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11129 if (nodeDomains[oldId].count(idom))
11131 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11132 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11135 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11140 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11141 grid->BuildLinks();
11149 * \brief Double nodes on some external faces and create flat elements.
11150 * Flat elements are mainly used by some types of mechanic calculations.
11152 * Each group of the list must be constituted of faces.
11153 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11154 * @param theElems - list of groups of faces, where a group of faces is a set of
11155 * SMDS_MeshElements sorted by Id.
11156 * @return TRUE if operation has been completed successfully, FALSE otherwise
11158 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11160 MESSAGE("-------------------------------------------------");
11161 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11162 MESSAGE("-------------------------------------------------");
11164 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11166 // --- For each group of faces
11167 // duplicate the nodes, create a flat element based on the face
11168 // replace the nodes of the faces by their clones
11170 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11171 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11172 clonedNodes.clear();
11173 intermediateNodes.clear();
11174 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11175 mapOfJunctionGroups.clear();
11177 for (int idom = 0; idom < theElems.size(); idom++)
11179 const TIDSortedElemSet& domain = theElems[idom];
11180 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11181 for (; elemItr != domain.end(); ++elemItr)
11183 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11184 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11187 // MESSAGE("aFace=" << aFace->GetID());
11188 bool isQuad = aFace->IsQuadratic();
11189 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11191 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11193 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11194 while (nodeIt->more())
11196 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11197 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11199 ln2.push_back(node);
11201 ln0.push_back(node);
11203 const SMDS_MeshNode* clone = 0;
11204 if (!clonedNodes.count(node))
11206 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11207 clonedNodes[node] = clone;
11210 clone = clonedNodes[node];
11213 ln3.push_back(clone);
11215 ln1.push_back(clone);
11217 const SMDS_MeshNode* inter = 0;
11218 if (isQuad && (!isMedium))
11220 if (!intermediateNodes.count(node))
11222 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11223 intermediateNodes[node] = inter;
11226 inter = intermediateNodes[node];
11227 ln4.push_back(inter);
11231 // --- extrude the face
11233 vector<const SMDS_MeshNode*> ln;
11234 SMDS_MeshVolume* vol = 0;
11235 vtkIdType aType = aFace->GetVtkType();
11239 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11240 // MESSAGE("vol prism " << vol->GetID());
11241 ln.push_back(ln1[0]);
11242 ln.push_back(ln1[1]);
11243 ln.push_back(ln1[2]);
11246 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11247 // MESSAGE("vol hexa " << vol->GetID());
11248 ln.push_back(ln1[0]);
11249 ln.push_back(ln1[1]);
11250 ln.push_back(ln1[2]);
11251 ln.push_back(ln1[3]);
11253 case VTK_QUADRATIC_TRIANGLE:
11254 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11255 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11256 // MESSAGE("vol quad prism " << vol->GetID());
11257 ln.push_back(ln1[0]);
11258 ln.push_back(ln1[1]);
11259 ln.push_back(ln1[2]);
11260 ln.push_back(ln3[0]);
11261 ln.push_back(ln3[1]);
11262 ln.push_back(ln3[2]);
11264 case VTK_QUADRATIC_QUAD:
11265 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11266 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11267 // ln4[0], ln4[1], ln4[2], ln4[3]);
11268 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11269 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11270 ln4[0], ln4[1], ln4[2], ln4[3]);
11271 // MESSAGE("vol quad hexa " << vol->GetID());
11272 ln.push_back(ln1[0]);
11273 ln.push_back(ln1[1]);
11274 ln.push_back(ln1[2]);
11275 ln.push_back(ln1[3]);
11276 ln.push_back(ln3[0]);
11277 ln.push_back(ln3[1]);
11278 ln.push_back(ln3[2]);
11279 ln.push_back(ln3[3]);
11289 stringstream grpname;
11293 string namegrp = grpname.str();
11294 if (!mapOfJunctionGroups.count(namegrp))
11295 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11296 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11298 sgrp->Add(vol->GetID());
11301 // --- modify the face
11303 aFace->ChangeNodes(&ln[0], ln.size());
11309 //================================================================================
11311 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11312 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11313 * \return TRUE if operation has been completed successfully, FALSE otherwise
11315 //================================================================================
11317 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11319 // iterates on volume elements and detect all free faces on them
11320 SMESHDS_Mesh* aMesh = GetMeshDS();
11323 //bool res = false;
11324 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11325 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11328 const SMDS_MeshVolume* volume = vIt->next();
11329 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11330 vTool.SetExternalNormal();
11331 //const bool isPoly = volume->IsPoly();
11332 const int iQuad = volume->IsQuadratic();
11333 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11335 if (!vTool.IsFreeFace(iface))
11338 vector<const SMDS_MeshNode *> nodes;
11339 int nbFaceNodes = vTool.NbFaceNodes(iface);
11340 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11342 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11343 nodes.push_back(faceNodes[inode]);
11344 if (iQuad) { // add medium nodes
11345 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11346 nodes.push_back(faceNodes[inode]);
11347 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11348 nodes.push_back(faceNodes[8]);
11350 // add new face based on volume nodes
11351 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11353 continue; // face already exsist
11355 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11359 return ( nbFree==(nbExisted+nbCreated) );
11364 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11366 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11368 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11371 //================================================================================
11373 * \brief Creates missing boundary elements
11374 * \param elements - elements whose boundary is to be checked
11375 * \param dimension - defines type of boundary elements to create
11376 * \param group - a group to store created boundary elements in
11377 * \param targetMesh - a mesh to store created boundary elements in
11378 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11379 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11380 * boundary elements will be copied into the targetMesh
11381 * \param toAddExistingBondary - if true, not only new but also pre-existing
11382 * boundary elements will be added into the new group
11383 * \param aroundElements - if true, elements will be created on boundary of given
11384 * elements else, on boundary of the whole mesh.
11385 * \return nb of added boundary elements
11387 //================================================================================
11389 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11390 Bnd_Dimension dimension,
11391 SMESH_Group* group/*=0*/,
11392 SMESH_Mesh* targetMesh/*=0*/,
11393 bool toCopyElements/*=false*/,
11394 bool toCopyExistingBoundary/*=false*/,
11395 bool toAddExistingBondary/*= false*/,
11396 bool aroundElements/*= false*/)
11398 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11399 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11400 // hope that all elements are of the same type, do not check them all
11401 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11402 throw SALOME_Exception(LOCALIZED("wrong element type"));
11405 toCopyElements = toCopyExistingBoundary = false;
11407 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11408 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11409 int nbAddedBnd = 0;
11411 // editor adding present bnd elements and optionally holding elements to add to the group
11412 SMESH_MeshEditor* presentEditor;
11413 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11414 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11416 SMDS_VolumeTool vTool;
11417 TIDSortedElemSet avoidSet;
11418 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11421 typedef vector<const SMDS_MeshNode*> TConnectivity;
11423 SMDS_ElemIteratorPtr eIt;
11424 if (elements.empty())
11425 eIt = aMesh->elementsIterator(elemType);
11427 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11429 while (eIt->more())
11431 const SMDS_MeshElement* elem = eIt->next();
11432 const int iQuad = elem->IsQuadratic();
11434 // ------------------------------------------------------------------------------------
11435 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11436 // ------------------------------------------------------------------------------------
11437 vector<const SMDS_MeshElement*> presentBndElems;
11438 vector<TConnectivity> missingBndElems;
11439 TConnectivity nodes;
11440 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11442 vTool.SetExternalNormal();
11443 const SMDS_MeshElement* otherVol = 0;
11444 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11446 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11447 ( !aroundElements || elements.count( otherVol )))
11449 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11450 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11451 if ( missType == SMDSAbs_Edge ) // boundary edges
11453 nodes.resize( 2+iQuad );
11454 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11456 for ( int j = 0; j < nodes.size(); ++j )
11458 if ( const SMDS_MeshElement* edge =
11459 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11460 presentBndElems.push_back( edge );
11462 missingBndElems.push_back( nodes );
11465 else // boundary face
11468 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11469 nodes.push_back( nn[inode] );
11470 if (iQuad) // add medium nodes
11471 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11472 nodes.push_back( nn[inode] );
11473 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11475 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11477 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11478 SMDSAbs_Face, /*noMedium=*/false ))
11479 presentBndElems.push_back( f );
11481 missingBndElems.push_back( nodes );
11483 if ( targetMesh != myMesh )
11485 // add 1D elements on face boundary to be added to a new mesh
11486 const SMDS_MeshElement* edge;
11487 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11490 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11492 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11493 if ( edge && avoidSet.insert( edge ).second )
11494 presentBndElems.push_back( edge );
11500 else // elem is a face ------------------------------------------
11502 avoidSet.clear(), avoidSet.insert( elem );
11503 int nbNodes = elem->NbCornerNodes();
11504 nodes.resize( 2 /*+ iQuad*/);
11505 for ( int i = 0; i < nbNodes; i++ )
11507 nodes[0] = elem->GetNode(i);
11508 nodes[1] = elem->GetNode((i+1)%nbNodes);
11509 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11510 continue; // not free link
11513 //nodes[2] = elem->GetNode( i + nbNodes );
11514 if ( const SMDS_MeshElement* edge =
11515 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11516 presentBndElems.push_back( edge );
11518 missingBndElems.push_back( nodes );
11522 // ---------------------------------
11523 // 2. Add missing boundary elements
11524 // ---------------------------------
11525 if ( targetMesh != myMesh )
11526 // instead of making a map of nodes in this mesh and targetMesh,
11527 // we create nodes with same IDs.
11528 for ( int i = 0; i < missingBndElems.size(); ++i )
11530 TConnectivity& srcNodes = missingBndElems[i];
11531 TConnectivity nodes( srcNodes.size() );
11532 for ( inode = 0; inode < nodes.size(); ++inode )
11533 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11534 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11536 /*noMedium=*/false))
11538 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11542 for ( int i = 0; i < missingBndElems.size(); ++i )
11544 TConnectivity& nodes = missingBndElems[i];
11545 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11547 /*noMedium=*/false))
11549 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11553 // ----------------------------------
11554 // 3. Copy present boundary elements
11555 // ----------------------------------
11556 if ( toCopyExistingBoundary )
11557 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11559 const SMDS_MeshElement* e = presentBndElems[i];
11560 TConnectivity nodes( e->NbNodes() );
11561 for ( inode = 0; inode < nodes.size(); ++inode )
11562 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11563 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11565 else // store present elements to add them to a group
11566 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11568 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11571 } // loop on given elements
11573 // ---------------------------------------------
11574 // 4. Fill group with boundary elements
11575 // ---------------------------------------------
11578 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11579 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11580 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11582 tgtEditor.myLastCreatedElems.Clear();
11583 tgtEditor2.myLastCreatedElems.Clear();
11585 // -----------------------
11586 // 5. Copy given elements
11587 // -----------------------
11588 if ( toCopyElements && targetMesh != myMesh )
11590 if (elements.empty())
11591 eIt = aMesh->elementsIterator(elemType);
11593 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11594 while (eIt->more())
11596 const SMDS_MeshElement* elem = eIt->next();
11597 TConnectivity nodes( elem->NbNodes() );
11598 for ( inode = 0; inode < nodes.size(); ++inode )
11599 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11600 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11602 tgtEditor.myLastCreatedElems.Clear();