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
7148 iRepl[ nbRepl++ ] = iCur;
7150 curNodes[ iCur ] = n;
7151 bool isUnique = nodeSet.insert( n ).second;
7153 uniqueNodes[ iUnique++ ] = n;
7154 if ( nbRepl && iRepl[ nbRepl-1 ] == iCur )
7155 --nbRepl; // n do not stick to a node of the elem
7160 // Analyse element topology after replacement
7163 int nbUniqueNodes = nodeSet.size();
7164 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7165 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7166 // Polygons and Polyhedral volumes
7167 if (elem->IsPoly()) {
7169 if (elem->GetType() == SMDSAbs_Face) {
7171 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7173 for (; inode < nbNodes; inode++) {
7174 face_nodes[inode] = curNodes[inode];
7177 vector<const SMDS_MeshNode *> polygons_nodes;
7178 vector<int> quantities;
7179 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7182 for (int iface = 0; iface < nbNew; iface++) {
7183 int nbNodes = quantities[iface];
7184 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7185 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7186 poly_nodes[ii] = polygons_nodes[inode];
7188 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7189 myLastCreatedElems.Append(newElem);
7191 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7194 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7195 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7196 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7198 if (nbNew > 0) quid = nbNew - 1;
7199 vector<int> newquant(quantities.begin()+quid, quantities.end());
7200 const SMDS_MeshElement* newElem = 0;
7201 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7202 myLastCreatedElems.Append(newElem);
7203 if ( aShapeId && newElem )
7204 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7205 rmElemIds.push_back(elem->GetID());
7208 rmElemIds.push_back(elem->GetID());
7212 else if (elem->GetType() == SMDSAbs_Volume) {
7213 // Polyhedral volume
7214 if (nbUniqueNodes < 4) {
7215 rmElemIds.push_back(elem->GetID());
7218 // each face has to be analyzed in order to check volume validity
7219 const SMDS_VtkVolume* aPolyedre =
7220 dynamic_cast<const SMDS_VtkVolume*>( elem );
7222 int nbFaces = aPolyedre->NbFaces();
7224 vector<const SMDS_MeshNode *> poly_nodes;
7225 vector<int> quantities;
7227 for (int iface = 1; iface <= nbFaces; iface++) {
7228 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7229 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7231 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7232 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7233 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7234 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7235 faceNode = (*nnIt).second;
7237 faceNodes[inode - 1] = faceNode;
7240 SimplifyFace(faceNodes, poly_nodes, quantities);
7243 if (quantities.size() > 3) {
7244 // to be done: remove coincident faces
7247 if (quantities.size() > 3)
7249 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7250 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7251 const SMDS_MeshElement* newElem = 0;
7252 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7253 myLastCreatedElems.Append(newElem);
7254 if ( aShapeId && newElem )
7255 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7256 rmElemIds.push_back(elem->GetID());
7260 rmElemIds.push_back(elem->GetID());
7271 // TODO not all the possible cases are solved. Find something more generic?
7272 switch ( nbNodes ) {
7273 case 2: ///////////////////////////////////// EDGE
7274 isOk = false; break;
7275 case 3: ///////////////////////////////////// TRIANGLE
7276 isOk = false; break;
7278 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7280 else { //////////////////////////////////// QUADRANGLE
7281 if ( nbUniqueNodes < 3 )
7283 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7284 isOk = false; // opposite nodes stick
7285 //MESSAGE("isOk " << isOk);
7288 case 6: ///////////////////////////////////// PENTAHEDRON
7289 if ( nbUniqueNodes == 4 ) {
7290 // ---------------------------------> tetrahedron
7292 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7293 // all top nodes stick: reverse a bottom
7294 uniqueNodes[ 0 ] = curNodes [ 1 ];
7295 uniqueNodes[ 1 ] = curNodes [ 0 ];
7297 else if (nbRepl == 3 &&
7298 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7299 // all bottom nodes stick: set a top before
7300 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7301 uniqueNodes[ 0 ] = curNodes [ 3 ];
7302 uniqueNodes[ 1 ] = curNodes [ 4 ];
7303 uniqueNodes[ 2 ] = curNodes [ 5 ];
7305 else if (nbRepl == 4 &&
7306 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7307 // a lateral face turns into a line: reverse a bottom
7308 uniqueNodes[ 0 ] = curNodes [ 1 ];
7309 uniqueNodes[ 1 ] = curNodes [ 0 ];
7314 else if ( nbUniqueNodes == 5 ) {
7315 // PENTAHEDRON --------------------> 2 tetrahedrons
7316 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7317 // a bottom node sticks with a linked top one
7319 SMDS_MeshElement* newElem =
7320 aMesh->AddVolume(curNodes[ 3 ],
7323 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7324 myLastCreatedElems.Append(newElem);
7326 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7327 // 2. : reverse a bottom
7328 uniqueNodes[ 0 ] = curNodes [ 1 ];
7329 uniqueNodes[ 1 ] = curNodes [ 0 ];
7339 if(elem->IsQuadratic()) { // Quadratic quadrangle
7351 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7354 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7356 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7357 uniqueNodes[0] = curNodes[0];
7358 uniqueNodes[1] = curNodes[2];
7359 uniqueNodes[2] = curNodes[3];
7360 uniqueNodes[3] = curNodes[5];
7361 uniqueNodes[4] = curNodes[6];
7362 uniqueNodes[5] = curNodes[7];
7365 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7366 uniqueNodes[0] = curNodes[0];
7367 uniqueNodes[1] = curNodes[1];
7368 uniqueNodes[2] = curNodes[2];
7369 uniqueNodes[3] = curNodes[4];
7370 uniqueNodes[4] = curNodes[5];
7371 uniqueNodes[5] = curNodes[6];
7374 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7375 uniqueNodes[0] = curNodes[1];
7376 uniqueNodes[1] = curNodes[2];
7377 uniqueNodes[2] = curNodes[3];
7378 uniqueNodes[3] = curNodes[5];
7379 uniqueNodes[4] = curNodes[6];
7380 uniqueNodes[5] = curNodes[0];
7383 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7384 uniqueNodes[0] = curNodes[0];
7385 uniqueNodes[1] = curNodes[1];
7386 uniqueNodes[2] = curNodes[3];
7387 uniqueNodes[3] = curNodes[4];
7388 uniqueNodes[4] = curNodes[6];
7389 uniqueNodes[5] = curNodes[7];
7392 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7393 uniqueNodes[0] = curNodes[0];
7394 uniqueNodes[1] = curNodes[2];
7395 uniqueNodes[2] = curNodes[3];
7396 uniqueNodes[3] = curNodes[1];
7397 uniqueNodes[4] = curNodes[6];
7398 uniqueNodes[5] = curNodes[7];
7401 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7402 uniqueNodes[0] = curNodes[0];
7403 uniqueNodes[1] = curNodes[1];
7404 uniqueNodes[2] = curNodes[2];
7405 uniqueNodes[3] = curNodes[4];
7406 uniqueNodes[4] = curNodes[5];
7407 uniqueNodes[5] = curNodes[7];
7410 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7411 uniqueNodes[0] = curNodes[0];
7412 uniqueNodes[1] = curNodes[1];
7413 uniqueNodes[2] = curNodes[3];
7414 uniqueNodes[3] = curNodes[4];
7415 uniqueNodes[4] = curNodes[2];
7416 uniqueNodes[5] = curNodes[7];
7419 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7420 uniqueNodes[0] = curNodes[0];
7421 uniqueNodes[1] = curNodes[1];
7422 uniqueNodes[2] = curNodes[2];
7423 uniqueNodes[3] = curNodes[4];
7424 uniqueNodes[4] = curNodes[5];
7425 uniqueNodes[5] = curNodes[3];
7430 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7433 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7437 //////////////////////////////////// HEXAHEDRON
7439 SMDS_VolumeTool hexa (elem);
7440 hexa.SetExternalNormal();
7441 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7442 //////////////////////// HEX ---> 1 tetrahedron
7443 for ( int iFace = 0; iFace < 6; iFace++ ) {
7444 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7445 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7446 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7447 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7448 // one face turns into a point ...
7449 int iOppFace = hexa.GetOppFaceIndex( iFace );
7450 ind = hexa.GetFaceNodesIndices( iOppFace );
7452 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7453 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7456 if ( nbStick == 1 ) {
7457 // ... and the opposite one - into a triangle.
7459 ind = hexa.GetFaceNodesIndices( iFace );
7460 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7467 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7468 //////////////////////// HEX ---> 1 prism
7469 int nbTria = 0, iTria[3];
7470 const int *ind; // indices of face nodes
7471 // look for triangular faces
7472 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7473 ind = hexa.GetFaceNodesIndices( iFace );
7474 TIDSortedNodeSet faceNodes;
7475 for ( iCur = 0; iCur < 4; iCur++ )
7476 faceNodes.insert( curNodes[ind[iCur]] );
7477 if ( faceNodes.size() == 3 )
7478 iTria[ nbTria++ ] = iFace;
7480 // check if triangles are opposite
7481 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7484 // set nodes of the bottom triangle
7485 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7487 for ( iCur = 0; iCur < 4; iCur++ )
7488 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7489 indB.push_back( ind[iCur] );
7490 if ( !hexa.IsForward() )
7491 std::swap( indB[0], indB[2] );
7492 for ( iCur = 0; iCur < 3; iCur++ )
7493 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7494 // set nodes of the top triangle
7495 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7496 for ( iCur = 0; iCur < 3; ++iCur )
7497 for ( int j = 0; j < 4; ++j )
7498 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7500 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7506 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7507 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7508 for ( int iFace = 0; iFace < 6; iFace++ ) {
7509 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7510 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7511 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7512 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7513 // one face turns into a point ...
7514 int iOppFace = hexa.GetOppFaceIndex( iFace );
7515 ind = hexa.GetFaceNodesIndices( iOppFace );
7517 iUnique = 2; // reverse a tetrahedron 1 bottom
7518 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7519 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7521 else if ( iUnique >= 0 )
7522 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7524 if ( nbStick == 0 ) {
7525 // ... and the opposite one is a quadrangle
7527 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7528 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7531 SMDS_MeshElement* newElem =
7532 aMesh->AddVolume(curNodes[ind[ 0 ]],
7535 curNodes[indTop[ 0 ]]);
7536 myLastCreatedElems.Append(newElem);
7538 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7545 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7546 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7547 // find indices of quad and tri faces
7548 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7549 for ( iFace = 0; iFace < 6; iFace++ ) {
7550 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7552 for ( iCur = 0; iCur < 4; iCur++ )
7553 nodeSet.insert( curNodes[ind[ iCur ]] );
7554 nbUniqueNodes = nodeSet.size();
7555 if ( nbUniqueNodes == 3 )
7556 iTriFace[ nbTri++ ] = iFace;
7557 else if ( nbUniqueNodes == 4 )
7558 iQuadFace[ nbQuad++ ] = iFace;
7560 if (nbQuad == 2 && nbTri == 4 &&
7561 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7562 // 2 opposite quadrangles stuck with a diagonal;
7563 // sample groups of merged indices: (0-4)(2-6)
7564 // --------------------------------------------> 2 tetrahedrons
7565 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7566 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7567 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7568 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7569 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7570 // stuck with 0-2 diagonal
7578 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7579 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7580 // stuck with 1-3 diagonal
7592 uniqueNodes[ 0 ] = curNodes [ i0 ];
7593 uniqueNodes[ 1 ] = curNodes [ i1d ];
7594 uniqueNodes[ 2 ] = curNodes [ i3d ];
7595 uniqueNodes[ 3 ] = curNodes [ i0t ];
7598 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7602 myLastCreatedElems.Append(newElem);
7604 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7607 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7608 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7609 // --------------------------------------------> prism
7610 // find 2 opposite triangles
7612 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7613 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7614 // find indices of kept and replaced nodes
7615 // and fill unique nodes of 2 opposite triangles
7616 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7617 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7618 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7619 // fill unique nodes
7622 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7623 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7624 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7626 // iCur of a linked node of the opposite face (make normals co-directed):
7627 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7628 // check that correspondent corners of triangles are linked
7629 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7632 uniqueNodes[ iUnique ] = n;
7633 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7642 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7645 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7652 } // switch ( nbNodes )
7654 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7656 if ( isOk ) { // the elem remains valid after sticking nodes
7657 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7659 // Change nodes of polyedre
7660 const SMDS_VtkVolume* aPolyedre =
7661 dynamic_cast<const SMDS_VtkVolume*>( elem );
7663 int nbFaces = aPolyedre->NbFaces();
7665 vector<const SMDS_MeshNode *> poly_nodes;
7666 vector<int> quantities (nbFaces);
7668 for (int iface = 1; iface <= nbFaces; iface++) {
7669 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7670 quantities[iface - 1] = nbFaceNodes;
7672 for (inode = 1; inode <= nbFaceNodes; inode++) {
7673 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7675 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7676 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7677 curNode = (*nnIt).second;
7679 poly_nodes.push_back(curNode);
7682 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7685 else // replace non-polyhedron elements
7687 const SMDSAbs_ElementType etyp = elem->GetType();
7688 const int elemId = elem->GetID();
7689 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7690 uniqueNodes.resize(nbUniqueNodes);
7692 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7694 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7695 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7696 if ( sm && newElem )
7697 sm->AddElement( newElem );
7698 if ( elem != newElem )
7699 ReplaceElemInGroups( elem, newElem, aMesh );
7703 // Remove invalid regular element or invalid polygon
7704 rmElemIds.push_back( elem->GetID() );
7707 } // loop on elements
7709 // Remove bad elements, then equal nodes (order important)
7711 Remove( rmElemIds, false );
7712 Remove( rmNodeIds, true );
7717 // ========================================================
7718 // class : SortableElement
7719 // purpose : allow sorting elements basing on their nodes
7720 // ========================================================
7721 class SortableElement : public set <const SMDS_MeshElement*>
7725 SortableElement( const SMDS_MeshElement* theElem )
7728 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7729 while ( nodeIt->more() )
7730 this->insert( nodeIt->next() );
7733 const SMDS_MeshElement* Get() const
7736 void Set(const SMDS_MeshElement* e) const
7741 mutable const SMDS_MeshElement* myElem;
7744 //=======================================================================
7745 //function : FindEqualElements
7746 //purpose : Return list of group of elements built on the same nodes.
7747 // Search among theElements or in the whole mesh if theElements is empty
7748 //=======================================================================
7749 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7750 TListOfListOfElementsID & theGroupsOfElementsID)
7752 myLastCreatedElems.Clear();
7753 myLastCreatedNodes.Clear();
7755 typedef set<const SMDS_MeshElement*> TElemsSet;
7756 typedef map< SortableElement, int > TMapOfNodeSet;
7757 typedef list<int> TGroupOfElems;
7760 if ( theElements.empty() )
7761 { // get all elements in the mesh
7762 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7763 while ( eIt->more() )
7764 elems.insert( elems.end(), eIt->next());
7767 elems = theElements;
7769 vector< TGroupOfElems > arrayOfGroups;
7770 TGroupOfElems groupOfElems;
7771 TMapOfNodeSet mapOfNodeSet;
7773 TElemsSet::iterator elemIt = elems.begin();
7774 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
7775 const SMDS_MeshElement* curElem = *elemIt;
7776 SortableElement SE(curElem);
7779 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7780 if( !(pp.second) ) {
7781 TMapOfNodeSet::iterator& itSE = pp.first;
7782 ind = (*itSE).second;
7783 arrayOfGroups[ind].push_back(curElem->GetID());
7786 groupOfElems.clear();
7787 groupOfElems.push_back(curElem->GetID());
7788 arrayOfGroups.push_back(groupOfElems);
7793 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7794 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7795 groupOfElems = *groupIt;
7796 if ( groupOfElems.size() > 1 ) {
7797 groupOfElems.sort();
7798 theGroupsOfElementsID.push_back(groupOfElems);
7803 //=======================================================================
7804 //function : MergeElements
7805 //purpose : In each given group, substitute all elements by the first one.
7806 //=======================================================================
7808 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7810 myLastCreatedElems.Clear();
7811 myLastCreatedNodes.Clear();
7813 typedef list<int> TListOfIDs;
7814 TListOfIDs rmElemIds; // IDs of elems to remove
7816 SMESHDS_Mesh* aMesh = GetMeshDS();
7818 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7819 while ( groupsIt != theGroupsOfElementsID.end() ) {
7820 TListOfIDs& aGroupOfElemID = *groupsIt;
7821 aGroupOfElemID.sort();
7822 int elemIDToKeep = aGroupOfElemID.front();
7823 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7824 aGroupOfElemID.pop_front();
7825 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7826 while ( idIt != aGroupOfElemID.end() ) {
7827 int elemIDToRemove = *idIt;
7828 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7829 // add the kept element in groups of removed one (PAL15188)
7830 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7831 rmElemIds.push_back( elemIDToRemove );
7837 Remove( rmElemIds, false );
7840 //=======================================================================
7841 //function : MergeEqualElements
7842 //purpose : Remove all but one of elements built on the same nodes.
7843 //=======================================================================
7845 void SMESH_MeshEditor::MergeEqualElements()
7847 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
7848 to merge equal elements in the whole mesh */
7849 TListOfListOfElementsID aGroupsOfElementsID;
7850 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7851 MergeElements(aGroupsOfElementsID);
7854 //=======================================================================
7855 //function : FindFaceInSet
7856 //purpose : Return a face having linked nodes n1 and n2 and which is
7857 // - not in avoidSet,
7858 // - in elemSet provided that !elemSet.empty()
7859 // i1 and i2 optionally returns indices of n1 and n2
7860 //=======================================================================
7862 const SMDS_MeshElement*
7863 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
7864 const SMDS_MeshNode* n2,
7865 const TIDSortedElemSet& elemSet,
7866 const TIDSortedElemSet& avoidSet,
7872 const SMDS_MeshElement* face = 0;
7874 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
7875 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
7876 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
7878 //MESSAGE("in while ( invElemIt->more() && !face )");
7879 const SMDS_MeshElement* elem = invElemIt->next();
7880 if (avoidSet.count( elem ))
7882 if ( !elemSet.empty() && !elemSet.count( elem ))
7885 i1 = elem->GetNodeIndex( n1 );
7886 // find a n2 linked to n1
7887 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
7888 for ( int di = -1; di < 2 && !face; di += 2 )
7890 i2 = (i1+di+nbN) % nbN;
7891 if ( elem->GetNode( i2 ) == n2 )
7894 if ( !face && elem->IsQuadratic())
7896 // analysis for quadratic elements using all nodes
7897 const SMDS_VtkFace* F =
7898 dynamic_cast<const SMDS_VtkFace*>(elem);
7899 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7900 // use special nodes iterator
7901 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7902 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
7903 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
7905 const SMDS_MeshNode* n = cast2Node( anIter->next() );
7906 if ( n1 == prevN && n2 == n )
7910 else if ( n2 == prevN && n1 == n )
7912 face = elem; swap( i1, i2 );
7918 if ( n1ind ) *n1ind = i1;
7919 if ( n2ind ) *n2ind = i2;
7923 //=======================================================================
7924 //function : findAdjacentFace
7926 //=======================================================================
7928 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7929 const SMDS_MeshNode* n2,
7930 const SMDS_MeshElement* elem)
7932 TIDSortedElemSet elemSet, avoidSet;
7934 avoidSet.insert ( elem );
7935 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
7938 //=======================================================================
7939 //function : FindFreeBorder
7941 //=======================================================================
7943 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7945 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7946 const SMDS_MeshNode* theSecondNode,
7947 const SMDS_MeshNode* theLastNode,
7948 list< const SMDS_MeshNode* > & theNodes,
7949 list< const SMDS_MeshElement* >& theFaces)
7951 if ( !theFirstNode || !theSecondNode )
7953 // find border face between theFirstNode and theSecondNode
7954 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7958 theFaces.push_back( curElem );
7959 theNodes.push_back( theFirstNode );
7960 theNodes.push_back( theSecondNode );
7962 //vector<const SMDS_MeshNode*> nodes;
7963 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7964 TIDSortedElemSet foundElems;
7965 bool needTheLast = ( theLastNode != 0 );
7967 while ( nStart != theLastNode ) {
7968 if ( nStart == theFirstNode )
7969 return !needTheLast;
7971 // find all free border faces sharing form nStart
7973 list< const SMDS_MeshElement* > curElemList;
7974 list< const SMDS_MeshNode* > nStartList;
7975 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7976 while ( invElemIt->more() ) {
7977 const SMDS_MeshElement* e = invElemIt->next();
7978 if ( e == curElem || foundElems.insert( e ).second ) {
7980 int iNode = 0, nbNodes = e->NbNodes();
7981 //const SMDS_MeshNode* nodes[nbNodes+1];
7982 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7984 if(e->IsQuadratic()) {
7985 const SMDS_VtkFace* F =
7986 dynamic_cast<const SMDS_VtkFace*>(e);
7987 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7988 // use special nodes iterator
7989 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7990 while( anIter->more() ) {
7991 nodes[ iNode++ ] = cast2Node(anIter->next());
7995 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7996 while ( nIt->more() )
7997 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7999 nodes[ iNode ] = nodes[ 0 ];
8001 for ( iNode = 0; iNode < nbNodes; iNode++ )
8002 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8003 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8004 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8006 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8007 curElemList.push_back( e );
8011 // analyse the found
8013 int nbNewBorders = curElemList.size();
8014 if ( nbNewBorders == 0 ) {
8015 // no free border furthermore
8016 return !needTheLast;
8018 else if ( nbNewBorders == 1 ) {
8019 // one more element found
8021 nStart = nStartList.front();
8022 curElem = curElemList.front();
8023 theFaces.push_back( curElem );
8024 theNodes.push_back( nStart );
8027 // several continuations found
8028 list< const SMDS_MeshElement* >::iterator curElemIt;
8029 list< const SMDS_MeshNode* >::iterator nStartIt;
8030 // check if one of them reached the last node
8031 if ( needTheLast ) {
8032 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8033 curElemIt!= curElemList.end();
8034 curElemIt++, nStartIt++ )
8035 if ( *nStartIt == theLastNode ) {
8036 theFaces.push_back( *curElemIt );
8037 theNodes.push_back( *nStartIt );
8041 // find the best free border by the continuations
8042 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8043 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8044 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8045 curElemIt!= curElemList.end();
8046 curElemIt++, nStartIt++ )
8048 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8049 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8050 // find one more free border
8051 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8055 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8056 // choice: clear a worse one
8057 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8058 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8059 contNodes[ iWorse ].clear();
8060 contFaces[ iWorse ].clear();
8063 if ( contNodes[0].empty() && contNodes[1].empty() )
8066 // append the best free border
8067 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8068 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8069 theNodes.pop_back(); // remove nIgnore
8070 theNodes.pop_back(); // remove nStart
8071 theFaces.pop_back(); // remove curElem
8072 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8073 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8074 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8075 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8078 } // several continuations found
8079 } // while ( nStart != theLastNode )
8084 //=======================================================================
8085 //function : CheckFreeBorderNodes
8086 //purpose : Return true if the tree nodes are on a free border
8087 //=======================================================================
8089 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8090 const SMDS_MeshNode* theNode2,
8091 const SMDS_MeshNode* theNode3)
8093 list< const SMDS_MeshNode* > nodes;
8094 list< const SMDS_MeshElement* > faces;
8095 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8098 //=======================================================================
8099 //function : SewFreeBorder
8101 //=======================================================================
8103 SMESH_MeshEditor::Sew_Error
8104 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8105 const SMDS_MeshNode* theBordSecondNode,
8106 const SMDS_MeshNode* theBordLastNode,
8107 const SMDS_MeshNode* theSideFirstNode,
8108 const SMDS_MeshNode* theSideSecondNode,
8109 const SMDS_MeshNode* theSideThirdNode,
8110 const bool theSideIsFreeBorder,
8111 const bool toCreatePolygons,
8112 const bool toCreatePolyedrs)
8114 myLastCreatedElems.Clear();
8115 myLastCreatedNodes.Clear();
8117 MESSAGE("::SewFreeBorder()");
8118 Sew_Error aResult = SEW_OK;
8120 // ====================================
8121 // find side nodes and elements
8122 // ====================================
8124 list< const SMDS_MeshNode* > nSide[ 2 ];
8125 list< const SMDS_MeshElement* > eSide[ 2 ];
8126 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8127 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8131 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8132 nSide[0], eSide[0])) {
8133 MESSAGE(" Free Border 1 not found " );
8134 aResult = SEW_BORDER1_NOT_FOUND;
8136 if (theSideIsFreeBorder) {
8139 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8140 nSide[1], eSide[1])) {
8141 MESSAGE(" Free Border 2 not found " );
8142 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8145 if ( aResult != SEW_OK )
8148 if (!theSideIsFreeBorder) {
8152 // -------------------------------------------------------------------------
8154 // 1. If nodes to merge are not coincident, move nodes of the free border
8155 // from the coord sys defined by the direction from the first to last
8156 // nodes of the border to the correspondent sys of the side 2
8157 // 2. On the side 2, find the links most co-directed with the correspondent
8158 // links of the free border
8159 // -------------------------------------------------------------------------
8161 // 1. Since sewing may break if there are volumes to split on the side 2,
8162 // we wont move nodes but just compute new coordinates for them
8163 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8164 TNodeXYZMap nBordXYZ;
8165 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8166 list< const SMDS_MeshNode* >::iterator nBordIt;
8168 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8169 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8170 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8171 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8172 double tol2 = 1.e-8;
8173 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8174 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8175 // Need node movement.
8177 // find X and Z axes to create trsf
8178 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8180 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8182 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8185 gp_Ax3 toBordAx( Pb1, Zb, X );
8186 gp_Ax3 fromSideAx( Ps1, Zs, X );
8187 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8189 gp_Trsf toBordSys, fromSide2Sys;
8190 toBordSys.SetTransformation( toBordAx );
8191 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8192 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8195 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8196 const SMDS_MeshNode* n = *nBordIt;
8197 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8198 toBordSys.Transforms( xyz );
8199 fromSide2Sys.Transforms( xyz );
8200 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8204 // just insert nodes XYZ in the nBordXYZ map
8205 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8206 const SMDS_MeshNode* n = *nBordIt;
8207 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8211 // 2. On the side 2, find the links most co-directed with the correspondent
8212 // links of the free border
8214 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8215 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8216 sideNodes.push_back( theSideFirstNode );
8218 bool hasVolumes = false;
8219 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8220 set<long> foundSideLinkIDs, checkedLinkIDs;
8221 SMDS_VolumeTool volume;
8222 //const SMDS_MeshNode* faceNodes[ 4 ];
8224 const SMDS_MeshNode* sideNode;
8225 const SMDS_MeshElement* sideElem;
8226 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8227 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8228 nBordIt = bordNodes.begin();
8230 // border node position and border link direction to compare with
8231 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8232 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8233 // choose next side node by link direction or by closeness to
8234 // the current border node:
8235 bool searchByDir = ( *nBordIt != theBordLastNode );
8237 // find the next node on the Side 2
8239 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8241 checkedLinkIDs.clear();
8242 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8244 // loop on inverse elements of current node (prevSideNode) on the Side 2
8245 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8246 while ( invElemIt->more() )
8248 const SMDS_MeshElement* elem = invElemIt->next();
8249 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8250 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8251 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8252 bool isVolume = volume.Set( elem );
8253 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8254 if ( isVolume ) // --volume
8256 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8257 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8258 if(elem->IsQuadratic()) {
8259 const SMDS_VtkFace* F =
8260 dynamic_cast<const SMDS_VtkFace*>(elem);
8261 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8262 // use special nodes iterator
8263 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8264 while( anIter->more() ) {
8265 nodes[ iNode ] = cast2Node(anIter->next());
8266 if ( nodes[ iNode++ ] == prevSideNode )
8267 iPrevNode = iNode - 1;
8271 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8272 while ( nIt->more() ) {
8273 nodes[ iNode ] = cast2Node( nIt->next() );
8274 if ( nodes[ iNode++ ] == prevSideNode )
8275 iPrevNode = iNode - 1;
8278 // there are 2 links to check
8283 // loop on links, to be precise, on the second node of links
8284 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8285 const SMDS_MeshNode* n = nodes[ iNode ];
8287 if ( !volume.IsLinked( n, prevSideNode ))
8291 if ( iNode ) // a node before prevSideNode
8292 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8293 else // a node after prevSideNode
8294 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8296 // check if this link was already used
8297 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8298 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8299 if (!isJustChecked &&
8300 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8302 // test a link geometrically
8303 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8304 bool linkIsBetter = false;
8305 double dot = 0.0, dist = 0.0;
8306 if ( searchByDir ) { // choose most co-directed link
8307 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8308 linkIsBetter = ( dot > maxDot );
8310 else { // choose link with the node closest to bordPos
8311 dist = ( nextXYZ - bordPos ).SquareModulus();
8312 linkIsBetter = ( dist < minDist );
8314 if ( linkIsBetter ) {
8323 } // loop on inverse elements of prevSideNode
8326 MESSAGE(" Cant find path by links of the Side 2 ");
8327 return SEW_BAD_SIDE_NODES;
8329 sideNodes.push_back( sideNode );
8330 sideElems.push_back( sideElem );
8331 foundSideLinkIDs.insert ( linkID );
8332 prevSideNode = sideNode;
8334 if ( *nBordIt == theBordLastNode )
8335 searchByDir = false;
8337 // find the next border link to compare with
8338 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8339 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8340 // move to next border node if sideNode is before forward border node (bordPos)
8341 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8342 prevBordNode = *nBordIt;
8344 bordPos = nBordXYZ[ *nBordIt ];
8345 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8346 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8350 while ( sideNode != theSideSecondNode );
8352 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8353 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8354 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8356 } // end nodes search on the side 2
8358 // ============================
8359 // sew the border to the side 2
8360 // ============================
8362 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8363 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8365 TListOfListOfNodes nodeGroupsToMerge;
8366 if ( nbNodes[0] == nbNodes[1] ||
8367 ( theSideIsFreeBorder && !theSideThirdNode)) {
8369 // all nodes are to be merged
8371 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8372 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8373 nIt[0]++, nIt[1]++ )
8375 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8376 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8377 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8382 // insert new nodes into the border and the side to get equal nb of segments
8384 // get normalized parameters of nodes on the borders
8385 //double param[ 2 ][ maxNbNodes ];
8387 param[0] = new double [ maxNbNodes ];
8388 param[1] = new double [ maxNbNodes ];
8390 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8391 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8392 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8393 const SMDS_MeshNode* nPrev = *nIt;
8394 double bordLength = 0;
8395 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8396 const SMDS_MeshNode* nCur = *nIt;
8397 gp_XYZ segment (nCur->X() - nPrev->X(),
8398 nCur->Y() - nPrev->Y(),
8399 nCur->Z() - nPrev->Z());
8400 double segmentLen = segment.Modulus();
8401 bordLength += segmentLen;
8402 param[ iBord ][ iNode ] = bordLength;
8405 // normalize within [0,1]
8406 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8407 param[ iBord ][ iNode ] /= bordLength;
8411 // loop on border segments
8412 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8413 int i[ 2 ] = { 0, 0 };
8414 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8415 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8417 TElemOfNodeListMap insertMap;
8418 TElemOfNodeListMap::iterator insertMapIt;
8420 // key: elem to insert nodes into
8421 // value: 2 nodes to insert between + nodes to be inserted
8423 bool next[ 2 ] = { false, false };
8425 // find min adjacent segment length after sewing
8426 double nextParam = 10., prevParam = 0;
8427 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8428 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8429 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8430 if ( i[ iBord ] > 0 )
8431 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8433 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8434 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8435 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8437 // choose to insert or to merge nodes
8438 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8439 if ( Abs( du ) <= minSegLen * 0.2 ) {
8442 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8443 const SMDS_MeshNode* n0 = *nIt[0];
8444 const SMDS_MeshNode* n1 = *nIt[1];
8445 nodeGroupsToMerge.back().push_back( n1 );
8446 nodeGroupsToMerge.back().push_back( n0 );
8447 // position of node of the border changes due to merge
8448 param[ 0 ][ i[0] ] += du;
8449 // move n1 for the sake of elem shape evaluation during insertion.
8450 // n1 will be removed by MergeNodes() anyway
8451 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8452 next[0] = next[1] = true;
8457 int intoBord = ( du < 0 ) ? 0 : 1;
8458 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8459 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8460 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8461 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8462 if ( intoBord == 1 ) {
8463 // move node of the border to be on a link of elem of the side
8464 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8465 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8466 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8467 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8468 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8470 insertMapIt = insertMap.find( elem );
8471 bool notFound = ( insertMapIt == insertMap.end() );
8472 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8474 // insert into another link of the same element:
8475 // 1. perform insertion into the other link of the elem
8476 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8477 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8478 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8479 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8480 // 2. perform insertion into the link of adjacent faces
8482 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8484 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8488 if (toCreatePolyedrs) {
8489 // perform insertion into the links of adjacent volumes
8490 UpdateVolumes(n12, n22, nodeList);
8492 // 3. find an element appeared on n1 and n2 after the insertion
8493 insertMap.erase( elem );
8494 elem = findAdjacentFace( n1, n2, 0 );
8496 if ( notFound || otherLink ) {
8497 // add element and nodes of the side into the insertMap
8498 insertMapIt = insertMap.insert
8499 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8500 (*insertMapIt).second.push_back( n1 );
8501 (*insertMapIt).second.push_back( n2 );
8503 // add node to be inserted into elem
8504 (*insertMapIt).second.push_back( nIns );
8505 next[ 1 - intoBord ] = true;
8508 // go to the next segment
8509 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8510 if ( next[ iBord ] ) {
8511 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8513 nPrev[ iBord ] = *nIt[ iBord ];
8514 nIt[ iBord ]++; i[ iBord ]++;
8518 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8520 // perform insertion of nodes into elements
8522 for (insertMapIt = insertMap.begin();
8523 insertMapIt != insertMap.end();
8526 const SMDS_MeshElement* elem = (*insertMapIt).first;
8527 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8528 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8529 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8531 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8533 if ( !theSideIsFreeBorder ) {
8534 // look for and insert nodes into the faces adjacent to elem
8536 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8538 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8543 if (toCreatePolyedrs) {
8544 // perform insertion into the links of adjacent volumes
8545 UpdateVolumes(n1, n2, nodeList);
8551 } // end: insert new nodes
8553 MergeNodes ( nodeGroupsToMerge );
8558 //=======================================================================
8559 //function : InsertNodesIntoLink
8560 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8561 // and theBetweenNode2 and split theElement
8562 //=======================================================================
8564 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8565 const SMDS_MeshNode* theBetweenNode1,
8566 const SMDS_MeshNode* theBetweenNode2,
8567 list<const SMDS_MeshNode*>& theNodesToInsert,
8568 const bool toCreatePoly)
8570 if ( theFace->GetType() != SMDSAbs_Face ) return;
8572 // find indices of 2 link nodes and of the rest nodes
8573 int iNode = 0, il1, il2, i3, i4;
8574 il1 = il2 = i3 = i4 = -1;
8575 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8576 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8578 if(theFace->IsQuadratic()) {
8579 const SMDS_VtkFace* F =
8580 dynamic_cast<const SMDS_VtkFace*>(theFace);
8581 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8582 // use special nodes iterator
8583 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8584 while( anIter->more() ) {
8585 const SMDS_MeshNode* n = cast2Node(anIter->next());
8586 if ( n == theBetweenNode1 )
8588 else if ( n == theBetweenNode2 )
8594 nodes[ iNode++ ] = n;
8598 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8599 while ( nodeIt->more() ) {
8600 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8601 if ( n == theBetweenNode1 )
8603 else if ( n == theBetweenNode2 )
8609 nodes[ iNode++ ] = n;
8612 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8615 // arrange link nodes to go one after another regarding the face orientation
8616 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8617 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8622 aNodesToInsert.reverse();
8624 // check that not link nodes of a quadrangles are in good order
8625 int nbFaceNodes = theFace->NbNodes();
8626 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8632 if (toCreatePoly || theFace->IsPoly()) {
8635 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8637 // add nodes of face up to first node of link
8640 if(theFace->IsQuadratic()) {
8641 const SMDS_VtkFace* F =
8642 dynamic_cast<const SMDS_VtkFace*>(theFace);
8643 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8644 // use special nodes iterator
8645 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8646 while( anIter->more() && !isFLN ) {
8647 const SMDS_MeshNode* n = cast2Node(anIter->next());
8648 poly_nodes[iNode++] = n;
8649 if (n == nodes[il1]) {
8653 // add nodes to insert
8654 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8655 for (; nIt != aNodesToInsert.end(); nIt++) {
8656 poly_nodes[iNode++] = *nIt;
8658 // add nodes of face starting from last node of link
8659 while ( anIter->more() ) {
8660 poly_nodes[iNode++] = cast2Node(anIter->next());
8664 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8665 while ( nodeIt->more() && !isFLN ) {
8666 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8667 poly_nodes[iNode++] = n;
8668 if (n == nodes[il1]) {
8672 // add nodes to insert
8673 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8674 for (; nIt != aNodesToInsert.end(); nIt++) {
8675 poly_nodes[iNode++] = *nIt;
8677 // add nodes of face starting from last node of link
8678 while ( nodeIt->more() ) {
8679 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8680 poly_nodes[iNode++] = n;
8684 // edit or replace the face
8685 SMESHDS_Mesh *aMesh = GetMeshDS();
8687 if (theFace->IsPoly()) {
8688 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8691 int aShapeId = FindShape( theFace );
8693 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8694 myLastCreatedElems.Append(newElem);
8695 if ( aShapeId && newElem )
8696 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8698 aMesh->RemoveElement(theFace);
8703 SMESHDS_Mesh *aMesh = GetMeshDS();
8704 if( !theFace->IsQuadratic() ) {
8706 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8707 int nbLinkNodes = 2 + aNodesToInsert.size();
8708 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8709 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8710 linkNodes[ 0 ] = nodes[ il1 ];
8711 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8712 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8713 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8714 linkNodes[ iNode++ ] = *nIt;
8716 // decide how to split a quadrangle: compare possible variants
8717 // and choose which of splits to be a quadrangle
8718 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8719 if ( nbFaceNodes == 3 ) {
8720 iBestQuad = nbSplits;
8723 else if ( nbFaceNodes == 4 ) {
8724 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8725 double aBestRate = DBL_MAX;
8726 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8728 double aBadRate = 0;
8729 // evaluate elements quality
8730 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8731 if ( iSplit == iQuad ) {
8732 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8736 aBadRate += getBadRate( &quad, aCrit );
8739 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8741 nodes[ iSplit < iQuad ? i4 : i3 ]);
8742 aBadRate += getBadRate( &tria, aCrit );
8746 if ( aBadRate < aBestRate ) {
8748 aBestRate = aBadRate;
8753 // create new elements
8754 int aShapeId = FindShape( theFace );
8757 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8758 SMDS_MeshElement* newElem = 0;
8759 if ( iSplit == iBestQuad )
8760 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8765 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8767 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8768 myLastCreatedElems.Append(newElem);
8769 if ( aShapeId && newElem )
8770 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8773 // change nodes of theFace
8774 const SMDS_MeshNode* newNodes[ 4 ];
8775 newNodes[ 0 ] = linkNodes[ i1 ];
8776 newNodes[ 1 ] = linkNodes[ i2 ];
8777 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8778 newNodes[ 3 ] = nodes[ i4 ];
8779 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8780 const SMDS_MeshElement* newElem = 0;
8781 if (iSplit == iBestQuad)
8782 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8784 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8785 myLastCreatedElems.Append(newElem);
8786 if ( aShapeId && newElem )
8787 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8788 } // end if(!theFace->IsQuadratic())
8789 else { // theFace is quadratic
8790 // we have to split theFace on simple triangles and one simple quadrangle
8792 int nbshift = tmp*2;
8793 // shift nodes in nodes[] by nbshift
8795 for(i=0; i<nbshift; i++) {
8796 const SMDS_MeshNode* n = nodes[0];
8797 for(j=0; j<nbFaceNodes-1; j++) {
8798 nodes[j] = nodes[j+1];
8800 nodes[nbFaceNodes-1] = n;
8802 il1 = il1 - nbshift;
8803 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8804 // n0 n1 n2 n0 n1 n2
8805 // +-----+-----+ +-----+-----+
8814 // create new elements
8815 int aShapeId = FindShape( theFace );
8818 if(nbFaceNodes==6) { // quadratic triangle
8819 SMDS_MeshElement* newElem =
8820 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8821 myLastCreatedElems.Append(newElem);
8822 if ( aShapeId && newElem )
8823 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8824 if(theFace->IsMediumNode(nodes[il1])) {
8825 // create quadrangle
8826 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8827 myLastCreatedElems.Append(newElem);
8828 if ( aShapeId && newElem )
8829 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8835 // create quadrangle
8836 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8837 myLastCreatedElems.Append(newElem);
8838 if ( aShapeId && newElem )
8839 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8845 else { // nbFaceNodes==8 - quadratic quadrangle
8846 SMDS_MeshElement* newElem =
8847 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8848 myLastCreatedElems.Append(newElem);
8849 if ( aShapeId && newElem )
8850 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8851 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8852 myLastCreatedElems.Append(newElem);
8853 if ( aShapeId && newElem )
8854 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8855 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8856 myLastCreatedElems.Append(newElem);
8857 if ( aShapeId && newElem )
8858 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8859 if(theFace->IsMediumNode(nodes[il1])) {
8860 // create quadrangle
8861 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8862 myLastCreatedElems.Append(newElem);
8863 if ( aShapeId && newElem )
8864 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8870 // create quadrangle
8871 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8872 myLastCreatedElems.Append(newElem);
8873 if ( aShapeId && newElem )
8874 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8880 // create needed triangles using n1,n2,n3 and inserted nodes
8881 int nbn = 2 + aNodesToInsert.size();
8882 //const SMDS_MeshNode* aNodes[nbn];
8883 vector<const SMDS_MeshNode*> aNodes(nbn);
8884 aNodes[0] = nodes[n1];
8885 aNodes[nbn-1] = nodes[n2];
8886 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8887 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8888 aNodes[iNode++] = *nIt;
8890 for(i=1; i<nbn; i++) {
8891 SMDS_MeshElement* newElem =
8892 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8893 myLastCreatedElems.Append(newElem);
8894 if ( aShapeId && newElem )
8895 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8899 aMesh->RemoveElement(theFace);
8902 //=======================================================================
8903 //function : UpdateVolumes
8905 //=======================================================================
8906 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8907 const SMDS_MeshNode* theBetweenNode2,
8908 list<const SMDS_MeshNode*>& theNodesToInsert)
8910 myLastCreatedElems.Clear();
8911 myLastCreatedNodes.Clear();
8913 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8914 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8915 const SMDS_MeshElement* elem = invElemIt->next();
8917 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8918 SMDS_VolumeTool aVolume (elem);
8919 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8922 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8923 int iface, nbFaces = aVolume.NbFaces();
8924 vector<const SMDS_MeshNode *> poly_nodes;
8925 vector<int> quantities (nbFaces);
8927 for (iface = 0; iface < nbFaces; iface++) {
8928 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8929 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8930 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8932 for (int inode = 0; inode < nbFaceNodes; inode++) {
8933 poly_nodes.push_back(faceNodes[inode]);
8935 if (nbInserted == 0) {
8936 if (faceNodes[inode] == theBetweenNode1) {
8937 if (faceNodes[inode + 1] == theBetweenNode2) {
8938 nbInserted = theNodesToInsert.size();
8940 // add nodes to insert
8941 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8942 for (; nIt != theNodesToInsert.end(); nIt++) {
8943 poly_nodes.push_back(*nIt);
8947 else if (faceNodes[inode] == theBetweenNode2) {
8948 if (faceNodes[inode + 1] == theBetweenNode1) {
8949 nbInserted = theNodesToInsert.size();
8951 // add nodes to insert in reversed order
8952 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8954 for (; nIt != theNodesToInsert.begin(); nIt--) {
8955 poly_nodes.push_back(*nIt);
8957 poly_nodes.push_back(*nIt);
8964 quantities[iface] = nbFaceNodes + nbInserted;
8967 // Replace or update the volume
8968 SMESHDS_Mesh *aMesh = GetMeshDS();
8970 if (elem->IsPoly()) {
8971 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8975 int aShapeId = FindShape( elem );
8977 SMDS_MeshElement* newElem =
8978 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8979 myLastCreatedElems.Append(newElem);
8980 if (aShapeId && newElem)
8981 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8983 aMesh->RemoveElement(elem);
8990 //================================================================================
8992 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8994 //================================================================================
8996 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8997 vector<const SMDS_MeshNode *> & nodes,
8998 vector<int> & nbNodeInFaces )
9001 nbNodeInFaces.clear();
9002 SMDS_VolumeTool vTool ( elem );
9003 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9005 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9006 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9007 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9012 //=======================================================================
9014 * \brief Convert elements contained in a submesh to quadratic
9015 * \return int - nb of checked elements
9017 //=======================================================================
9019 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9020 SMESH_MesherHelper& theHelper,
9021 const bool theForce3d)
9024 if( !theSm ) return nbElem;
9026 vector<int> nbNodeInFaces;
9027 vector<const SMDS_MeshNode *> nodes;
9028 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9029 while(ElemItr->more())
9032 const SMDS_MeshElement* elem = ElemItr->next();
9033 if( !elem || elem->IsQuadratic() ) continue;
9035 // get elem data needed to re-create it
9037 int id = elem->GetID();
9038 int nbNodes = elem->NbNodes();
9039 SMDSAbs_ElementType aType = elem->GetType();
9040 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9041 if ( elem->GetEntityType() == SMDSEntity_Polyhedra )
9042 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9043 else if ( elem->GetEntityType() == SMDSEntity_Hexagonal_Prism )
9044 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9046 // remove a linear element
9047 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9049 const SMDS_MeshElement* NewElem = 0;
9055 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9063 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9066 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9069 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9074 case SMDSAbs_Volume :
9076 switch( elem->GetEntityType() )
9078 case SMDSEntity_Tetra:
9079 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9081 case SMDSEntity_Pyramid:
9082 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9084 case SMDSEntity_Penta:
9085 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9087 case SMDSEntity_Hexa:
9088 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9089 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9091 case SMDSEntity_Hexagonal_Prism:
9093 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9100 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9102 theSm->AddElement( NewElem );
9104 // if (!GetMeshDS()->isCompacted())
9105 // GetMeshDS()->compactMesh();
9109 //=======================================================================
9110 //function : ConvertToQuadratic
9112 //=======================================================================
9114 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9116 SMESHDS_Mesh* meshDS = GetMeshDS();
9118 SMESH_MesherHelper aHelper(*myMesh);
9119 aHelper.SetIsQuadratic( true );
9121 int nbCheckedElems = 0;
9122 if ( myMesh->HasShapeToMesh() )
9124 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9126 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9127 while ( smIt->more() ) {
9128 SMESH_subMesh* sm = smIt->next();
9129 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9130 aHelper.SetSubShape( sm->GetSubShape() );
9131 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9136 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9137 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9139 SMESHDS_SubMesh *smDS = 0;
9140 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9141 while(aEdgeItr->more())
9143 const SMDS_MeshEdge* edge = aEdgeItr->next();
9144 if(edge && !edge->IsQuadratic())
9146 int id = edge->GetID();
9147 //MESSAGE("edge->GetID() " << id);
9148 const SMDS_MeshNode* n1 = edge->GetNode(0);
9149 const SMDS_MeshNode* n2 = edge->GetNode(1);
9151 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9153 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9154 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9157 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9158 while(aFaceItr->more())
9160 const SMDS_MeshFace* face = aFaceItr->next();
9161 if(!face || face->IsQuadratic() ) continue;
9163 const int id = face->GetID();
9164 const SMDSAbs_EntityType type = face->GetEntityType();
9165 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9167 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9169 SMDS_MeshFace * NewFace = 0;
9172 case SMDSEntity_Triangle:
9173 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9175 case SMDSEntity_Quadrangle:
9176 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9179 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9181 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9183 vector<int> nbNodeInFaces;
9184 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9185 while(aVolumeItr->more())
9187 const SMDS_MeshVolume* volume = aVolumeItr->next();
9188 if(!volume || volume->IsQuadratic() ) continue;
9190 const int id = volume->GetID();
9191 const SMDSAbs_EntityType type = volume->GetEntityType();
9192 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9193 if ( type == SMDSEntity_Polyhedra )
9194 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9195 else if ( type == SMDSEntity_Hexagonal_Prism )
9196 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9198 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9200 SMDS_MeshVolume * NewVolume = 0;
9203 case SMDSEntity_Tetra:
9204 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9205 nodes[3], id, theForce3d );
9207 case SMDSEntity_Hexa:
9208 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9209 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9211 case SMDSEntity_Pyramid:
9212 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9213 nodes[3], nodes[4], id, theForce3d);
9215 case SMDSEntity_Penta:
9216 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9217 nodes[3], nodes[4], nodes[5], id, theForce3d);
9219 case SMDSEntity_Hexagonal_Prism:
9221 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9223 ReplaceElemInGroups(volume, NewVolume, meshDS);
9228 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9229 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9230 aHelper.FixQuadraticElements();
9234 //================================================================================
9236 * \brief Makes given elements quadratic
9237 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9238 * \param theElements - elements to make quadratic
9240 //================================================================================
9242 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9243 TIDSortedElemSet& theElements)
9245 if ( theElements.empty() ) return;
9247 // we believe that all theElements are of the same type
9248 SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9250 // get all nodes shared by theElements
9251 TIDSortedNodeSet allNodes;
9252 TIDSortedElemSet::iterator eIt = theElements.begin();
9253 for ( ; eIt != theElements.end(); ++eIt )
9254 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9256 // complete theElements with elements of lower dim whose all nodes are in allNodes
9258 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9259 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9260 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9261 for ( ; nIt != allNodes.end(); ++nIt )
9263 const SMDS_MeshNode* n = *nIt;
9264 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9265 while ( invIt->more() )
9267 const SMDS_MeshElement* e = invIt->next();
9268 if ( e->IsQuadratic() )
9270 quadAdjacentElems[ e->GetType() ].insert( e );
9273 if ( e->GetType() >= elemType )
9275 continue; // same type of more complex linear element
9278 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9279 continue; // e is already checked
9283 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9284 while ( nodeIt->more() && allIn )
9285 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9287 theElements.insert(e );
9291 SMESH_MesherHelper helper(*myMesh);
9292 helper.SetIsQuadratic( true );
9294 // add links of quadratic adjacent elements to the helper
9296 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9297 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9298 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9300 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9302 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9303 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9304 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9306 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9308 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9309 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9310 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9312 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9315 // make quadratic elements instead of linear ones
9317 SMESHDS_Mesh* meshDS = GetMeshDS();
9318 SMESHDS_SubMesh* smDS = 0;
9319 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9321 const SMDS_MeshElement* elem = *eIt;
9322 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9325 int id = elem->GetID();
9326 SMDSAbs_ElementType type = elem->GetType();
9327 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9329 if ( !smDS || !smDS->Contains( elem ))
9330 smDS = meshDS->MeshElements( elem->getshapeId() );
9331 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9333 SMDS_MeshElement * newElem = 0;
9334 switch( nodes.size() )
9336 case 4: // cases for most multiple element types go first (for optimization)
9337 if ( type == SMDSAbs_Volume )
9338 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9340 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9343 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9344 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9347 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9350 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9353 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9354 nodes[4], id, theForce3d);
9357 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9358 nodes[4], nodes[5], id, theForce3d);
9362 ReplaceElemInGroups( elem, newElem, meshDS);
9363 if( newElem && smDS )
9364 smDS->AddElement( newElem );
9367 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9368 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9369 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9370 helper.FixQuadraticElements();
9374 //=======================================================================
9376 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9377 * \return int - nb of checked elements
9379 //=======================================================================
9381 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9382 SMDS_ElemIteratorPtr theItr,
9383 const int theShapeID)
9386 SMESHDS_Mesh* meshDS = GetMeshDS();
9388 while( theItr->more() )
9390 const SMDS_MeshElement* elem = theItr->next();
9392 if( elem && elem->IsQuadratic())
9394 int id = elem->GetID();
9395 int nbCornerNodes = elem->NbCornerNodes();
9396 SMDSAbs_ElementType aType = elem->GetType();
9398 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9400 //remove a quadratic element
9401 if ( !theSm || !theSm->Contains( elem ))
9402 theSm = meshDS->MeshElements( elem->getshapeId() );
9403 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9405 // remove medium nodes
9406 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9407 if ( nodes[i]->NbInverseElements() == 0 )
9408 meshDS->RemoveFreeNode( nodes[i], theSm );
9410 // add a linear element
9411 nodes.resize( nbCornerNodes );
9412 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9413 ReplaceElemInGroups(elem, newElem, meshDS);
9414 if( theSm && newElem )
9415 theSm->AddElement( newElem );
9421 //=======================================================================
9422 //function : ConvertFromQuadratic
9424 //=======================================================================
9426 bool SMESH_MeshEditor::ConvertFromQuadratic()
9428 int nbCheckedElems = 0;
9429 if ( myMesh->HasShapeToMesh() )
9431 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9433 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9434 while ( smIt->more() ) {
9435 SMESH_subMesh* sm = smIt->next();
9436 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9437 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9443 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9444 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9446 SMESHDS_SubMesh *aSM = 0;
9447 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9455 //================================================================================
9457 * \brief Return true if all medium nodes of the element are in the node set
9459 //================================================================================
9461 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9463 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9464 if ( !nodeSet.count( elem->GetNode(i) ))
9470 //================================================================================
9472 * \brief Makes given elements linear
9474 //================================================================================
9476 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9478 if ( theElements.empty() ) return;
9480 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9481 set<int> mediumNodeIDs;
9482 TIDSortedElemSet::iterator eIt = theElements.begin();
9483 for ( ; eIt != theElements.end(); ++eIt )
9485 const SMDS_MeshElement* e = *eIt;
9486 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9487 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9490 // replace given elements by linear ones
9491 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9492 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9493 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9495 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9496 // except those elements sharing medium nodes of quadratic element whose medium nodes
9497 // are not all in mediumNodeIDs
9499 // get remaining medium nodes
9500 TIDSortedNodeSet mediumNodes;
9501 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9502 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9503 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9504 mediumNodes.insert( mediumNodes.end(), n );
9506 // find more quadratic elements to convert
9507 TIDSortedElemSet moreElemsToConvert;
9508 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9509 for ( ; nIt != mediumNodes.end(); ++nIt )
9511 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9512 while ( invIt->more() )
9514 const SMDS_MeshElement* e = invIt->next();
9515 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9517 // find a more complex element including e and
9518 // whose medium nodes are not in mediumNodes
9519 bool complexFound = false;
9520 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9522 SMDS_ElemIteratorPtr invIt2 =
9523 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9524 while ( invIt2->more() )
9526 const SMDS_MeshElement* eComplex = invIt2->next();
9527 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9529 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9530 if ( nbCommonNodes == e->NbNodes())
9532 complexFound = true;
9533 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9539 if ( !complexFound )
9540 moreElemsToConvert.insert( e );
9544 elemIt = SMDS_ElemIteratorPtr
9545 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9546 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9549 //=======================================================================
9550 //function : SewSideElements
9552 //=======================================================================
9554 SMESH_MeshEditor::Sew_Error
9555 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9556 TIDSortedElemSet& theSide2,
9557 const SMDS_MeshNode* theFirstNode1,
9558 const SMDS_MeshNode* theFirstNode2,
9559 const SMDS_MeshNode* theSecondNode1,
9560 const SMDS_MeshNode* theSecondNode2)
9562 myLastCreatedElems.Clear();
9563 myLastCreatedNodes.Clear();
9565 MESSAGE ("::::SewSideElements()");
9566 if ( theSide1.size() != theSide2.size() )
9567 return SEW_DIFF_NB_OF_ELEMENTS;
9569 Sew_Error aResult = SEW_OK;
9571 // 1. Build set of faces representing each side
9572 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9573 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9575 // =======================================================================
9576 // 1. Build set of faces representing each side:
9577 // =======================================================================
9578 // a. build set of nodes belonging to faces
9579 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9580 // c. create temporary faces representing side of volumes if correspondent
9581 // face does not exist
9583 SMESHDS_Mesh* aMesh = GetMeshDS();
9584 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9585 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9586 set<const SMDS_MeshElement*> faceSet1, faceSet2;
9587 set<const SMDS_MeshElement*> volSet1, volSet2;
9588 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9589 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9590 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9591 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9592 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9593 int iSide, iFace, iNode;
9595 list<const SMDS_MeshElement* > tempFaceList;
9596 for ( iSide = 0; iSide < 2; iSide++ ) {
9597 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9598 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9599 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9600 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9601 set<const SMDS_MeshElement*>::iterator vIt;
9602 TIDSortedElemSet::iterator eIt;
9603 set<const SMDS_MeshNode*>::iterator nIt;
9605 // check that given nodes belong to given elements
9606 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9607 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9608 int firstIndex = -1, secondIndex = -1;
9609 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9610 const SMDS_MeshElement* elem = *eIt;
9611 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9612 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9613 if ( firstIndex > -1 && secondIndex > -1 ) break;
9615 if ( firstIndex < 0 || secondIndex < 0 ) {
9616 // we can simply return until temporary faces created
9617 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9620 // -----------------------------------------------------------
9621 // 1a. Collect nodes of existing faces
9622 // and build set of face nodes in order to detect missing
9623 // faces corresponding to sides of volumes
9624 // -----------------------------------------------------------
9626 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9628 // loop on the given element of a side
9629 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9630 //const SMDS_MeshElement* elem = *eIt;
9631 const SMDS_MeshElement* elem = *eIt;
9632 if ( elem->GetType() == SMDSAbs_Face ) {
9633 faceSet->insert( elem );
9634 set <const SMDS_MeshNode*> faceNodeSet;
9635 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9636 while ( nodeIt->more() ) {
9637 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9638 nodeSet->insert( n );
9639 faceNodeSet.insert( n );
9641 setOfFaceNodeSet.insert( faceNodeSet );
9643 else if ( elem->GetType() == SMDSAbs_Volume )
9644 volSet->insert( elem );
9646 // ------------------------------------------------------------------------------
9647 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9648 // ------------------------------------------------------------------------------
9650 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9651 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9652 while ( fIt->more() ) { // loop on faces sharing a node
9653 const SMDS_MeshElement* f = fIt->next();
9654 if ( faceSet->find( f ) == faceSet->end() ) {
9655 // check if all nodes are in nodeSet and
9656 // complete setOfFaceNodeSet if they are
9657 set <const SMDS_MeshNode*> faceNodeSet;
9658 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9659 bool allInSet = true;
9660 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9661 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9662 if ( nodeSet->find( n ) == nodeSet->end() )
9665 faceNodeSet.insert( n );
9668 faceSet->insert( f );
9669 setOfFaceNodeSet.insert( faceNodeSet );
9675 // -------------------------------------------------------------------------
9676 // 1c. Create temporary faces representing sides of volumes if correspondent
9677 // face does not exist
9678 // -------------------------------------------------------------------------
9680 if ( !volSet->empty() ) {
9681 //int nodeSetSize = nodeSet->size();
9683 // loop on given volumes
9684 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9685 SMDS_VolumeTool vol (*vIt);
9686 // loop on volume faces: find free faces
9687 // --------------------------------------
9688 list<const SMDS_MeshElement* > freeFaceList;
9689 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9690 if ( !vol.IsFreeFace( iFace ))
9692 // check if there is already a face with same nodes in a face set
9693 const SMDS_MeshElement* aFreeFace = 0;
9694 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9695 int nbNodes = vol.NbFaceNodes( iFace );
9696 set <const SMDS_MeshNode*> faceNodeSet;
9697 vol.GetFaceNodes( iFace, faceNodeSet );
9698 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9700 // no such a face is given but it still can exist, check it
9701 if ( nbNodes == 3 ) {
9702 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9704 else if ( nbNodes == 4 ) {
9705 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9708 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9709 aFreeFace = aMesh->FindFace(poly_nodes);
9713 // create a temporary face
9714 if ( nbNodes == 3 ) {
9715 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9716 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9718 else if ( nbNodes == 4 ) {
9719 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9720 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9723 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9724 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9725 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9729 freeFaceList.push_back( aFreeFace );
9730 tempFaceList.push_back( aFreeFace );
9733 } // loop on faces of a volume
9735 // choose one of several free faces
9736 // --------------------------------------
9737 if ( freeFaceList.size() > 1 ) {
9738 // choose a face having max nb of nodes shared by other elems of a side
9739 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9740 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9741 while ( fIt != freeFaceList.end() ) { // loop on free faces
9742 int nbSharedNodes = 0;
9743 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9744 while ( nodeIt->more() ) { // loop on free face nodes
9745 const SMDS_MeshNode* n =
9746 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9747 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9748 while ( invElemIt->more() ) {
9749 const SMDS_MeshElement* e = invElemIt->next();
9750 if ( faceSet->find( e ) != faceSet->end() )
9752 if ( elemSet->find( e ) != elemSet->end() )
9756 if ( nbSharedNodes >= maxNbNodes ) {
9757 maxNbNodes = nbSharedNodes;
9761 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9763 if ( freeFaceList.size() > 1 )
9765 // could not choose one face, use another way
9766 // choose a face most close to the bary center of the opposite side
9767 gp_XYZ aBC( 0., 0., 0. );
9768 set <const SMDS_MeshNode*> addedNodes;
9769 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9770 eIt = elemSet2->begin();
9771 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9772 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9773 while ( nodeIt->more() ) { // loop on free face nodes
9774 const SMDS_MeshNode* n =
9775 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9776 if ( addedNodes.insert( n ).second )
9777 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9780 aBC /= addedNodes.size();
9781 double minDist = DBL_MAX;
9782 fIt = freeFaceList.begin();
9783 while ( fIt != freeFaceList.end() ) { // loop on free faces
9785 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9786 while ( nodeIt->more() ) { // loop on free face nodes
9787 const SMDS_MeshNode* n =
9788 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9789 gp_XYZ p( n->X(),n->Y(),n->Z() );
9790 dist += ( aBC - p ).SquareModulus();
9792 if ( dist < minDist ) {
9794 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9797 fIt = freeFaceList.erase( fIt++ );
9800 } // choose one of several free faces of a volume
9802 if ( freeFaceList.size() == 1 ) {
9803 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9804 faceSet->insert( aFreeFace );
9805 // complete a node set with nodes of a found free face
9806 // for ( iNode = 0; iNode < ; iNode++ )
9807 // nodeSet->insert( fNodes[ iNode ] );
9810 } // loop on volumes of a side
9812 // // complete a set of faces if new nodes in a nodeSet appeared
9813 // // ----------------------------------------------------------
9814 // if ( nodeSetSize != nodeSet->size() ) {
9815 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9816 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9817 // while ( fIt->more() ) { // loop on faces sharing a node
9818 // const SMDS_MeshElement* f = fIt->next();
9819 // if ( faceSet->find( f ) == faceSet->end() ) {
9820 // // check if all nodes are in nodeSet and
9821 // // complete setOfFaceNodeSet if they are
9822 // set <const SMDS_MeshNode*> faceNodeSet;
9823 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9824 // bool allInSet = true;
9825 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9826 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9827 // if ( nodeSet->find( n ) == nodeSet->end() )
9828 // allInSet = false;
9830 // faceNodeSet.insert( n );
9832 // if ( allInSet ) {
9833 // faceSet->insert( f );
9834 // setOfFaceNodeSet.insert( faceNodeSet );
9840 } // Create temporary faces, if there are volumes given
9843 if ( faceSet1.size() != faceSet2.size() ) {
9844 // delete temporary faces: they are in reverseElements of actual nodes
9845 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9846 // while ( tmpFaceIt->more() )
9847 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9848 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9849 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9850 // aMesh->RemoveElement(*tmpFaceIt);
9851 MESSAGE("Diff nb of faces");
9852 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9855 // ============================================================
9856 // 2. Find nodes to merge:
9857 // bind a node to remove to a node to put instead
9858 // ============================================================
9860 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9861 if ( theFirstNode1 != theFirstNode2 )
9862 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
9863 if ( theSecondNode1 != theSecondNode2 )
9864 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
9866 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9867 set< long > linkIdSet; // links to process
9868 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9870 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9871 list< NLink > linkList[2];
9872 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9873 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9874 // loop on links in linkList; find faces by links and append links
9875 // of the found faces to linkList
9876 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9877 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9878 NLink link[] = { *linkIt[0], *linkIt[1] };
9879 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9880 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
9883 // by links, find faces in the face sets,
9884 // and find indices of link nodes in the found faces;
9885 // in a face set, there is only one or no face sharing a link
9886 // ---------------------------------------------------------------
9888 const SMDS_MeshElement* face[] = { 0, 0 };
9889 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
9890 vector<const SMDS_MeshNode*> fnodes1(9);
9891 vector<const SMDS_MeshNode*> fnodes2(9);
9892 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
9893 vector<const SMDS_MeshNode*> notLinkNodes1(6);
9894 vector<const SMDS_MeshNode*> notLinkNodes2(6);
9895 int iLinkNode[2][2];
9896 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9897 const SMDS_MeshNode* n1 = link[iSide].first;
9898 const SMDS_MeshNode* n2 = link[iSide].second;
9899 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9900 set< const SMDS_MeshElement* > fMap;
9901 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
9902 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
9903 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9904 while ( fIt->more() ) { // loop on faces sharing a node
9905 const SMDS_MeshElement* f = fIt->next();
9906 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9907 ! fMap.insert( f ).second ) // f encounters twice
9909 if ( face[ iSide ] ) {
9910 MESSAGE( "2 faces per link " );
9911 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
9915 faceSet->erase( f );
9916 // get face nodes and find ones of a link
9921 fnodes1.resize(f->NbNodes()+1);
9922 notLinkNodes1.resize(f->NbNodes()-2);
9925 fnodes2.resize(f->NbNodes()+1);
9926 notLinkNodes2.resize(f->NbNodes()-2);
9929 if(!f->IsQuadratic()) {
9930 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
9931 while ( nIt->more() ) {
9932 const SMDS_MeshNode* n =
9933 static_cast<const SMDS_MeshNode*>( nIt->next() );
9935 iLinkNode[ iSide ][ 0 ] = iNode;
9937 else if ( n == n2 ) {
9938 iLinkNode[ iSide ][ 1 ] = iNode;
9940 //else if ( notLinkNodes[ iSide ][ 0 ] )
9941 // notLinkNodes[ iSide ][ 1 ] = n;
9943 // notLinkNodes[ iSide ][ 0 ] = n;
9947 notLinkNodes1[nbl] = n;
9948 //notLinkNodes1.push_back(n);
9950 notLinkNodes2[nbl] = n;
9951 //notLinkNodes2.push_back(n);
9953 //faceNodes[ iSide ][ iNode++ ] = n;
9955 fnodes1[iNode++] = n;
9958 fnodes2[iNode++] = n;
9962 else { // f->IsQuadratic()
9963 const SMDS_VtkFace* F =
9964 dynamic_cast<const SMDS_VtkFace*>(f);
9965 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9966 // use special nodes iterator
9967 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9968 while ( anIter->more() ) {
9969 const SMDS_MeshNode* n =
9970 static_cast<const SMDS_MeshNode*>( anIter->next() );
9972 iLinkNode[ iSide ][ 0 ] = iNode;
9974 else if ( n == n2 ) {
9975 iLinkNode[ iSide ][ 1 ] = iNode;
9980 notLinkNodes1[nbl] = n;
9983 notLinkNodes2[nbl] = n;
9987 fnodes1[iNode++] = n;
9990 fnodes2[iNode++] = n;
9994 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
9996 fnodes1[iNode] = fnodes1[0];
9999 fnodes2[iNode] = fnodes1[0];
10006 // check similarity of elements of the sides
10007 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10008 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10009 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10010 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10013 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10015 break; // do not return because it s necessary to remove tmp faces
10018 // set nodes to merge
10019 // -------------------
10021 if ( face[0] && face[1] ) {
10022 int nbNodes = face[0]->NbNodes();
10023 if ( nbNodes != face[1]->NbNodes() ) {
10024 MESSAGE("Diff nb of face nodes");
10025 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10026 break; // do not return because it s necessary to remove tmp faces
10028 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
10029 if ( nbNodes == 3 ) {
10030 //nReplaceMap.insert( TNodeNodeMap::value_type
10031 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10032 nReplaceMap.insert( TNodeNodeMap::value_type
10033 ( notLinkNodes1[0], notLinkNodes2[0] ));
10036 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10037 // analyse link orientation in faces
10038 int i1 = iLinkNode[ iSide ][ 0 ];
10039 int i2 = iLinkNode[ iSide ][ 1 ];
10040 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10041 // if notLinkNodes are the first and the last ones, then
10042 // their order does not correspond to the link orientation
10043 if (( i1 == 1 && i2 == 2 ) ||
10044 ( i1 == 2 && i2 == 1 ))
10045 reverse[ iSide ] = !reverse[ iSide ];
10047 if ( reverse[0] == reverse[1] ) {
10048 //nReplaceMap.insert( TNodeNodeMap::value_type
10049 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10050 //nReplaceMap.insert( TNodeNodeMap::value_type
10051 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
10052 for(int nn=0; nn<nbNodes-2; nn++) {
10053 nReplaceMap.insert( TNodeNodeMap::value_type
10054 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
10058 //nReplaceMap.insert( TNodeNodeMap::value_type
10059 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
10060 //nReplaceMap.insert( TNodeNodeMap::value_type
10061 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
10062 for(int nn=0; nn<nbNodes-2; nn++) {
10063 nReplaceMap.insert( TNodeNodeMap::value_type
10064 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
10069 // add other links of the faces to linkList
10070 // -----------------------------------------
10072 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
10073 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10074 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
10075 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
10076 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10077 if ( !iter_isnew.second ) { // already in a set: no need to process
10078 linkIdSet.erase( iter_isnew.first );
10080 else // new in set == encountered for the first time: add
10082 //const SMDS_MeshNode* n1 = nodes[ iNode ];
10083 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
10084 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
10085 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
10086 linkList[0].push_back ( NLink( n1, n2 ));
10087 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10091 } // loop on link lists
10093 if ( aResult == SEW_OK &&
10094 ( linkIt[0] != linkList[0].end() ||
10095 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10096 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10097 " " << (faceSetPtr[1]->empty()));
10098 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10101 // ====================================================================
10102 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10103 // ====================================================================
10105 // delete temporary faces: they are in reverseElements of actual nodes
10106 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10107 // while ( tmpFaceIt->more() )
10108 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10109 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10110 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10111 // aMesh->RemoveElement(*tmpFaceIt);
10113 if ( aResult != SEW_OK)
10116 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10117 // loop on nodes replacement map
10118 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10119 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10120 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10121 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10122 nodeIDsToRemove.push_back( nToRemove->GetID() );
10123 // loop on elements sharing nToRemove
10124 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10125 while ( invElemIt->more() ) {
10126 const SMDS_MeshElement* e = invElemIt->next();
10127 // get a new suite of nodes: make replacement
10128 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10129 vector< const SMDS_MeshNode*> nodes( nbNodes );
10130 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10131 while ( nIt->more() ) {
10132 const SMDS_MeshNode* n =
10133 static_cast<const SMDS_MeshNode*>( nIt->next() );
10134 nnIt = nReplaceMap.find( n );
10135 if ( nnIt != nReplaceMap.end() ) {
10137 n = (*nnIt).second;
10141 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10142 // elemIDsToRemove.push_back( e->GetID() );
10146 SMDSAbs_ElementType etyp = e->GetType();
10147 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10150 myLastCreatedElems.Append(newElem);
10151 AddToSameGroups(newElem, e, aMesh);
10152 int aShapeId = e->getshapeId();
10155 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10158 aMesh->RemoveElement(e);
10163 Remove( nodeIDsToRemove, true );
10168 //================================================================================
10170 * \brief Find corresponding nodes in two sets of faces
10171 * \param theSide1 - first face set
10172 * \param theSide2 - second first face
10173 * \param theFirstNode1 - a boundary node of set 1
10174 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10175 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10176 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10177 * \param nReplaceMap - output map of corresponding nodes
10178 * \return bool - is a success or not
10180 //================================================================================
10183 //#define DEBUG_MATCHING_NODES
10186 SMESH_MeshEditor::Sew_Error
10187 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10188 set<const SMDS_MeshElement*>& theSide2,
10189 const SMDS_MeshNode* theFirstNode1,
10190 const SMDS_MeshNode* theFirstNode2,
10191 const SMDS_MeshNode* theSecondNode1,
10192 const SMDS_MeshNode* theSecondNode2,
10193 TNodeNodeMap & nReplaceMap)
10195 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10197 nReplaceMap.clear();
10198 if ( theFirstNode1 != theFirstNode2 )
10199 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10200 if ( theSecondNode1 != theSecondNode2 )
10201 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10203 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10204 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10206 list< NLink > linkList[2];
10207 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10208 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10210 // loop on links in linkList; find faces by links and append links
10211 // of the found faces to linkList
10212 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10213 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10214 NLink link[] = { *linkIt[0], *linkIt[1] };
10215 if ( linkSet.find( link[0] ) == linkSet.end() )
10218 // by links, find faces in the face sets,
10219 // and find indices of link nodes in the found faces;
10220 // in a face set, there is only one or no face sharing a link
10221 // ---------------------------------------------------------------
10223 const SMDS_MeshElement* face[] = { 0, 0 };
10224 list<const SMDS_MeshNode*> notLinkNodes[2];
10225 //bool reverse[] = { false, false }; // order of notLinkNodes
10227 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10229 const SMDS_MeshNode* n1 = link[iSide].first;
10230 const SMDS_MeshNode* n2 = link[iSide].second;
10231 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10232 set< const SMDS_MeshElement* > facesOfNode1;
10233 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10235 // during a loop of the first node, we find all faces around n1,
10236 // during a loop of the second node, we find one face sharing both n1 and n2
10237 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10238 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10239 while ( fIt->more() ) { // loop on faces sharing a node
10240 const SMDS_MeshElement* f = fIt->next();
10241 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10242 ! facesOfNode1.insert( f ).second ) // f encounters twice
10244 if ( face[ iSide ] ) {
10245 MESSAGE( "2 faces per link " );
10246 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10249 faceSet->erase( f );
10251 // get not link nodes
10252 int nbN = f->NbNodes();
10253 if ( f->IsQuadratic() )
10255 nbNodes[ iSide ] = nbN;
10256 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10257 int i1 = f->GetNodeIndex( n1 );
10258 int i2 = f->GetNodeIndex( n2 );
10259 int iEnd = nbN, iBeg = -1, iDelta = 1;
10260 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10262 std::swap( iEnd, iBeg ); iDelta = -1;
10267 if ( i == iEnd ) i = iBeg + iDelta;
10268 if ( i == i1 ) break;
10269 nodes.push_back ( f->GetNode( i ) );
10275 // check similarity of elements of the sides
10276 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10277 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10278 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10279 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10282 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10286 // set nodes to merge
10287 // -------------------
10289 if ( face[0] && face[1] ) {
10290 if ( nbNodes[0] != nbNodes[1] ) {
10291 MESSAGE("Diff nb of face nodes");
10292 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10294 #ifdef DEBUG_MATCHING_NODES
10295 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10296 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10297 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10299 int nbN = nbNodes[0];
10301 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10302 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10303 for ( int i = 0 ; i < nbN - 2; ++i ) {
10304 #ifdef DEBUG_MATCHING_NODES
10305 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10307 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10311 // add other links of the face 1 to linkList
10312 // -----------------------------------------
10314 const SMDS_MeshElement* f0 = face[0];
10315 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10316 for ( int i = 0; i < nbN; i++ )
10318 const SMDS_MeshNode* n2 = f0->GetNode( i );
10319 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10320 linkSet.insert( SMESH_TLink( n1, n2 ));
10321 if ( !iter_isnew.second ) { // already in a set: no need to process
10322 linkSet.erase( iter_isnew.first );
10324 else // new in set == encountered for the first time: add
10326 #ifdef DEBUG_MATCHING_NODES
10327 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10328 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10330 linkList[0].push_back ( NLink( n1, n2 ));
10331 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10336 } // loop on link lists
10341 //================================================================================
10343 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10344 \param theElems - the list of elements (edges or faces) to be replicated
10345 The nodes for duplication could be found from these elements
10346 \param theNodesNot - list of nodes to NOT replicate
10347 \param theAffectedElems - the list of elements (cells and edges) to which the
10348 replicated nodes should be associated to.
10349 \return TRUE if operation has been completed successfully, FALSE otherwise
10351 //================================================================================
10353 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10354 const TIDSortedElemSet& theNodesNot,
10355 const TIDSortedElemSet& theAffectedElems )
10357 myLastCreatedElems.Clear();
10358 myLastCreatedNodes.Clear();
10360 if ( theElems.size() == 0 )
10363 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10368 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10369 // duplicate elements and nodes
10370 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10371 // replce nodes by duplications
10372 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10376 //================================================================================
10378 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10379 \param theMeshDS - mesh instance
10380 \param theElems - the elements replicated or modified (nodes should be changed)
10381 \param theNodesNot - nodes to NOT replicate
10382 \param theNodeNodeMap - relation of old node to new created node
10383 \param theIsDoubleElem - flag os to replicate element or modify
10384 \return TRUE if operation has been completed successfully, FALSE otherwise
10386 //================================================================================
10388 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10389 const TIDSortedElemSet& theElems,
10390 const TIDSortedElemSet& theNodesNot,
10391 std::map< const SMDS_MeshNode*,
10392 const SMDS_MeshNode* >& theNodeNodeMap,
10393 const bool theIsDoubleElem )
10395 MESSAGE("doubleNodes");
10396 // iterate on through element and duplicate them (by nodes duplication)
10398 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10399 for ( ; elemItr != theElems.end(); ++elemItr )
10401 const SMDS_MeshElement* anElem = *elemItr;
10405 bool isDuplicate = false;
10406 // duplicate nodes to duplicate element
10407 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10408 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10410 while ( anIter->more() )
10413 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10414 SMDS_MeshNode* aNewNode = aCurrNode;
10415 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10416 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10417 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10420 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10421 theNodeNodeMap[ aCurrNode ] = aNewNode;
10422 myLastCreatedNodes.Append( aNewNode );
10424 isDuplicate |= (aCurrNode != aNewNode);
10425 newNodes[ ind++ ] = aNewNode;
10427 if ( !isDuplicate )
10430 if ( theIsDoubleElem )
10431 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10434 MESSAGE("ChangeElementNodes");
10435 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10442 //================================================================================
10444 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10445 \param theNodes - identifiers of nodes to be doubled
10446 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10447 nodes. If list of element identifiers is empty then nodes are doubled but
10448 they not assigned to elements
10449 \return TRUE if operation has been completed successfully, FALSE otherwise
10451 //================================================================================
10453 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10454 const std::list< int >& theListOfModifiedElems )
10456 MESSAGE("DoubleNodes");
10457 myLastCreatedElems.Clear();
10458 myLastCreatedNodes.Clear();
10460 if ( theListOfNodes.size() == 0 )
10463 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10467 // iterate through nodes and duplicate them
10469 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10471 std::list< int >::const_iterator aNodeIter;
10472 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10474 int aCurr = *aNodeIter;
10475 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10481 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10484 anOldNodeToNewNode[ aNode ] = aNewNode;
10485 myLastCreatedNodes.Append( aNewNode );
10489 // Create map of new nodes for modified elements
10491 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10493 std::list< int >::const_iterator anElemIter;
10494 for ( anElemIter = theListOfModifiedElems.begin();
10495 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10497 int aCurr = *anElemIter;
10498 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10502 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10504 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10506 while ( anIter->more() )
10508 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10509 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10511 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10512 aNodeArr[ ind++ ] = aNewNode;
10515 aNodeArr[ ind++ ] = aCurrNode;
10517 anElemToNodes[ anElem ] = aNodeArr;
10520 // Change nodes of elements
10522 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10523 anElemToNodesIter = anElemToNodes.begin();
10524 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10526 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10527 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10530 MESSAGE("ChangeElementNodes");
10531 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10540 //================================================================================
10542 \brief Check if element located inside shape
10543 \return TRUE if IN or ON shape, FALSE otherwise
10545 //================================================================================
10547 template<class Classifier>
10548 bool isInside(const SMDS_MeshElement* theElem,
10549 Classifier& theClassifier,
10550 const double theTol)
10552 gp_XYZ centerXYZ (0, 0, 0);
10553 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10554 while (aNodeItr->more())
10555 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10557 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10558 theClassifier.Perform(aPnt, theTol);
10559 TopAbs_State aState = theClassifier.State();
10560 return (aState == TopAbs_IN || aState == TopAbs_ON );
10563 //================================================================================
10565 * \brief Classifier of the 3D point on the TopoDS_Face
10566 * with interaface suitable for isInside()
10568 //================================================================================
10570 struct _FaceClassifier
10572 Extrema_ExtPS _extremum;
10573 BRepAdaptor_Surface _surface;
10574 TopAbs_State _state;
10576 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10578 _extremum.Initialize( _surface,
10579 _surface.FirstUParameter(), _surface.LastUParameter(),
10580 _surface.FirstVParameter(), _surface.LastVParameter(),
10581 _surface.Tolerance(), _surface.Tolerance() );
10583 void Perform(const gp_Pnt& aPnt, double theTol)
10585 _state = TopAbs_OUT;
10586 _extremum.Perform(aPnt);
10587 if ( _extremum.IsDone() )
10588 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10589 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10590 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10592 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10595 TopAbs_State State() const
10602 //================================================================================
10604 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10605 \param theElems - group of of elements (edges or faces) to be replicated
10606 \param theNodesNot - group of nodes not to replicate
10607 \param theShape - shape to detect affected elements (element which geometric center
10608 located on or inside shape).
10609 The replicated nodes should be associated to affected elements.
10610 \return TRUE if operation has been completed successfully, FALSE otherwise
10612 //================================================================================
10614 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10615 const TIDSortedElemSet& theNodesNot,
10616 const TopoDS_Shape& theShape )
10618 if ( theShape.IsNull() )
10621 const double aTol = Precision::Confusion();
10622 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10623 auto_ptr<_FaceClassifier> aFaceClassifier;
10624 if ( theShape.ShapeType() == TopAbs_SOLID )
10626 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10627 bsc3d->PerformInfinitePoint(aTol);
10629 else if (theShape.ShapeType() == TopAbs_FACE )
10631 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10634 // iterates on indicated elements and get elements by back references from their nodes
10635 TIDSortedElemSet anAffected;
10636 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10637 for ( ; elemItr != theElems.end(); ++elemItr )
10639 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10643 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10644 while ( nodeItr->more() )
10646 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10647 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10649 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10650 while ( backElemItr->more() )
10652 const SMDS_MeshElement* curElem = backElemItr->next();
10653 if ( curElem && theElems.find(curElem) == theElems.end() &&
10655 isInside( curElem, *bsc3d, aTol ) :
10656 isInside( curElem, *aFaceClassifier, aTol )))
10657 anAffected.insert( curElem );
10661 return DoubleNodes( theElems, theNodesNot, anAffected );
10665 * \brief compute an oriented angle between two planes defined by four points.
10666 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10667 * @param p0 base of the rotation axe
10668 * @param p1 extremity of the rotation axe
10669 * @param g1 belongs to the first plane
10670 * @param g2 belongs to the second plane
10672 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10674 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10675 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10676 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10677 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10678 gp_Vec vref(p0, p1);
10681 gp_Vec n1 = vref.Crossed(v1);
10682 gp_Vec n2 = vref.Crossed(v2);
10683 return n2.AngleWithRef(n1, vref);
10687 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10688 * The list of groups must describe a partition of the mesh volumes.
10689 * The nodes of the internal faces at the boundaries of the groups are doubled.
10690 * In option, the internal faces are replaced by flat elements.
10691 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10692 * The flat elements are stored in groups of volumes.
10693 * @param theElems - list of groups of volumes, where a group of volume is a set of
10694 * SMDS_MeshElements sorted by Id.
10695 * @param createJointElems - if TRUE, create the elements
10696 * @return TRUE if operation has been completed successfully, FALSE otherwise
10698 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10699 bool createJointElems)
10701 MESSAGE("----------------------------------------------");
10702 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10703 MESSAGE("----------------------------------------------");
10705 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10706 meshDS->BuildDownWardConnectivity(true);
10708 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10710 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10711 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10712 // build the list of nodes shared by 2 or more domains, with their domain indexes
10714 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10715 std::map<int,int>celldom; // cell vtkId --> domain
10716 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10717 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10718 faceDomains.clear();
10720 cellDomains.clear();
10721 nodeDomains.clear();
10722 std::map<int,int> emptyMap;
10723 std::set<int> emptySet;
10726 for (int idom = 0; idom < theElems.size(); idom++)
10729 // --- build a map (face to duplicate --> volume to modify)
10730 // with all the faces shared by 2 domains (group of elements)
10731 // and corresponding volume of this domain, for each shared face.
10732 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10734 const TIDSortedElemSet& domain = theElems[idom];
10735 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10736 for (; elemItr != domain.end(); ++elemItr)
10738 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10741 int vtkId = anElem->getVtkId();
10742 int neighborsVtkIds[NBMAXNEIGHBORS];
10743 int downIds[NBMAXNEIGHBORS];
10744 unsigned char downTypes[NBMAXNEIGHBORS];
10745 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10746 for (int n = 0; n < nbNeighbors; n++)
10748 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10749 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10750 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10752 DownIdType face(downIds[n], downTypes[n]);
10753 if (!faceDomains.count(face))
10754 faceDomains[face] = emptyMap; // create an empty entry for face
10755 if (!faceDomains[face].count(idom))
10757 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10758 celldom[vtkId] = idom;
10765 //MESSAGE("Number of shared faces " << faceDomains.size());
10766 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10768 // --- explore the shared faces domain by domain,
10769 // explore the nodes of the face and see if they belong to a cell in the domain,
10770 // which has only a node or an edge on the border (not a shared face)
10772 for (int idomain = 0; idomain < theElems.size(); idomain++)
10774 const TIDSortedElemSet& domain = theElems[idomain];
10775 itface = faceDomains.begin();
10776 for (; itface != faceDomains.end(); ++itface)
10778 std::map<int, int> domvol = itface->second;
10779 if (!domvol.count(idomain))
10781 DownIdType face = itface->first;
10782 //MESSAGE(" --- face " << face.cellId);
10783 std::set<int> oldNodes;
10785 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10786 std::set<int>::iterator itn = oldNodes.begin();
10787 for (; itn != oldNodes.end(); ++itn)
10790 //MESSAGE(" node " << oldId);
10791 std::set<int> cells;
10793 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10794 for (int i=0; i<l.ncells; i++)
10796 int vtkId = l.cells[i];
10797 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10798 if (!domain.count(anElem))
10800 int vtkType = grid->GetCellType(vtkId);
10801 int downId = grid->CellIdToDownId(vtkId);
10804 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10805 continue; // not OK at this stage of the algorithm:
10806 //no cells created after BuildDownWardConnectivity
10808 DownIdType aCell(downId, vtkType);
10809 if (celldom.count(vtkId))
10811 cellDomains[aCell][idomain] = vtkId;
10812 celldom[vtkId] = idomain;
10818 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10819 // for each shared face, get the nodes
10820 // for each node, for each domain of the face, create a clone of the node
10822 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10823 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10824 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10826 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10827 std::map<int, std::vector<int> > mutipleNodes; // nodes muti domains with domain order
10829 for (int idomain = 0; idomain < theElems.size(); idomain++)
10831 itface = faceDomains.begin();
10832 for (; itface != faceDomains.end(); ++itface)
10834 std::map<int, int> domvol = itface->second;
10835 if (!domvol.count(idomain))
10837 DownIdType face = itface->first;
10838 //MESSAGE(" --- face " << face.cellId);
10839 std::set<int> oldNodes;
10841 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10842 bool isMultipleDetected = false;
10843 std::set<int>::iterator itn = oldNodes.begin();
10844 for (; itn != oldNodes.end(); ++itn)
10847 //MESSAGE(" node " << oldId);
10848 if (!nodeDomains.count(oldId))
10849 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10850 if (nodeDomains[oldId].empty())
10851 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10852 std::map<int, int>::iterator itdom = domvol.begin();
10853 for (; itdom != domvol.end(); ++itdom)
10855 int idom = itdom->first;
10856 //MESSAGE(" domain " << idom);
10857 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10859 if (nodeDomains[oldId].size() >= 2) // a multiple node
10861 vector<int> orderedDoms;
10862 //MESSAGE("multiple node " << oldId);
10863 isMultipleDetected =true;
10864 if (mutipleNodes.count(oldId))
10865 orderedDoms = mutipleNodes[oldId];
10868 map<int,int>::iterator it = nodeDomains[oldId].begin();
10869 for (; it != nodeDomains[oldId].end(); ++it)
10870 orderedDoms.push_back(it->first);
10872 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10873 //stringstream txt;
10874 //for (int i=0; i<orderedDoms.size(); i++)
10875 // txt << orderedDoms[i] << " ";
10876 //MESSAGE("orderedDoms " << txt.str());
10877 mutipleNodes[oldId] = orderedDoms;
10879 double *coords = grid->GetPoint(oldId);
10880 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10881 int newId = newNode->getVtkId();
10882 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10883 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
10885 if (nodeDomains[oldId].size() >= 3)
10887 //MESSAGE("confirm multiple node " << oldId);
10888 isMultipleDetected =true;
10892 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
10894 //MESSAGE("multiple Nodes detected on a shared face");
10895 int downId = itface->first.cellId;
10896 unsigned char cellType = itface->first.cellType;
10897 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10898 const int *downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10899 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10900 for (int ie =0; ie < nbEdges; ie++)
10903 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10904 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10906 vector<int> vn0 = mutipleNodes[nodes[0]];
10907 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10908 sort( vn0.begin(), vn0.end() );
10909 sort( vn1.begin(), vn1.end() );
10912 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10913 double *coords = grid->GetPoint(nodes[0]);
10914 gp_Pnt p0(coords[0], coords[1], coords[2]);
10915 coords = grid->GetPoint(nodes[nbNodes - 1]);
10916 gp_Pnt p1(coords[0], coords[1], coords[2]);
10918 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10919 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10920 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10921 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10922 for (int id=0; id < vn0.size(); id++)
10924 int idom = vn0[id];
10925 for (int ivol=0; ivol<nbvol; ivol++)
10927 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10928 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10929 if (theElems[idom].count(elem))
10931 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10932 domvol[idom] = svol;
10933 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10935 vtkIdType npts = 0;
10936 vtkIdType* pts = 0;
10937 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10938 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10941 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10942 angleDom[idom] = 0;
10946 gp_Pnt g(values[0], values[1], values[2]);
10947 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10948 //MESSAGE(" angle=" << angleDom[idom]);
10954 map<double, int> sortedDom; // sort domains by angle
10955 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10956 sortedDom[ia->second] = ia->first;
10957 vector<int> vnodes;
10959 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10961 vdom.push_back(ib->second);
10962 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10964 for (int ino = 0; ino < nbNodes; ino++)
10965 vnodes.push_back(nodes[ino]);
10966 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10974 // --- iterate on shared faces (volumes to modify, face to extrude)
10975 // get node id's of the face (id SMDS = id VTK)
10976 // create flat element with old and new nodes if requested
10978 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10979 // (domain1 X domain2) = domain1 + MAXINT*domain2
10981 std::map<int, std::map<long,int> > nodeQuadDomains;
10982 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10984 if (createJointElems)
10986 itface = faceDomains.begin();
10987 for (; itface != faceDomains.end(); ++itface)
10989 DownIdType face = itface->first;
10990 std::set<int> oldNodes;
10991 std::set<int>::iterator itn;
10993 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10995 std::map<int, int> domvol = itface->second;
10996 std::map<int, int>::iterator itdom = domvol.begin();
10997 int dom1 = itdom->first;
10998 int vtkVolId = itdom->second;
11000 int dom2 = itdom->first;
11001 SMDS_MeshVolume *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11003 stringstream grpname;
11006 grpname << dom1 << "_" << dom2;
11008 grpname << dom2 << "_" << dom1;
11010 string namegrp = grpname.str();
11011 if (!mapOfJunctionGroups.count(namegrp))
11012 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11013 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11015 sgrp->Add(vol->GetID());
11019 // --- create volumes on multiple domain intersection if requested
11020 // iterate on edgesMultiDomains
11022 if (createJointElems)
11024 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11025 for (; ite != edgesMultiDomains.end(); ++ite)
11027 vector<int> nodes = ite->first;
11028 vector<int> orderDom = ite->second;
11029 vector<vtkIdType> orderedNodes;
11030 if (nodes.size() == 2)
11032 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11033 for (int ino=0; ino < nodes.size(); ino++)
11034 if (orderDom.size() == 3)
11035 for (int idom = 0; idom <orderDom.size(); idom++)
11036 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11038 for (int idom = orderDom.size()-1; idom >=0; idom--)
11039 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11040 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11042 stringstream grpname;
11044 grpname << 0 << "_" << 0;
11046 string namegrp = grpname.str();
11047 if (!mapOfJunctionGroups.count(namegrp))
11048 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11049 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11051 sgrp->Add(vol->GetID());
11055 //MESSAGE("Quadratic multiple joints not implemented");
11056 // TODO quadratic nodes
11061 // --- list the explicit faces and edges of the mesh that need to be modified,
11062 // i.e. faces and edges built with one or more duplicated nodes.
11063 // associate these faces or edges to their corresponding domain.
11064 // only the first domain found is kept when a face or edge is shared
11066 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11067 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11068 faceOrEdgeDom.clear();
11071 for (int idomain = 0; idomain < theElems.size(); idomain++)
11073 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11074 for (; itnod != nodeDomains.end(); ++itnod)
11076 int oldId = itnod->first;
11077 //MESSAGE(" node " << oldId);
11078 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11079 for (int i = 0; i < l.ncells; i++)
11081 int vtkId = l.cells[i];
11082 int vtkType = grid->GetCellType(vtkId);
11083 int downId = grid->CellIdToDownId(vtkId);
11085 continue; // new cells: not to be modified
11086 DownIdType aCell(downId, vtkType);
11087 int volParents[1000];
11088 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11089 for (int j = 0; j < nbvol; j++)
11090 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11091 if (!feDom.count(vtkId))
11093 feDom[vtkId] = idomain;
11094 faceOrEdgeDom[aCell] = emptyMap;
11095 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11096 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11097 // << " type " << vtkType << " downId " << downId);
11103 // --- iterate on shared faces (volumes to modify, face to extrude)
11104 // get node id's of the face
11105 // replace old nodes by new nodes in volumes, and update inverse connectivity
11107 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11108 for (int m=0; m<3; m++)
11110 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11111 itface = (*amap).begin();
11112 for (; itface != (*amap).end(); ++itface)
11114 DownIdType face = itface->first;
11115 std::set<int> oldNodes;
11116 std::set<int>::iterator itn;
11118 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11119 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11120 std::map<int, int> localClonedNodeIds;
11122 std::map<int, int> domvol = itface->second;
11123 std::map<int, int>::iterator itdom = domvol.begin();
11124 for (; itdom != domvol.end(); ++itdom)
11126 int idom = itdom->first;
11127 int vtkVolId = itdom->second;
11128 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11129 localClonedNodeIds.clear();
11130 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11133 if (nodeDomains[oldId].count(idom))
11135 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11136 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11139 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11144 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11145 grid->BuildLinks();
11153 * \brief Double nodes on some external faces and create flat elements.
11154 * Flat elements are mainly used by some types of mechanic calculations.
11156 * Each group of the list must be constituted of faces.
11157 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11158 * @param theElems - list of groups of faces, where a group of faces is a set of
11159 * SMDS_MeshElements sorted by Id.
11160 * @return TRUE if operation has been completed successfully, FALSE otherwise
11162 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11164 MESSAGE("-------------------------------------------------");
11165 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11166 MESSAGE("-------------------------------------------------");
11168 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11170 // --- For each group of faces
11171 // duplicate the nodes, create a flat element based on the face
11172 // replace the nodes of the faces by their clones
11174 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11175 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11176 clonedNodes.clear();
11177 intermediateNodes.clear();
11178 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11179 mapOfJunctionGroups.clear();
11181 for (int idom = 0; idom < theElems.size(); idom++)
11183 const TIDSortedElemSet& domain = theElems[idom];
11184 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11185 for (; elemItr != domain.end(); ++elemItr)
11187 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11188 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11191 // MESSAGE("aFace=" << aFace->GetID());
11192 bool isQuad = aFace->IsQuadratic();
11193 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11195 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11197 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11198 while (nodeIt->more())
11200 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11201 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11203 ln2.push_back(node);
11205 ln0.push_back(node);
11207 const SMDS_MeshNode* clone = 0;
11208 if (!clonedNodes.count(node))
11210 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11211 clonedNodes[node] = clone;
11214 clone = clonedNodes[node];
11217 ln3.push_back(clone);
11219 ln1.push_back(clone);
11221 const SMDS_MeshNode* inter = 0;
11222 if (isQuad && (!isMedium))
11224 if (!intermediateNodes.count(node))
11226 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11227 intermediateNodes[node] = inter;
11230 inter = intermediateNodes[node];
11231 ln4.push_back(inter);
11235 // --- extrude the face
11237 vector<const SMDS_MeshNode*> ln;
11238 SMDS_MeshVolume* vol = 0;
11239 vtkIdType aType = aFace->GetVtkType();
11243 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11244 // MESSAGE("vol prism " << vol->GetID());
11245 ln.push_back(ln1[0]);
11246 ln.push_back(ln1[1]);
11247 ln.push_back(ln1[2]);
11250 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11251 // MESSAGE("vol hexa " << vol->GetID());
11252 ln.push_back(ln1[0]);
11253 ln.push_back(ln1[1]);
11254 ln.push_back(ln1[2]);
11255 ln.push_back(ln1[3]);
11257 case VTK_QUADRATIC_TRIANGLE:
11258 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11259 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11260 // MESSAGE("vol quad prism " << vol->GetID());
11261 ln.push_back(ln1[0]);
11262 ln.push_back(ln1[1]);
11263 ln.push_back(ln1[2]);
11264 ln.push_back(ln3[0]);
11265 ln.push_back(ln3[1]);
11266 ln.push_back(ln3[2]);
11268 case VTK_QUADRATIC_QUAD:
11269 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11270 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11271 // ln4[0], ln4[1], ln4[2], ln4[3]);
11272 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11273 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11274 ln4[0], ln4[1], ln4[2], ln4[3]);
11275 // MESSAGE("vol quad hexa " << vol->GetID());
11276 ln.push_back(ln1[0]);
11277 ln.push_back(ln1[1]);
11278 ln.push_back(ln1[2]);
11279 ln.push_back(ln1[3]);
11280 ln.push_back(ln3[0]);
11281 ln.push_back(ln3[1]);
11282 ln.push_back(ln3[2]);
11283 ln.push_back(ln3[3]);
11293 stringstream grpname;
11297 string namegrp = grpname.str();
11298 if (!mapOfJunctionGroups.count(namegrp))
11299 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11300 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11302 sgrp->Add(vol->GetID());
11305 // --- modify the face
11307 aFace->ChangeNodes(&ln[0], ln.size());
11313 //================================================================================
11315 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11316 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11317 * \return TRUE if operation has been completed successfully, FALSE otherwise
11319 //================================================================================
11321 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11323 // iterates on volume elements and detect all free faces on them
11324 SMESHDS_Mesh* aMesh = GetMeshDS();
11327 //bool res = false;
11328 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11329 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11332 const SMDS_MeshVolume* volume = vIt->next();
11333 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11334 vTool.SetExternalNormal();
11335 //const bool isPoly = volume->IsPoly();
11336 const int iQuad = volume->IsQuadratic();
11337 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11339 if (!vTool.IsFreeFace(iface))
11342 vector<const SMDS_MeshNode *> nodes;
11343 int nbFaceNodes = vTool.NbFaceNodes(iface);
11344 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11346 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11347 nodes.push_back(faceNodes[inode]);
11348 if (iQuad) { // add medium nodes
11349 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11350 nodes.push_back(faceNodes[inode]);
11351 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11352 nodes.push_back(faceNodes[8]);
11354 // add new face based on volume nodes
11355 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11357 continue; // face already exsist
11359 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11363 return ( nbFree==(nbExisted+nbCreated) );
11368 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11370 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11372 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11375 //================================================================================
11377 * \brief Creates missing boundary elements
11378 * \param elements - elements whose boundary is to be checked
11379 * \param dimension - defines type of boundary elements to create
11380 * \param group - a group to store created boundary elements in
11381 * \param targetMesh - a mesh to store created boundary elements in
11382 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11383 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11384 * boundary elements will be copied into the targetMesh
11385 * \param toAddExistingBondary - if true, not only new but also pre-existing
11386 * boundary elements will be added into the new group
11387 * \param aroundElements - if true, elements will be created on boundary of given
11388 * elements else, on boundary of the whole mesh.
11389 * \return nb of added boundary elements
11391 //================================================================================
11393 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11394 Bnd_Dimension dimension,
11395 SMESH_Group* group/*=0*/,
11396 SMESH_Mesh* targetMesh/*=0*/,
11397 bool toCopyElements/*=false*/,
11398 bool toCopyExistingBoundary/*=false*/,
11399 bool toAddExistingBondary/*= false*/,
11400 bool aroundElements/*= false*/)
11402 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11403 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11404 // hope that all elements are of the same type, do not check them all
11405 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11406 throw SALOME_Exception(LOCALIZED("wrong element type"));
11409 toCopyElements = toCopyExistingBoundary = false;
11411 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11412 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11413 int nbAddedBnd = 0;
11415 // editor adding present bnd elements and optionally holding elements to add to the group
11416 SMESH_MeshEditor* presentEditor;
11417 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11418 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11420 SMDS_VolumeTool vTool;
11421 TIDSortedElemSet avoidSet;
11422 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11425 typedef vector<const SMDS_MeshNode*> TConnectivity;
11427 SMDS_ElemIteratorPtr eIt;
11428 if (elements.empty())
11429 eIt = aMesh->elementsIterator(elemType);
11431 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11433 while (eIt->more())
11435 const SMDS_MeshElement* elem = eIt->next();
11436 const int iQuad = elem->IsQuadratic();
11438 // ------------------------------------------------------------------------------------
11439 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11440 // ------------------------------------------------------------------------------------
11441 vector<const SMDS_MeshElement*> presentBndElems;
11442 vector<TConnectivity> missingBndElems;
11443 TConnectivity nodes;
11444 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11446 vTool.SetExternalNormal();
11447 const SMDS_MeshElement* otherVol = 0;
11448 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11450 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11451 ( !aroundElements || elements.count( otherVol )))
11453 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11454 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11455 if ( missType == SMDSAbs_Edge ) // boundary edges
11457 nodes.resize( 2+iQuad );
11458 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11460 for ( int j = 0; j < nodes.size(); ++j )
11462 if ( const SMDS_MeshElement* edge =
11463 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11464 presentBndElems.push_back( edge );
11466 missingBndElems.push_back( nodes );
11469 else // boundary face
11472 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11473 nodes.push_back( nn[inode] );
11474 if (iQuad) // add medium nodes
11475 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11476 nodes.push_back( nn[inode] );
11477 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11479 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11481 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11482 SMDSAbs_Face, /*noMedium=*/false ))
11483 presentBndElems.push_back( f );
11485 missingBndElems.push_back( nodes );
11487 if ( targetMesh != myMesh )
11489 // add 1D elements on face boundary to be added to a new mesh
11490 const SMDS_MeshElement* edge;
11491 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11494 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11496 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11497 if ( edge && avoidSet.insert( edge ).second )
11498 presentBndElems.push_back( edge );
11504 else // elem is a face ------------------------------------------
11506 avoidSet.clear(), avoidSet.insert( elem );
11507 int nbNodes = elem->NbCornerNodes();
11508 nodes.resize( 2 /*+ iQuad*/);
11509 for ( int i = 0; i < nbNodes; i++ )
11511 nodes[0] = elem->GetNode(i);
11512 nodes[1] = elem->GetNode((i+1)%nbNodes);
11513 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11514 continue; // not free link
11517 //nodes[2] = elem->GetNode( i + nbNodes );
11518 if ( const SMDS_MeshElement* edge =
11519 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11520 presentBndElems.push_back( edge );
11522 missingBndElems.push_back( nodes );
11526 // ---------------------------------
11527 // 2. Add missing boundary elements
11528 // ---------------------------------
11529 if ( targetMesh != myMesh )
11530 // instead of making a map of nodes in this mesh and targetMesh,
11531 // we create nodes with same IDs.
11532 for ( int i = 0; i < missingBndElems.size(); ++i )
11534 TConnectivity& srcNodes = missingBndElems[i];
11535 TConnectivity nodes( srcNodes.size() );
11536 for ( inode = 0; inode < nodes.size(); ++inode )
11537 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11538 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11540 /*noMedium=*/false))
11542 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11546 for ( int i = 0; i < missingBndElems.size(); ++i )
11548 TConnectivity& nodes = missingBndElems[i];
11549 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11551 /*noMedium=*/false))
11553 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11557 // ----------------------------------
11558 // 3. Copy present boundary elements
11559 // ----------------------------------
11560 if ( toCopyExistingBoundary )
11561 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11563 const SMDS_MeshElement* e = presentBndElems[i];
11564 TConnectivity nodes( e->NbNodes() );
11565 for ( inode = 0; inode < nodes.size(); ++inode )
11566 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11567 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11569 else // store present elements to add them to a group
11570 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11572 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11575 } // loop on given elements
11577 // ---------------------------------------------
11578 // 4. Fill group with boundary elements
11579 // ---------------------------------------------
11582 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11583 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11584 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11586 tgtEditor.myLastCreatedElems.Clear();
11587 tgtEditor2.myLastCreatedElems.Clear();
11589 // -----------------------
11590 // 5. Copy given elements
11591 // -----------------------
11592 if ( toCopyElements && targetMesh != myMesh )
11594 if (elements.empty())
11595 eIt = aMesh->elementsIterator(elemType);
11597 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11598 while (eIt->more())
11600 const SMDS_MeshElement* elem = eIt->next();
11601 TConnectivity nodes( elem->NbNodes() );
11602 for ( inode = 0; inode < nodes.size(); ++inode )
11603 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11604 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11606 tgtEditor.myLastCreatedElems.Clear();