1 // Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // SMESH SMESH : idl implementation based on 'SMESH' unit's classes
24 // File : SMESH_MeshEditor.cxx
25 // Created : Mon Apr 12 16:10:22 2004
26 // Author : Edward AGAPOV (eap)
28 #include "SMESH_MeshEditor.hxx"
30 #include "SMDS_FaceOfNodes.hxx"
31 #include "SMDS_VolumeTool.hxx"
32 #include "SMDS_EdgePosition.hxx"
33 #include "SMDS_FacePosition.hxx"
34 #include "SMDS_SpacePosition.hxx"
35 #include "SMDS_MeshGroup.hxx"
36 #include "SMDS_LinearEdge.hxx"
37 #include "SMDS_Downward.hxx"
38 #include "SMDS_SetIterator.hxx"
40 #include "SMESHDS_Group.hxx"
41 #include "SMESHDS_Mesh.hxx"
43 #include "SMESH_Algo.hxx"
44 #include "SMESH_ControlsDef.hxx"
45 #include "SMESH_Group.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <GC_MakeSegment.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAPI_ExtremaCurveCurve.hxx>
65 #include <GeomAdaptor_Surface.hxx>
66 #include <Geom_Curve.hxx>
67 #include <Geom_Line.hxx>
68 #include <Geom_Surface.hxx>
69 #include <IntAna_IntConicQuad.hxx>
70 #include <IntAna_Quadric.hxx>
71 #include <Precision.hxx>
72 #include <TColStd_ListOfInteger.hxx>
73 #include <TopAbs_State.hxx>
75 #include <TopExp_Explorer.hxx>
76 #include <TopTools_ListIteratorOfListOfShape.hxx>
77 #include <TopTools_ListOfShape.hxx>
78 #include <TopTools_SequenceOfShape.hxx>
80 #include <TopoDS_Face.hxx>
86 #include <gp_Trsf.hxx>
100 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103 using namespace SMESH::Controls;
105 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
106 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
108 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
110 //=======================================================================
111 //function : SMESH_MeshEditor
113 //=======================================================================
115 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
116 :myMesh( theMesh ) // theMesh may be NULL
120 //=======================================================================
124 //=======================================================================
127 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
128 const SMDSAbs_ElementType type,
132 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
133 SMDS_MeshElement* e = 0;
134 int nbnode = node.size();
135 SMESHDS_Mesh* mesh = GetMeshDS();
140 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
141 else e = mesh->AddFace (node[0], node[1], node[2] );
143 else if (nbnode == 4) {
144 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
145 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
147 else if (nbnode == 6) {
148 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
149 node[4], node[5], ID);
150 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
153 else if (nbnode == 8) {
154 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
155 node[4], node[5], node[6], node[7], ID);
156 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
157 node[4], node[5], node[6], node[7] );
159 else if (nbnode == 9) {
160 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
161 node[4], node[5], node[6], node[7], node[8], ID);
162 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
163 node[4], node[5], node[6], node[7], node[8] );
166 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
167 else e = mesh->AddPolygonalFace (node );
174 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
175 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
177 else if (nbnode == 5) {
178 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
180 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
183 else if (nbnode == 6) {
184 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
185 node[4], node[5], ID);
186 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
189 else if (nbnode == 8) {
190 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
191 node[4], node[5], node[6], node[7], ID);
192 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
193 node[4], node[5], node[6], node[7] );
195 else if (nbnode == 10) {
196 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
197 node[4], node[5], node[6], node[7],
198 node[8], node[9], ID);
199 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
200 node[4], node[5], node[6], node[7],
203 else if (nbnode == 12) {
204 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
205 node[4], node[5], node[6], node[7],
206 node[8], node[9], node[10], node[11], ID);
207 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
208 node[4], node[5], node[6], node[7],
209 node[8], node[9], node[10], node[11] );
211 else if (nbnode == 13) {
212 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6], node[7],
214 node[8], node[9], node[10],node[11],
216 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7],
218 node[8], node[9], node[10],node[11],
221 else if (nbnode == 15) {
222 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7],
224 node[8], node[9], node[10],node[11],
225 node[12],node[13],node[14],ID);
226 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7],
228 node[8], node[9], node[10],node[11],
229 node[12],node[13],node[14] );
231 else if (nbnode == 20) {
232 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7],
234 node[8], node[9], node[10],node[11],
235 node[12],node[13],node[14],node[15],
236 node[16],node[17],node[18],node[19],ID);
237 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
238 node[4], node[5], node[6], node[7],
239 node[8], node[9], node[10],node[11],
240 node[12],node[13],node[14],node[15],
241 node[16],node[17],node[18],node[19] );
243 else if (nbnode == 27) {
244 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
245 node[4], node[5], node[6], node[7],
246 node[8], node[9], node[10],node[11],
247 node[12],node[13],node[14],node[15],
248 node[16],node[17],node[18],node[19],
249 node[20],node[21],node[22],node[23],
250 node[24],node[25],node[26], ID);
251 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
252 node[4], node[5], node[6], node[7],
253 node[8], node[9], node[10],node[11],
254 node[12],node[13],node[14],node[15],
255 node[16],node[17],node[18],node[19],
256 node[20],node[21],node[22],node[23],
257 node[24],node[25],node[26] );
264 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
265 else e = mesh->AddEdge (node[0], node[1] );
267 else if ( nbnode == 3 ) {
268 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
269 else e = mesh->AddEdge (node[0], node[1], node[2] );
273 case SMDSAbs_0DElement:
275 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
276 else e = mesh->Add0DElement (node[0] );
281 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
282 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
287 if ( e ) myLastCreatedElems.Append( e );
291 //=======================================================================
295 //=======================================================================
297 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
298 const SMDSAbs_ElementType type,
302 vector<const SMDS_MeshNode*> nodes;
303 nodes.reserve( nodeIDs.size() );
304 vector<int>::const_iterator id = nodeIDs.begin();
305 while ( id != nodeIDs.end() ) {
306 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
307 nodes.push_back( node );
311 return AddElement( nodes, type, isPoly, ID );
314 //=======================================================================
316 //purpose : Remove a node or an element.
317 // Modify a compute state of sub-meshes which become empty
318 //=======================================================================
320 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
323 myLastCreatedElems.Clear();
324 myLastCreatedNodes.Clear();
326 SMESHDS_Mesh* aMesh = GetMeshDS();
327 set< SMESH_subMesh *> smmap;
330 list<int>::const_iterator it = theIDs.begin();
331 for ( ; it != theIDs.end(); it++ ) {
332 const SMDS_MeshElement * elem;
334 elem = aMesh->FindNode( *it );
336 elem = aMesh->FindElement( *it );
340 // Notify VERTEX sub-meshes about modification
342 const SMDS_MeshNode* node = cast2Node( elem );
343 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
344 if ( int aShapeID = node->getshapeId() )
345 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
348 // Find sub-meshes to notify about modification
349 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
350 // while ( nodeIt->more() ) {
351 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
352 // const SMDS_PositionPtr& aPosition = node->GetPosition();
353 // if ( aPosition.get() ) {
354 // if ( int aShapeID = aPosition->GetShapeId() ) {
355 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
356 // smmap.insert( sm );
363 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
365 aMesh->RemoveElement( elem );
369 // Notify sub-meshes about modification
370 if ( !smmap.empty() ) {
371 set< SMESH_subMesh *>::iterator smIt;
372 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
373 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
376 // // Check if the whole mesh becomes empty
377 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
378 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
383 //=======================================================================
384 //function : FindShape
385 //purpose : Return an index of the shape theElem is on
386 // or zero if a shape not found
387 //=======================================================================
389 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
391 myLastCreatedElems.Clear();
392 myLastCreatedNodes.Clear();
394 SMESHDS_Mesh * aMesh = GetMeshDS();
395 if ( aMesh->ShapeToMesh().IsNull() )
398 int aShapeID = theElem->getshapeId();
402 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
403 if ( sm->Contains( theElem ))
406 if ( theElem->GetType() == SMDSAbs_Node ) {
407 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
410 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
413 TopoDS_Shape aShape; // the shape a node of theElem is on
414 if ( theElem->GetType() != SMDSAbs_Node )
416 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
417 while ( nodeIt->more() ) {
418 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
419 if ((aShapeID = node->getshapeId()) > 0) {
420 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
421 if ( sm->Contains( theElem ))
423 if ( aShape.IsNull() )
424 aShape = aMesh->IndexToShape( aShapeID );
430 // None of nodes is on a proper shape,
431 // find the shape among ancestors of aShape on which a node is
432 if ( !aShape.IsNull() ) {
433 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
434 for ( ; ancIt.More(); ancIt.Next() ) {
435 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
436 if ( sm && sm->Contains( theElem ))
437 return aMesh->ShapeToIndex( ancIt.Value() );
442 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
443 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
444 for ( ; id_sm != id2sm.end(); ++id_sm )
445 if ( id_sm->second->Contains( theElem ))
449 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
453 //=======================================================================
454 //function : IsMedium
456 //=======================================================================
458 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
459 const SMDSAbs_ElementType typeToCheck)
461 bool isMedium = false;
462 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
463 while (it->more() && !isMedium ) {
464 const SMDS_MeshElement* elem = it->next();
465 isMedium = elem->IsMediumNode(node);
470 //=======================================================================
471 //function : ShiftNodesQuadTria
473 // Shift nodes in the array corresponded to quadratic triangle
474 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
475 //=======================================================================
476 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
478 const SMDS_MeshNode* nd1 = aNodes[0];
479 aNodes[0] = aNodes[1];
480 aNodes[1] = aNodes[2];
482 const SMDS_MeshNode* nd2 = aNodes[3];
483 aNodes[3] = aNodes[4];
484 aNodes[4] = aNodes[5];
488 //=======================================================================
489 //function : edgeConnectivity
491 // return number of the edges connected with the theNode.
492 // if theEdges has connections with the other type of the
493 // elements, return -1
494 //=======================================================================
495 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
497 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
499 while(elemIt->more()) {
507 //=======================================================================
508 //function : GetNodesFromTwoTria
510 // Shift nodes in the array corresponded to quadratic triangle
511 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
512 //=======================================================================
513 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
514 const SMDS_MeshElement * theTria2,
515 const SMDS_MeshNode* N1[],
516 const SMDS_MeshNode* N2[])
518 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
521 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
524 if(it->more()) return false;
525 it = theTria2->nodesIterator();
528 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
531 if(it->more()) return false;
533 int sames[3] = {-1,-1,-1};
545 if(nbsames!=2) return false;
547 ShiftNodesQuadTria(N1);
549 ShiftNodesQuadTria(N1);
552 i = sames[0] + sames[1] + sames[2];
554 ShiftNodesQuadTria(N2);
556 // now we receive following N1 and N2 (using numeration as above image)
557 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
558 // i.e. first nodes from both arrays determ new diagonal
562 //=======================================================================
563 //function : InverseDiag
564 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
565 // but having other common link.
566 // Return False if args are improper
567 //=======================================================================
569 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
570 const SMDS_MeshElement * theTria2 )
572 MESSAGE("InverseDiag");
573 myLastCreatedElems.Clear();
574 myLastCreatedNodes.Clear();
576 if (!theTria1 || !theTria2)
579 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
580 if (!F1) return false;
581 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
582 if (!F2) return false;
583 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
584 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
586 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
587 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
591 // put nodes in array and find out indices of the same ones
592 const SMDS_MeshNode* aNodes [6];
593 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
595 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
596 while ( it->more() ) {
597 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
599 if ( i > 2 ) // theTria2
600 // find same node of theTria1
601 for ( int j = 0; j < 3; j++ )
602 if ( aNodes[ i ] == aNodes[ j ]) {
611 return false; // theTria1 is not a triangle
612 it = theTria2->nodesIterator();
614 if ( i == 6 && it->more() )
615 return false; // theTria2 is not a triangle
618 // find indices of 1,2 and of A,B in theTria1
619 int iA = 0, iB = 0, i1 = 0, i2 = 0;
620 for ( i = 0; i < 6; i++ ) {
621 if ( sameInd [ i ] == 0 ) {
630 // nodes 1 and 2 should not be the same
631 if ( aNodes[ i1 ] == aNodes[ i2 ] )
635 aNodes[ iA ] = aNodes[ i2 ];
637 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
639 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
640 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
644 } // end if(F1 && F2)
646 // check case of quadratic faces
647 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
649 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
653 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
654 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
662 const SMDS_MeshNode* N1 [6];
663 const SMDS_MeshNode* N2 [6];
664 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
666 // now we receive following N1 and N2 (using numeration as above image)
667 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
668 // i.e. first nodes from both arrays determ new diagonal
670 const SMDS_MeshNode* N1new [6];
671 const SMDS_MeshNode* N2new [6];
684 // replaces nodes in faces
685 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
686 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
691 //=======================================================================
692 //function : findTriangles
693 //purpose : find triangles sharing theNode1-theNode2 link
694 //=======================================================================
696 static bool findTriangles(const SMDS_MeshNode * theNode1,
697 const SMDS_MeshNode * theNode2,
698 const SMDS_MeshElement*& theTria1,
699 const SMDS_MeshElement*& theTria2)
701 if ( !theNode1 || !theNode2 ) return false;
703 theTria1 = theTria2 = 0;
705 set< const SMDS_MeshElement* > emap;
706 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
708 const SMDS_MeshElement* elem = it->next();
709 if ( elem->NbNodes() == 3 )
712 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
714 const SMDS_MeshElement* elem = it->next();
715 if ( emap.find( elem ) != emap.end() ) {
717 // theTria1 must be element with minimum ID
718 if( theTria1->GetID() < elem->GetID() ) {
732 return ( theTria1 && theTria2 );
735 //=======================================================================
736 //function : InverseDiag
737 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
738 // with ones built on the same 4 nodes but having other common link.
739 // Return false if proper faces not found
740 //=======================================================================
742 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
743 const SMDS_MeshNode * theNode2)
745 myLastCreatedElems.Clear();
746 myLastCreatedNodes.Clear();
748 MESSAGE( "::InverseDiag()" );
750 const SMDS_MeshElement *tr1, *tr2;
751 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
754 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
755 if (!F1) return false;
756 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
757 if (!F2) return false;
758 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
759 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
761 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
762 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
766 // put nodes in array
767 // and find indices of 1,2 and of A in tr1 and of B in tr2
768 int i, iA1 = 0, i1 = 0;
769 const SMDS_MeshNode* aNodes1 [3];
770 SMDS_ElemIteratorPtr it;
771 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
772 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
773 if ( aNodes1[ i ] == theNode1 )
774 iA1 = i; // node A in tr1
775 else if ( aNodes1[ i ] != theNode2 )
779 const SMDS_MeshNode* aNodes2 [3];
780 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
781 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
782 if ( aNodes2[ i ] == theNode2 )
783 iB2 = i; // node B in tr2
784 else if ( aNodes2[ i ] != theNode1 )
788 // nodes 1 and 2 should not be the same
789 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
793 aNodes1[ iA1 ] = aNodes2[ i2 ];
795 aNodes2[ iB2 ] = aNodes1[ i1 ];
797 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
798 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
803 // check case of quadratic faces
804 return InverseDiag(tr1,tr2);
807 //=======================================================================
808 //function : getQuadrangleNodes
809 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
810 // fusion of triangles tr1 and tr2 having shared link on
811 // theNode1 and theNode2
812 //=======================================================================
814 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
815 const SMDS_MeshNode * theNode1,
816 const SMDS_MeshNode * theNode2,
817 const SMDS_MeshElement * tr1,
818 const SMDS_MeshElement * tr2 )
820 if( tr1->NbNodes() != tr2->NbNodes() )
822 // find the 4-th node to insert into tr1
823 const SMDS_MeshNode* n4 = 0;
824 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
826 while ( !n4 && i<3 ) {
827 const SMDS_MeshNode * n = cast2Node( it->next() );
829 bool isDiag = ( n == theNode1 || n == theNode2 );
833 // Make an array of nodes to be in a quadrangle
834 int iNode = 0, iFirstDiag = -1;
835 it = tr1->nodesIterator();
838 const SMDS_MeshNode * n = cast2Node( it->next() );
840 bool isDiag = ( n == theNode1 || n == theNode2 );
842 if ( iFirstDiag < 0 )
844 else if ( iNode - iFirstDiag == 1 )
845 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
847 else if ( n == n4 ) {
848 return false; // tr1 and tr2 should not have all the same nodes
850 theQuadNodes[ iNode++ ] = n;
852 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
853 theQuadNodes[ iNode ] = n4;
858 //=======================================================================
859 //function : DeleteDiag
860 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
861 // with a quadrangle built on the same 4 nodes.
862 // Return false if proper faces not found
863 //=======================================================================
865 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
866 const SMDS_MeshNode * theNode2)
868 myLastCreatedElems.Clear();
869 myLastCreatedNodes.Clear();
871 MESSAGE( "::DeleteDiag()" );
873 const SMDS_MeshElement *tr1, *tr2;
874 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
877 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
878 if (!F1) return false;
879 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
880 if (!F2) return false;
881 SMESHDS_Mesh * aMesh = GetMeshDS();
883 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
884 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
886 const SMDS_MeshNode* aNodes [ 4 ];
887 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
890 const SMDS_MeshElement* newElem = 0;
891 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
892 myLastCreatedElems.Append(newElem);
893 AddToSameGroups( newElem, tr1, aMesh );
894 int aShapeId = tr1->getshapeId();
897 aMesh->SetMeshElementOnShape( newElem, aShapeId );
899 aMesh->RemoveElement( tr1 );
900 aMesh->RemoveElement( tr2 );
905 // check case of quadratic faces
906 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
908 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
912 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
913 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
921 const SMDS_MeshNode* N1 [6];
922 const SMDS_MeshNode* N2 [6];
923 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
925 // now we receive following N1 and N2 (using numeration as above image)
926 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
927 // i.e. first nodes from both arrays determ new diagonal
929 const SMDS_MeshNode* aNodes[8];
939 const SMDS_MeshElement* newElem = 0;
940 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
941 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
942 myLastCreatedElems.Append(newElem);
943 AddToSameGroups( newElem, tr1, aMesh );
944 int aShapeId = tr1->getshapeId();
947 aMesh->SetMeshElementOnShape( newElem, aShapeId );
949 aMesh->RemoveElement( tr1 );
950 aMesh->RemoveElement( tr2 );
952 // remove middle node (9)
953 GetMeshDS()->RemoveNode( N1[4] );
958 //=======================================================================
959 //function : Reorient
960 //purpose : Reverse theElement orientation
961 //=======================================================================
963 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
966 myLastCreatedElems.Clear();
967 myLastCreatedNodes.Clear();
971 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
972 if ( !it || !it->more() )
975 switch ( theElem->GetType() ) {
979 if(!theElem->IsQuadratic()) {
980 int i = theElem->NbNodes();
981 vector<const SMDS_MeshNode*> aNodes( i );
983 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
984 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
987 // quadratic elements
988 if(theElem->GetType()==SMDSAbs_Edge) {
989 vector<const SMDS_MeshNode*> aNodes(3);
990 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
991 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
992 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
993 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
996 int nbn = theElem->NbNodes();
997 vector<const SMDS_MeshNode*> aNodes(nbn);
998 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1000 for(; i<nbn/2; i++) {
1001 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1003 for(i=0; i<nbn/2; i++) {
1004 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1006 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1010 case SMDSAbs_Volume: {
1011 if (theElem->IsPoly()) {
1012 // TODO reorient vtk polyhedron
1013 MESSAGE("reorient vtk polyhedron ?");
1014 const SMDS_VtkVolume* aPolyedre =
1015 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1017 MESSAGE("Warning: bad volumic element");
1021 int nbFaces = aPolyedre->NbFaces();
1022 vector<const SMDS_MeshNode *> poly_nodes;
1023 vector<int> quantities (nbFaces);
1025 // reverse each face of the polyedre
1026 for (int iface = 1; iface <= nbFaces; iface++) {
1027 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1028 quantities[iface - 1] = nbFaceNodes;
1030 for (inode = nbFaceNodes; inode >= 1; inode--) {
1031 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1032 poly_nodes.push_back(curNode);
1036 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1040 SMDS_VolumeTool vTool;
1041 if ( !vTool.Set( theElem ))
1044 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1045 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1054 //=======================================================================
1055 //function : getBadRate
1057 //=======================================================================
1059 static double getBadRate (const SMDS_MeshElement* theElem,
1060 SMESH::Controls::NumericalFunctorPtr& theCrit)
1062 SMESH::Controls::TSequenceOfXYZ P;
1063 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1065 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1066 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1069 //=======================================================================
1070 //function : QuadToTri
1071 //purpose : Cut quadrangles into triangles.
1072 // theCrit is used to select a diagonal to cut
1073 //=======================================================================
1075 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1076 SMESH::Controls::NumericalFunctorPtr theCrit)
1078 myLastCreatedElems.Clear();
1079 myLastCreatedNodes.Clear();
1081 MESSAGE( "::QuadToTri()" );
1083 if ( !theCrit.get() )
1086 SMESHDS_Mesh * aMesh = GetMeshDS();
1088 Handle(Geom_Surface) surface;
1089 SMESH_MesherHelper helper( *GetMesh() );
1091 TIDSortedElemSet::iterator itElem;
1092 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1093 const SMDS_MeshElement* elem = *itElem;
1094 if ( !elem || elem->GetType() != SMDSAbs_Face )
1096 if ( elem->NbCornerNodes() != 4 )
1099 // retrieve element nodes
1100 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1102 // compare two sets of possible triangles
1103 double aBadRate1, aBadRate2; // to what extent a set is bad
1104 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1105 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1106 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1108 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1109 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1110 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1112 int aShapeId = FindShape( elem );
1113 const SMDS_MeshElement* newElem1 = 0;
1114 const SMDS_MeshElement* newElem2 = 0;
1116 if( !elem->IsQuadratic() ) {
1118 // split liner quadrangle
1120 if ( aBadRate1 <= aBadRate2 ) {
1121 // tr1 + tr2 is better
1122 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1123 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1126 // tr3 + tr4 is better
1127 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1128 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1133 // split quadratic quadrangle
1135 // get surface elem is on
1136 if ( aShapeId != helper.GetSubShapeID() ) {
1140 shape = aMesh->IndexToShape( aShapeId );
1141 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1142 TopoDS_Face face = TopoDS::Face( shape );
1143 surface = BRep_Tool::Surface( face );
1144 if ( !surface.IsNull() )
1145 helper.SetSubShape( shape );
1148 // find middle point for (0,1,2,3)
1149 // and create a node in this point;
1150 const SMDS_MeshNode* newN = 0;
1151 if ( aNodes.size() == 9 )
1153 // SMDSEntity_BiQuad_Quadrangle
1154 newN = aNodes.back();
1159 if ( surface.IsNull() )
1161 for ( int i = 0; i < 4; i++ )
1162 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1167 const SMDS_MeshNode* inFaceNode = 0;
1168 if ( helper.GetNodeUVneedInFaceNode() )
1169 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1170 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1171 inFaceNode = aNodes[ i ];
1173 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1175 for ( int i = 0; i < 4; i++ )
1176 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1178 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1180 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1181 myLastCreatedNodes.Append(newN);
1183 // create a new element
1184 if ( aBadRate1 <= aBadRate2 ) {
1185 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1186 aNodes[6], aNodes[7], newN );
1187 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1188 newN, aNodes[4], aNodes[5] );
1191 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1192 aNodes[7], aNodes[4], newN );
1193 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1194 newN, aNodes[5], aNodes[6] );
1198 // care of a new element
1200 myLastCreatedElems.Append(newElem1);
1201 myLastCreatedElems.Append(newElem2);
1202 AddToSameGroups( newElem1, elem, aMesh );
1203 AddToSameGroups( newElem2, elem, aMesh );
1205 // put a new triangle on the same shape
1208 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1209 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1211 aMesh->RemoveElement( elem );
1216 //=======================================================================
1217 //function : BestSplit
1218 //purpose : Find better diagonal for cutting.
1219 //=======================================================================
1221 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1222 SMESH::Controls::NumericalFunctorPtr theCrit)
1224 myLastCreatedElems.Clear();
1225 myLastCreatedNodes.Clear();
1230 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1233 if( theQuad->NbNodes()==4 ||
1234 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1236 // retrieve element nodes
1237 const SMDS_MeshNode* aNodes [4];
1238 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1240 //while (itN->more())
1242 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1244 // compare two sets of possible triangles
1245 double aBadRate1, aBadRate2; // to what extent a set is bad
1246 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1247 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1248 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1250 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1251 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1252 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1254 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1255 return 1; // diagonal 1-3
1257 return 2; // diagonal 2-4
1264 // Methods of splitting volumes into tetra
1266 const int theHexTo5_1[5*4+1] =
1268 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1270 const int theHexTo5_2[5*4+1] =
1272 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1274 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1276 const int theHexTo6_1[6*4+1] =
1278 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
1280 const int theHexTo6_2[6*4+1] =
1282 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
1284 const int theHexTo6_3[6*4+1] =
1286 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
1288 const int theHexTo6_4[6*4+1] =
1290 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
1292 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1294 const int thePyraTo2_1[2*4+1] =
1296 0, 1, 2, 4, 0, 2, 3, 4, -1
1298 const int thePyraTo2_2[2*4+1] =
1300 1, 2, 3, 4, 1, 3, 0, 4, -1
1302 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1304 const int thePentaTo3_1[3*4+1] =
1306 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1308 const int thePentaTo3_2[3*4+1] =
1310 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1312 const int thePentaTo3_3[3*4+1] =
1314 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1316 const int thePentaTo3_4[3*4+1] =
1318 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1320 const int thePentaTo3_5[3*4+1] =
1322 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1324 const int thePentaTo3_6[3*4+1] =
1326 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1328 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1329 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1331 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1334 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1335 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1336 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1341 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1342 bool _baryNode; //!< additional node is to be created at cell barycenter
1343 bool _ownConn; //!< to delete _connectivity in destructor
1344 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1346 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1347 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1348 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1349 bool hasFacet( const TTriangleFacet& facet ) const
1351 const int* tetConn = _connectivity;
1352 for ( ; tetConn[0] >= 0; tetConn += 4 )
1353 if (( facet.contains( tetConn[0] ) +
1354 facet.contains( tetConn[1] ) +
1355 facet.contains( tetConn[2] ) +
1356 facet.contains( tetConn[3] )) == 3 )
1362 //=======================================================================
1364 * \brief return TSplitMethod for the given element
1366 //=======================================================================
1368 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1370 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1372 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1373 // an edge and a face barycenter; tertaherdons are based on triangles and
1374 // a volume barycenter
1375 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1377 // Find out how adjacent volumes are split
1379 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1380 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1381 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1383 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1384 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1385 if ( nbNodes < 4 ) continue;
1387 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1388 const int* nInd = vol.GetFaceNodesIndices( iF );
1391 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1392 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1393 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1394 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1398 int iCom = 0; // common node of triangle faces to split into
1399 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1401 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1402 nInd[ iQ * ( (iCom+1)%nbNodes )],
1403 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1404 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1405 nInd[ iQ * ( (iCom+2)%nbNodes )],
1406 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1407 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1409 triaSplits.push_back( t012 );
1410 triaSplits.push_back( t023 );
1415 if ( !triaSplits.empty() )
1416 hasAdjacentSplits = true;
1419 // Among variants of split method select one compliant with adjacent volumes
1421 TSplitMethod method;
1422 if ( !vol.Element()->IsPoly() && !is24TetMode )
1424 int nbVariants = 2, nbTet = 0;
1425 const int** connVariants = 0;
1426 switch ( vol.Element()->GetEntityType() )
1428 case SMDSEntity_Hexa:
1429 case SMDSEntity_Quad_Hexa:
1430 case SMDSEntity_TriQuad_Hexa:
1431 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1432 connVariants = theHexTo5, nbTet = 5;
1434 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1436 case SMDSEntity_Pyramid:
1437 case SMDSEntity_Quad_Pyramid:
1438 connVariants = thePyraTo2; nbTet = 2;
1440 case SMDSEntity_Penta:
1441 case SMDSEntity_Quad_Penta:
1442 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1447 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1449 // check method compliancy with adjacent tetras,
1450 // all found splits must be among facets of tetras described by this method
1451 method = TSplitMethod( nbTet, connVariants[variant] );
1452 if ( hasAdjacentSplits && method._nbTetra > 0 )
1454 bool facetCreated = true;
1455 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1457 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1458 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1459 facetCreated = method.hasFacet( *facet );
1461 if ( !facetCreated )
1462 method = TSplitMethod(0); // incompatible method
1466 if ( method._nbTetra < 1 )
1468 // No standard method is applicable, use a generic solution:
1469 // each facet of a volume is split into triangles and
1470 // each of triangles and a volume barycenter form a tetrahedron.
1472 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1474 int* connectivity = new int[ maxTetConnSize + 1 ];
1475 method._connectivity = connectivity;
1476 method._ownConn = true;
1477 method._baryNode = !isHex27; // to create central node or not
1480 int baryCenInd = vol.NbNodes() - int( isHex27 );
1481 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1483 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1484 const int* nInd = vol.GetFaceNodesIndices( iF );
1485 // find common node of triangle facets of tetra to create
1486 int iCommon = 0; // index in linear numeration
1487 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1488 if ( !triaSplits.empty() )
1491 const TTriangleFacet* facet = &triaSplits.front();
1492 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1493 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1494 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1497 else if ( nbNodes > 3 && !is24TetMode )
1499 // find the best method of splitting into triangles by aspect ratio
1500 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1501 map< double, int > badness2iCommon;
1502 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1503 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1504 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1507 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1509 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1510 nodes[ iQ*((iLast-1)%nbNodes)],
1511 nodes[ iQ*((iLast )%nbNodes)]);
1512 badness += getBadRate( &tria, aspectRatio );
1514 badness2iCommon.insert( make_pair( badness, iCommon ));
1516 // use iCommon with lowest badness
1517 iCommon = badness2iCommon.begin()->second;
1519 if ( iCommon >= nbNodes )
1520 iCommon = 0; // something wrong
1522 // fill connectivity of tetrahedra based on a current face
1523 int nbTet = nbNodes - 2;
1524 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1529 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1530 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1534 method._faceBaryNode[ iF ] = 0;
1535 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1538 for ( int i = 0; i < nbTet; ++i )
1540 int i1 = i, i2 = (i+1) % nbNodes;
1541 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1542 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1543 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1544 connectivity[ connSize++ ] = faceBaryCenInd;
1545 connectivity[ connSize++ ] = baryCenInd;
1550 for ( int i = 0; i < nbTet; ++i )
1552 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1553 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1554 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1555 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1556 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1557 connectivity[ connSize++ ] = baryCenInd;
1560 method._nbTetra += nbTet;
1562 } // loop on volume faces
1564 connectivity[ connSize++ ] = -1;
1566 } // end of generic solution
1570 //================================================================================
1572 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1574 //================================================================================
1576 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1578 // find the tetrahedron including the three nodes of facet
1579 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1580 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1581 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1582 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1583 while ( volIt1->more() )
1585 const SMDS_MeshElement* v = volIt1->next();
1586 SMDSAbs_EntityType type = v->GetEntityType();
1587 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1589 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1590 continue; // medium node not allowed
1591 const int ind2 = v->GetNodeIndex( n2 );
1592 if ( ind2 < 0 || 3 < ind2 )
1594 const int ind3 = v->GetNodeIndex( n3 );
1595 if ( ind3 < 0 || 3 < ind3 )
1602 //=======================================================================
1604 * \brief A key of a face of volume
1606 //=======================================================================
1608 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1610 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1612 TIDSortedNodeSet sortedNodes;
1613 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1614 int nbNodes = vol.NbFaceNodes( iF );
1615 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1616 for ( int i = 0; i < nbNodes; i += iQ )
1617 sortedNodes.insert( fNodes[i] );
1618 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1619 first.first = (*(n++))->GetID();
1620 first.second = (*(n++))->GetID();
1621 second.first = (*(n++))->GetID();
1622 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1627 //=======================================================================
1628 //function : SplitVolumesIntoTetra
1629 //purpose : Split volume elements into tetrahedra.
1630 //=======================================================================
1632 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1633 const int theMethodFlags)
1635 // std-like iterator on coordinates of nodes of mesh element
1636 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1637 NXyzIterator xyzEnd;
1639 SMDS_VolumeTool volTool;
1640 SMESH_MesherHelper helper( *GetMesh());
1642 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1643 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1645 SMESH_SequenceOfElemPtr newNodes, newElems;
1647 // map face of volume to it's baricenrtic node
1648 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1651 TIDSortedElemSet::const_iterator elem = theElems.begin();
1652 for ( ; elem != theElems.end(); ++elem )
1654 if ( (*elem)->GetType() != SMDSAbs_Volume )
1656 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1657 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1660 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1662 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1663 if ( splitMethod._nbTetra < 1 ) continue;
1665 // find submesh to add new tetras to
1666 if ( !subMesh || !subMesh->Contains( *elem ))
1668 int shapeID = FindShape( *elem );
1669 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1670 subMesh = GetMeshDS()->MeshElements( shapeID );
1673 if ( (*elem)->IsQuadratic() )
1676 // add quadratic links to the helper
1677 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1679 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1680 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1681 for ( int iN = 0; iN < nbN; iN += iQ )
1682 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1684 helper.SetIsQuadratic( true );
1689 helper.SetIsQuadratic( false );
1691 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1692 helper.SetElementsOnShape( true );
1693 if ( splitMethod._baryNode )
1695 // make a node at barycenter
1696 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1697 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1698 nodes.push_back( gcNode );
1699 newNodes.Append( gcNode );
1701 if ( !splitMethod._faceBaryNode.empty() )
1703 // make or find baricentric nodes of faces
1704 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1705 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1707 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1708 volFace2BaryNode.insert
1709 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1712 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1713 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1715 nodes.push_back( iF_n->second = f_n->second );
1720 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1721 const int* tetConn = splitMethod._connectivity;
1722 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1723 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1724 nodes[ tetConn[1] ],
1725 nodes[ tetConn[2] ],
1726 nodes[ tetConn[3] ]));
1728 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1730 // Split faces on sides of the split volume
1732 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1733 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1735 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1736 if ( nbNodes < 4 ) continue;
1738 // find an existing face
1739 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1740 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1741 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1742 /*noMedium=*/false))
1745 helper.SetElementsOnShape( false );
1746 vector< const SMDS_MeshElement* > triangles;
1748 // find submesh to add new triangles in
1749 if ( !fSubMesh || !fSubMesh->Contains( face ))
1751 int shapeID = FindShape( face );
1752 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1754 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1755 if ( iF_n != splitMethod._faceBaryNode.end() )
1757 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1759 const SMDS_MeshNode* n1 = fNodes[iN];
1760 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1761 const SMDS_MeshNode *n3 = iF_n->second;
1762 if ( !volTool.IsFaceExternal( iF ))
1764 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1766 if ( fSubMesh && n3->getshapeId() < 1 )
1767 fSubMesh->AddNode( n3 );
1772 // among possible triangles create ones discribed by split method
1773 const int* nInd = volTool.GetFaceNodesIndices( iF );
1774 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1775 int iCom = 0; // common node of triangle faces to split into
1776 list< TTriangleFacet > facets;
1777 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1779 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1780 nInd[ iQ * ( (iCom+1)%nbNodes )],
1781 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1782 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1783 nInd[ iQ * ( (iCom+2)%nbNodes )],
1784 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1785 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1787 facets.push_back( t012 );
1788 facets.push_back( t023 );
1789 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1790 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1791 nInd[ iQ * ((iLast-1)%nbNodes )],
1792 nInd[ iQ * ((iLast )%nbNodes )]));
1796 list< TTriangleFacet >::iterator facet = facets.begin();
1797 for ( ; facet != facets.end(); ++facet )
1799 if ( !volTool.IsFaceExternal( iF ))
1800 swap( facet->_n2, facet->_n3 );
1801 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1802 volNodes[ facet->_n2 ],
1803 volNodes[ facet->_n3 ]));
1806 for ( int i = 0; i < triangles.size(); ++i )
1808 if ( !triangles[i] ) continue;
1810 fSubMesh->AddElement( triangles[i]);
1811 newElems.Append( triangles[i] );
1813 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1814 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1817 } // loop on volume faces to split them into triangles
1819 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1821 if ( geomType == SMDSEntity_TriQuad_Hexa )
1823 // remove medium nodes that could become free
1824 for ( int i = 20; i < volTool.NbNodes(); ++i )
1825 if ( volNodes[i]->NbInverseElements() == 0 )
1826 GetMeshDS()->RemoveNode( volNodes[i] );
1828 } // loop on volumes to split
1830 myLastCreatedNodes = newNodes;
1831 myLastCreatedElems = newElems;
1834 //=======================================================================
1835 //function : AddToSameGroups
1836 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1837 //=======================================================================
1839 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1840 const SMDS_MeshElement* elemInGroups,
1841 SMESHDS_Mesh * aMesh)
1843 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1844 if (!groups.empty()) {
1845 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1846 for ( ; grIt != groups.end(); grIt++ ) {
1847 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1848 if ( group && group->Contains( elemInGroups ))
1849 group->SMDSGroup().Add( elemToAdd );
1855 //=======================================================================
1856 //function : RemoveElemFromGroups
1857 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1858 //=======================================================================
1859 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1860 SMESHDS_Mesh * aMesh)
1862 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1863 if (!groups.empty())
1865 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1866 for (; GrIt != groups.end(); GrIt++)
1868 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1869 if (!grp || grp->IsEmpty()) continue;
1870 grp->SMDSGroup().Remove(removeelem);
1875 //================================================================================
1877 * \brief Replace elemToRm by elemToAdd in the all groups
1879 //================================================================================
1881 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1882 const SMDS_MeshElement* elemToAdd,
1883 SMESHDS_Mesh * aMesh)
1885 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1886 if (!groups.empty()) {
1887 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1888 for ( ; grIt != groups.end(); grIt++ ) {
1889 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1890 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1891 group->SMDSGroup().Add( elemToAdd );
1896 //================================================================================
1898 * \brief Replace elemToRm by elemToAdd in the all groups
1900 //================================================================================
1902 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1903 const vector<const SMDS_MeshElement*>& elemToAdd,
1904 SMESHDS_Mesh * aMesh)
1906 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1907 if (!groups.empty())
1909 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1910 for ( ; grIt != groups.end(); grIt++ ) {
1911 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1912 if ( group && group->SMDSGroup().Remove( elemToRm ) )
1913 for ( int i = 0; i < elemToAdd.size(); ++i )
1914 group->SMDSGroup().Add( elemToAdd[ i ] );
1919 //=======================================================================
1920 //function : QuadToTri
1921 //purpose : Cut quadrangles into triangles.
1922 // theCrit is used to select a diagonal to cut
1923 //=======================================================================
1925 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1926 const bool the13Diag)
1928 myLastCreatedElems.Clear();
1929 myLastCreatedNodes.Clear();
1931 MESSAGE( "::QuadToTri()" );
1933 SMESHDS_Mesh * aMesh = GetMeshDS();
1935 Handle(Geom_Surface) surface;
1936 SMESH_MesherHelper helper( *GetMesh() );
1938 TIDSortedElemSet::iterator itElem;
1939 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1940 const SMDS_MeshElement* elem = *itElem;
1941 if ( !elem || elem->GetType() != SMDSAbs_Face )
1943 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
1944 if(!isquad) continue;
1946 if(elem->NbNodes()==4) {
1947 // retrieve element nodes
1948 const SMDS_MeshNode* aNodes [4];
1949 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
1951 while ( itN->more() )
1952 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1954 int aShapeId = FindShape( elem );
1955 const SMDS_MeshElement* newElem1 = 0;
1956 const SMDS_MeshElement* newElem2 = 0;
1958 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1959 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1962 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1963 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1965 myLastCreatedElems.Append(newElem1);
1966 myLastCreatedElems.Append(newElem2);
1967 // put a new triangle on the same shape and add to the same groups
1970 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1971 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1973 AddToSameGroups( newElem1, elem, aMesh );
1974 AddToSameGroups( newElem2, elem, aMesh );
1975 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
1976 aMesh->RemoveElement( elem );
1979 // Quadratic quadrangle
1981 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
1983 // get surface elem is on
1984 int aShapeId = FindShape( elem );
1985 if ( aShapeId != helper.GetSubShapeID() ) {
1989 shape = aMesh->IndexToShape( aShapeId );
1990 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1991 TopoDS_Face face = TopoDS::Face( shape );
1992 surface = BRep_Tool::Surface( face );
1993 if ( !surface.IsNull() )
1994 helper.SetSubShape( shape );
1998 const SMDS_MeshNode* aNodes [8];
1999 const SMDS_MeshNode* inFaceNode = 0;
2000 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2002 while ( itN->more() ) {
2003 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2004 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2005 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2007 inFaceNode = aNodes[ i-1 ];
2011 // find middle point for (0,1,2,3)
2012 // and create a node in this point;
2014 if ( surface.IsNull() ) {
2016 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2020 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2023 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2025 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2027 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2028 myLastCreatedNodes.Append(newN);
2030 // create a new element
2031 const SMDS_MeshElement* newElem1 = 0;
2032 const SMDS_MeshElement* newElem2 = 0;
2034 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2035 aNodes[6], aNodes[7], newN );
2036 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2037 newN, aNodes[4], aNodes[5] );
2040 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2041 aNodes[7], aNodes[4], newN );
2042 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2043 newN, aNodes[5], aNodes[6] );
2045 myLastCreatedElems.Append(newElem1);
2046 myLastCreatedElems.Append(newElem2);
2047 // put a new triangle on the same shape and add to the same groups
2050 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2051 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2053 AddToSameGroups( newElem1, elem, aMesh );
2054 AddToSameGroups( newElem2, elem, aMesh );
2055 aMesh->RemoveElement( elem );
2062 //=======================================================================
2063 //function : getAngle
2065 //=======================================================================
2067 double getAngle(const SMDS_MeshElement * tr1,
2068 const SMDS_MeshElement * tr2,
2069 const SMDS_MeshNode * n1,
2070 const SMDS_MeshNode * n2)
2072 double angle = 2. * M_PI; // bad angle
2075 SMESH::Controls::TSequenceOfXYZ P1, P2;
2076 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2077 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2080 if(!tr1->IsQuadratic())
2081 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2083 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2084 if ( N1.SquareMagnitude() <= gp::Resolution() )
2086 if(!tr2->IsQuadratic())
2087 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2089 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2090 if ( N2.SquareMagnitude() <= gp::Resolution() )
2093 // find the first diagonal node n1 in the triangles:
2094 // take in account a diagonal link orientation
2095 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2096 for ( int t = 0; t < 2; t++ ) {
2097 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2098 int i = 0, iDiag = -1;
2099 while ( it->more()) {
2100 const SMDS_MeshElement *n = it->next();
2101 if ( n == n1 || n == n2 ) {
2105 if ( i - iDiag == 1 )
2106 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2115 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2118 angle = N1.Angle( N2 );
2123 // =================================================
2124 // class generating a unique ID for a pair of nodes
2125 // and able to return nodes by that ID
2126 // =================================================
2130 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2131 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2134 long GetLinkID (const SMDS_MeshNode * n1,
2135 const SMDS_MeshNode * n2) const
2137 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2140 bool GetNodes (const long theLinkID,
2141 const SMDS_MeshNode* & theNode1,
2142 const SMDS_MeshNode* & theNode2) const
2144 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2145 if ( !theNode1 ) return false;
2146 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2147 if ( !theNode2 ) return false;
2153 const SMESHDS_Mesh* myMesh;
2158 //=======================================================================
2159 //function : TriToQuad
2160 //purpose : Fuse neighbour triangles into quadrangles.
2161 // theCrit is used to select a neighbour to fuse with.
2162 // theMaxAngle is a max angle between element normals at which
2163 // fusion is still performed.
2164 //=======================================================================
2166 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2167 SMESH::Controls::NumericalFunctorPtr theCrit,
2168 const double theMaxAngle)
2170 myLastCreatedElems.Clear();
2171 myLastCreatedNodes.Clear();
2173 MESSAGE( "::TriToQuad()" );
2175 if ( !theCrit.get() )
2178 SMESHDS_Mesh * aMesh = GetMeshDS();
2180 // Prepare data for algo: build
2181 // 1. map of elements with their linkIDs
2182 // 2. map of linkIDs with their elements
2184 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2185 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2186 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2187 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2189 TIDSortedElemSet::iterator itElem;
2190 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2191 const SMDS_MeshElement* elem = *itElem;
2192 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2193 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2194 if(!IsTria) continue;
2196 // retrieve element nodes
2197 const SMDS_MeshNode* aNodes [4];
2198 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2201 aNodes[ i++ ] = cast2Node( itN->next() );
2202 aNodes[ 3 ] = aNodes[ 0 ];
2205 for ( i = 0; i < 3; i++ ) {
2206 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2207 // check if elements sharing a link can be fused
2208 itLE = mapLi_listEl.find( link );
2209 if ( itLE != mapLi_listEl.end() ) {
2210 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2212 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2213 //if ( FindShape( elem ) != FindShape( elem2 ))
2214 // continue; // do not fuse triangles laying on different shapes
2215 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2216 continue; // avoid making badly shaped quads
2217 (*itLE).second.push_back( elem );
2220 mapLi_listEl[ link ].push_back( elem );
2222 mapEl_setLi [ elem ].insert( link );
2225 // Clean the maps from the links shared by a sole element, ie
2226 // links to which only one element is bound in mapLi_listEl
2228 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2229 int nbElems = (*itLE).second.size();
2230 if ( nbElems < 2 ) {
2231 const SMDS_MeshElement* elem = (*itLE).second.front();
2232 SMESH_TLink link = (*itLE).first;
2233 mapEl_setLi[ elem ].erase( link );
2234 if ( mapEl_setLi[ elem ].empty() )
2235 mapEl_setLi.erase( elem );
2239 // Algo: fuse triangles into quadrangles
2241 while ( ! mapEl_setLi.empty() ) {
2242 // Look for the start element:
2243 // the element having the least nb of shared links
2244 const SMDS_MeshElement* startElem = 0;
2246 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2247 int nbLinks = (*itEL).second.size();
2248 if ( nbLinks < minNbLinks ) {
2249 startElem = (*itEL).first;
2250 minNbLinks = nbLinks;
2251 if ( minNbLinks == 1 )
2256 // search elements to fuse starting from startElem or links of elements
2257 // fused earlyer - startLinks
2258 list< SMESH_TLink > startLinks;
2259 while ( startElem || !startLinks.empty() ) {
2260 while ( !startElem && !startLinks.empty() ) {
2261 // Get an element to start, by a link
2262 SMESH_TLink linkId = startLinks.front();
2263 startLinks.pop_front();
2264 itLE = mapLi_listEl.find( linkId );
2265 if ( itLE != mapLi_listEl.end() ) {
2266 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2267 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2268 for ( ; itE != listElem.end() ; itE++ )
2269 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2271 mapLi_listEl.erase( itLE );
2276 // Get candidates to be fused
2277 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2278 const SMESH_TLink *link12, *link13;
2280 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2281 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2282 ASSERT( !setLi.empty() );
2283 set< SMESH_TLink >::iterator itLi;
2284 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2286 const SMESH_TLink & link = (*itLi);
2287 itLE = mapLi_listEl.find( link );
2288 if ( itLE == mapLi_listEl.end() )
2291 const SMDS_MeshElement* elem = (*itLE).second.front();
2293 elem = (*itLE).second.back();
2294 mapLi_listEl.erase( itLE );
2295 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2306 // add other links of elem to list of links to re-start from
2307 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2308 set< SMESH_TLink >::iterator it;
2309 for ( it = links.begin(); it != links.end(); it++ ) {
2310 const SMESH_TLink& link2 = (*it);
2311 if ( link2 != link )
2312 startLinks.push_back( link2 );
2316 // Get nodes of possible quadrangles
2317 const SMDS_MeshNode *n12 [4], *n13 [4];
2318 bool Ok12 = false, Ok13 = false;
2319 const SMDS_MeshNode *linkNode1, *linkNode2;
2321 linkNode1 = link12->first;
2322 linkNode2 = link12->second;
2323 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2327 linkNode1 = link13->first;
2328 linkNode2 = link13->second;
2329 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2333 // Choose a pair to fuse
2334 if ( Ok12 && Ok13 ) {
2335 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2336 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2337 double aBadRate12 = getBadRate( &quad12, theCrit );
2338 double aBadRate13 = getBadRate( &quad13, theCrit );
2339 if ( aBadRate13 < aBadRate12 )
2346 // and remove fused elems and removed links from the maps
2347 mapEl_setLi.erase( tr1 );
2349 mapEl_setLi.erase( tr2 );
2350 mapLi_listEl.erase( *link12 );
2351 if(tr1->NbNodes()==3) {
2352 const SMDS_MeshElement* newElem = 0;
2353 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2354 myLastCreatedElems.Append(newElem);
2355 AddToSameGroups( newElem, tr1, aMesh );
2356 int aShapeId = tr1->getshapeId();
2359 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2361 aMesh->RemoveElement( tr1 );
2362 aMesh->RemoveElement( tr2 );
2365 const SMDS_MeshNode* N1 [6];
2366 const SMDS_MeshNode* N2 [6];
2367 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2368 // now we receive following N1 and N2 (using numeration as above image)
2369 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2370 // i.e. first nodes from both arrays determ new diagonal
2371 const SMDS_MeshNode* aNodes[8];
2380 const SMDS_MeshElement* newElem = 0;
2381 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2382 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2383 myLastCreatedElems.Append(newElem);
2384 AddToSameGroups( newElem, tr1, aMesh );
2385 int aShapeId = tr1->getshapeId();
2388 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2390 aMesh->RemoveElement( tr1 );
2391 aMesh->RemoveElement( tr2 );
2392 // remove middle node (9)
2393 GetMeshDS()->RemoveNode( N1[4] );
2397 mapEl_setLi.erase( tr3 );
2398 mapLi_listEl.erase( *link13 );
2399 if(tr1->NbNodes()==3) {
2400 const SMDS_MeshElement* newElem = 0;
2401 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2402 myLastCreatedElems.Append(newElem);
2403 AddToSameGroups( newElem, tr1, aMesh );
2404 int aShapeId = tr1->getshapeId();
2407 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2409 aMesh->RemoveElement( tr1 );
2410 aMesh->RemoveElement( tr3 );
2413 const SMDS_MeshNode* N1 [6];
2414 const SMDS_MeshNode* N2 [6];
2415 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2416 // now we receive following N1 and N2 (using numeration as above image)
2417 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2418 // i.e. first nodes from both arrays determ new diagonal
2419 const SMDS_MeshNode* aNodes[8];
2428 const SMDS_MeshElement* newElem = 0;
2429 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2430 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2431 myLastCreatedElems.Append(newElem);
2432 AddToSameGroups( newElem, tr1, aMesh );
2433 int aShapeId = tr1->getshapeId();
2436 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2438 aMesh->RemoveElement( tr1 );
2439 aMesh->RemoveElement( tr3 );
2440 // remove middle node (9)
2441 GetMeshDS()->RemoveNode( N1[4] );
2445 // Next element to fuse: the rejected one
2447 startElem = Ok12 ? tr3 : tr2;
2449 } // if ( startElem )
2450 } // while ( startElem || !startLinks.empty() )
2451 } // while ( ! mapEl_setLi.empty() )
2457 /*#define DUMPSO(txt) \
2458 // cout << txt << endl;
2459 //=============================================================================
2463 //=============================================================================
2464 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2468 int tmp = idNodes[ i1 ];
2469 idNodes[ i1 ] = idNodes[ i2 ];
2470 idNodes[ i2 ] = tmp;
2471 gp_Pnt Ptmp = P[ i1 ];
2474 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2477 //=======================================================================
2478 //function : SortQuadNodes
2479 //purpose : Set 4 nodes of a quadrangle face in a good order.
2480 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2482 //=======================================================================
2484 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2489 for ( i = 0; i < 4; i++ ) {
2490 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2492 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2495 gp_Vec V1(P[0], P[1]);
2496 gp_Vec V2(P[0], P[2]);
2497 gp_Vec V3(P[0], P[3]);
2499 gp_Vec Cross1 = V1 ^ V2;
2500 gp_Vec Cross2 = V2 ^ V3;
2503 if (Cross1.Dot(Cross2) < 0)
2508 if (Cross1.Dot(Cross2) < 0)
2512 swap ( i, i + 1, idNodes, P );
2514 // for ( int ii = 0; ii < 4; ii++ ) {
2515 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2516 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2522 //=======================================================================
2523 //function : SortHexaNodes
2524 //purpose : Set 8 nodes of a hexahedron in a good order.
2525 // Return success status
2526 //=======================================================================
2528 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2533 DUMPSO( "INPUT: ========================================");
2534 for ( i = 0; i < 8; i++ ) {
2535 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2536 if ( !n ) return false;
2537 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2538 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2540 DUMPSO( "========================================");
2543 set<int> faceNodes; // ids of bottom face nodes, to be found
2544 set<int> checkedId1; // ids of tried 2-nd nodes
2545 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2546 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2547 int iMin, iLoop1 = 0;
2549 // Loop to try the 2-nd nodes
2551 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2553 // Find not checked 2-nd node
2554 for ( i = 1; i < 8; i++ )
2555 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2556 int id1 = idNodes[i];
2557 swap ( 1, i, idNodes, P );
2558 checkedId1.insert ( id1 );
2562 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2563 // ie that all but meybe one (id3 which is on the same face) nodes
2564 // lay on the same side from the triangle plane.
2566 bool manyInPlane = false; // more than 4 nodes lay in plane
2568 while ( ++iLoop2 < 6 ) {
2570 // get 1-2-3 plane coeffs
2571 Standard_Real A, B, C, D;
2572 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2573 if ( N.SquareMagnitude() > gp::Resolution() )
2575 gp_Pln pln ( P[0], N );
2576 pln.Coefficients( A, B, C, D );
2578 // find the node (iMin) closest to pln
2579 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2581 for ( i = 3; i < 8; i++ ) {
2582 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2583 if ( fabs( dist[i] ) < minDist ) {
2584 minDist = fabs( dist[i] );
2587 if ( fabs( dist[i] ) <= tol )
2588 idInPln.insert( idNodes[i] );
2591 // there should not be more than 4 nodes in bottom plane
2592 if ( idInPln.size() > 1 )
2594 DUMPSO( "### idInPln.size() = " << idInPln.size());
2595 // idInPlane does not contain the first 3 nodes
2596 if ( manyInPlane || idInPln.size() == 5)
2597 return false; // all nodes in one plane
2600 // set the 1-st node to be not in plane
2601 for ( i = 3; i < 8; i++ ) {
2602 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2603 DUMPSO( "### Reset 0-th node");
2604 swap( 0, i, idNodes, P );
2609 // reset to re-check second nodes
2610 leastDist = DBL_MAX;
2614 break; // from iLoop2;
2617 // check that the other 4 nodes are on the same side
2618 bool sameSide = true;
2619 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2620 for ( i = 3; sameSide && i < 8; i++ ) {
2622 sameSide = ( isNeg == dist[i] <= 0.);
2625 // keep best solution
2626 if ( sameSide && minDist < leastDist ) {
2627 leastDist = minDist;
2629 faceNodes.insert( idNodes[ 1 ] );
2630 faceNodes.insert( idNodes[ 2 ] );
2631 faceNodes.insert( idNodes[ iMin ] );
2632 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2633 << " leastDist = " << leastDist);
2634 if ( leastDist <= DBL_MIN )
2639 // set next 3-d node to check
2640 int iNext = 2 + iLoop2;
2642 DUMPSO( "Try 2-nd");
2643 swap ( 2, iNext, idNodes, P );
2645 } // while ( iLoop2 < 6 )
2648 if ( faceNodes.empty() ) return false;
2650 // Put the faceNodes in proper places
2651 for ( i = 4; i < 8; i++ ) {
2652 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2653 // find a place to put
2655 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2657 DUMPSO( "Set faceNodes");
2658 swap ( iTo, i, idNodes, P );
2663 // Set nodes of the found bottom face in good order
2664 DUMPSO( " Found bottom face: ");
2665 i = SortQuadNodes( theMesh, idNodes );
2667 gp_Pnt Ptmp = P[ i ];
2672 // for ( int ii = 0; ii < 4; ii++ ) {
2673 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2674 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2677 // Gravity center of the top and bottom faces
2678 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2679 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2681 // Get direction from the bottom to the top face
2682 gp_Vec upDir ( aGCb, aGCt );
2683 Standard_Real upDirSize = upDir.Magnitude();
2684 if ( upDirSize <= gp::Resolution() ) return false;
2687 // Assure that the bottom face normal points up
2688 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2689 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2690 if ( Nb.Dot( upDir ) < 0 ) {
2691 DUMPSO( "Reverse bottom face");
2692 swap( 1, 3, idNodes, P );
2695 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2696 Standard_Real minDist = DBL_MAX;
2697 for ( i = 4; i < 8; i++ ) {
2698 // projection of P[i] to the plane defined by P[0] and upDir
2699 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2700 Standard_Real sqDist = P[0].SquareDistance( Pp );
2701 if ( sqDist < minDist ) {
2706 DUMPSO( "Set 4-th");
2707 swap ( 4, iMin, idNodes, P );
2709 // Set nodes of the top face in good order
2710 DUMPSO( "Sort top face");
2711 i = SortQuadNodes( theMesh, &idNodes[4] );
2714 gp_Pnt Ptmp = P[ i ];
2719 // Assure that direction of the top face normal is from the bottom face
2720 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2721 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2722 if ( Nt.Dot( upDir ) < 0 ) {
2723 DUMPSO( "Reverse top face");
2724 swap( 5, 7, idNodes, P );
2727 // DUMPSO( "OUTPUT: ========================================");
2728 // for ( i = 0; i < 8; i++ ) {
2729 // float *p = ugrid->GetPoint(idNodes[i]);
2730 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2736 //================================================================================
2738 * \brief Return nodes linked to the given one
2739 * \param theNode - the node
2740 * \param linkedNodes - the found nodes
2741 * \param type - the type of elements to check
2743 * Medium nodes are ignored
2745 //================================================================================
2747 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2748 TIDSortedElemSet & linkedNodes,
2749 SMDSAbs_ElementType type )
2751 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2752 while ( elemIt->more() )
2754 const SMDS_MeshElement* elem = elemIt->next();
2755 if(elem->GetType() == SMDSAbs_0DElement)
2758 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2759 if ( elem->GetType() == SMDSAbs_Volume )
2761 SMDS_VolumeTool vol( elem );
2762 while ( nodeIt->more() ) {
2763 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2764 if ( theNode != n && vol.IsLinked( theNode, n ))
2765 linkedNodes.insert( n );
2770 for ( int i = 0; nodeIt->more(); ++i ) {
2771 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2772 if ( n == theNode ) {
2773 int iBefore = i - 1;
2775 if ( elem->IsQuadratic() ) {
2776 int nb = elem->NbNodes() / 2;
2777 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2778 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2780 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2781 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2788 //=======================================================================
2789 //function : laplacianSmooth
2790 //purpose : pulls theNode toward the center of surrounding nodes directly
2791 // connected to that node along an element edge
2792 //=======================================================================
2794 void laplacianSmooth(const SMDS_MeshNode* theNode,
2795 const Handle(Geom_Surface)& theSurface,
2796 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2798 // find surrounding nodes
2800 TIDSortedElemSet nodeSet;
2801 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2803 // compute new coodrs
2805 double coord[] = { 0., 0., 0. };
2806 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2807 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2808 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2809 if ( theSurface.IsNull() ) { // smooth in 3D
2810 coord[0] += node->X();
2811 coord[1] += node->Y();
2812 coord[2] += node->Z();
2814 else { // smooth in 2D
2815 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2816 gp_XY* uv = theUVMap[ node ];
2817 coord[0] += uv->X();
2818 coord[1] += uv->Y();
2821 int nbNodes = nodeSet.size();
2824 coord[0] /= nbNodes;
2825 coord[1] /= nbNodes;
2827 if ( !theSurface.IsNull() ) {
2828 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2829 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2830 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2836 coord[2] /= nbNodes;
2840 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2843 //=======================================================================
2844 //function : centroidalSmooth
2845 //purpose : pulls theNode toward the element-area-weighted centroid of the
2846 // surrounding elements
2847 //=======================================================================
2849 void centroidalSmooth(const SMDS_MeshNode* theNode,
2850 const Handle(Geom_Surface)& theSurface,
2851 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2853 gp_XYZ aNewXYZ(0.,0.,0.);
2854 SMESH::Controls::Area anAreaFunc;
2855 double totalArea = 0.;
2860 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2861 while ( elemIt->more() )
2863 const SMDS_MeshElement* elem = elemIt->next();
2866 gp_XYZ elemCenter(0.,0.,0.);
2867 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2868 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2869 int nn = elem->NbNodes();
2870 if(elem->IsQuadratic()) nn = nn/2;
2872 //while ( itN->more() ) {
2874 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2876 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2877 aNodePoints.push_back( aP );
2878 if ( !theSurface.IsNull() ) { // smooth in 2D
2879 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2880 gp_XY* uv = theUVMap[ aNode ];
2881 aP.SetCoord( uv->X(), uv->Y(), 0. );
2885 double elemArea = anAreaFunc.GetValue( aNodePoints );
2886 totalArea += elemArea;
2888 aNewXYZ += elemCenter * elemArea;
2890 aNewXYZ /= totalArea;
2891 if ( !theSurface.IsNull() ) {
2892 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2893 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2898 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
2901 //=======================================================================
2902 //function : getClosestUV
2903 //purpose : return UV of closest projection
2904 //=======================================================================
2906 static bool getClosestUV (Extrema_GenExtPS& projector,
2907 const gp_Pnt& point,
2910 projector.Perform( point );
2911 if ( projector.IsDone() ) {
2912 double u, v, minVal = DBL_MAX;
2913 for ( int i = projector.NbExt(); i > 0; i-- )
2914 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
2915 if ( projector.SquareDistance( i ) < minVal ) {
2916 minVal = projector.SquareDistance( i );
2918 if ( projector.Value( i ) < minVal ) {
2919 minVal = projector.Value( i );
2921 projector.Point( i ).Parameter( u, v );
2923 result.SetCoord( u, v );
2929 //=======================================================================
2931 //purpose : Smooth theElements during theNbIterations or until a worst
2932 // element has aspect ratio <= theTgtAspectRatio.
2933 // Aspect Ratio varies in range [1.0, inf].
2934 // If theElements is empty, the whole mesh is smoothed.
2935 // theFixedNodes contains additionally fixed nodes. Nodes built
2936 // on edges and boundary nodes are always fixed.
2937 //=======================================================================
2939 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
2940 set<const SMDS_MeshNode*> & theFixedNodes,
2941 const SmoothMethod theSmoothMethod,
2942 const int theNbIterations,
2943 double theTgtAspectRatio,
2946 myLastCreatedElems.Clear();
2947 myLastCreatedNodes.Clear();
2949 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
2951 if ( theTgtAspectRatio < 1.0 )
2952 theTgtAspectRatio = 1.0;
2954 const double disttol = 1.e-16;
2956 SMESH::Controls::AspectRatio aQualityFunc;
2958 SMESHDS_Mesh* aMesh = GetMeshDS();
2960 if ( theElems.empty() ) {
2961 // add all faces to theElems
2962 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
2963 while ( fIt->more() ) {
2964 const SMDS_MeshElement* face = fIt->next();
2965 theElems.insert( face );
2968 // get all face ids theElems are on
2969 set< int > faceIdSet;
2970 TIDSortedElemSet::iterator itElem;
2972 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2973 int fId = FindShape( *itElem );
2974 // check that corresponding submesh exists and a shape is face
2976 faceIdSet.find( fId ) == faceIdSet.end() &&
2977 aMesh->MeshElements( fId )) {
2978 TopoDS_Shape F = aMesh->IndexToShape( fId );
2979 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
2980 faceIdSet.insert( fId );
2983 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
2985 // ===============================================
2986 // smooth elements on each TopoDS_Face separately
2987 // ===============================================
2989 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
2990 for ( ; fId != faceIdSet.rend(); ++fId ) {
2991 // get face surface and submesh
2992 Handle(Geom_Surface) surface;
2993 SMESHDS_SubMesh* faceSubMesh = 0;
2995 double fToler2 = 0, f,l;
2996 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
2997 bool isUPeriodic = false, isVPeriodic = false;
2999 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3000 surface = BRep_Tool::Surface( face );
3001 faceSubMesh = aMesh->MeshElements( *fId );
3002 fToler2 = BRep_Tool::Tolerance( face );
3003 fToler2 *= fToler2 * 10.;
3004 isUPeriodic = surface->IsUPeriodic();
3007 isVPeriodic = surface->IsVPeriodic();
3010 surface->Bounds( u1, u2, v1, v2 );
3012 // ---------------------------------------------------------
3013 // for elements on a face, find movable and fixed nodes and
3014 // compute UV for them
3015 // ---------------------------------------------------------
3016 bool checkBoundaryNodes = false;
3017 bool isQuadratic = false;
3018 set<const SMDS_MeshNode*> setMovableNodes;
3019 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3020 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3021 list< const SMDS_MeshElement* > elemsOnFace;
3023 Extrema_GenExtPS projector;
3024 GeomAdaptor_Surface surfAdaptor;
3025 if ( !surface.IsNull() ) {
3026 surfAdaptor.Load( surface );
3027 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3029 int nbElemOnFace = 0;
3030 itElem = theElems.begin();
3031 // loop on not yet smoothed elements: look for elems on a face
3032 while ( itElem != theElems.end() ) {
3033 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3034 break; // all elements found
3036 const SMDS_MeshElement* elem = *itElem;
3037 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3038 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3042 elemsOnFace.push_back( elem );
3043 theElems.erase( itElem++ );
3047 isQuadratic = elem->IsQuadratic();
3049 // get movable nodes of elem
3050 const SMDS_MeshNode* node;
3051 SMDS_TypeOfPosition posType;
3052 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3053 int nn = 0, nbn = elem->NbNodes();
3054 if(elem->IsQuadratic())
3056 while ( nn++ < nbn ) {
3057 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3058 const SMDS_PositionPtr& pos = node->GetPosition();
3059 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3060 if (posType != SMDS_TOP_EDGE &&
3061 posType != SMDS_TOP_VERTEX &&
3062 theFixedNodes.find( node ) == theFixedNodes.end())
3064 // check if all faces around the node are on faceSubMesh
3065 // because a node on edge may be bound to face
3066 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3068 if ( faceSubMesh ) {
3069 while ( eIt->more() && all ) {
3070 const SMDS_MeshElement* e = eIt->next();
3071 all = faceSubMesh->Contains( e );
3075 setMovableNodes.insert( node );
3077 checkBoundaryNodes = true;
3079 if ( posType == SMDS_TOP_3DSPACE )
3080 checkBoundaryNodes = true;
3083 if ( surface.IsNull() )
3086 // get nodes to check UV
3087 list< const SMDS_MeshNode* > uvCheckNodes;
3088 itN = elem->nodesIterator();
3089 nn = 0; nbn = elem->NbNodes();
3090 if(elem->IsQuadratic())
3092 while ( nn++ < nbn ) {
3093 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3094 if ( uvMap.find( node ) == uvMap.end() )
3095 uvCheckNodes.push_back( node );
3096 // add nodes of elems sharing node
3097 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3098 // while ( eIt->more() ) {
3099 // const SMDS_MeshElement* e = eIt->next();
3100 // if ( e != elem ) {
3101 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3102 // while ( nIt->more() ) {
3103 // const SMDS_MeshNode* n =
3104 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3105 // if ( uvMap.find( n ) == uvMap.end() )
3106 // uvCheckNodes.push_back( n );
3112 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3113 for ( ; n != uvCheckNodes.end(); ++n ) {
3116 const SMDS_PositionPtr& pos = node->GetPosition();
3117 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3119 switch ( posType ) {
3120 case SMDS_TOP_FACE: {
3121 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3122 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3125 case SMDS_TOP_EDGE: {
3126 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3127 Handle(Geom2d_Curve) pcurve;
3128 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3129 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3130 if ( !pcurve.IsNull() ) {
3131 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3132 uv = pcurve->Value( u ).XY();
3136 case SMDS_TOP_VERTEX: {
3137 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3138 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3139 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3144 // check existing UV
3145 bool project = true;
3146 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3147 double dist1 = DBL_MAX, dist2 = 0;
3148 if ( posType != SMDS_TOP_3DSPACE ) {
3149 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3150 project = dist1 > fToler2;
3152 if ( project ) { // compute new UV
3154 if ( !getClosestUV( projector, pNode, newUV )) {
3155 MESSAGE("Node Projection Failed " << node);
3159 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3161 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3163 if ( posType != SMDS_TOP_3DSPACE )
3164 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3165 if ( dist2 < dist1 )
3169 // store UV in the map
3170 listUV.push_back( uv );
3171 uvMap.insert( make_pair( node, &listUV.back() ));
3173 } // loop on not yet smoothed elements
3175 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3176 checkBoundaryNodes = true;
3178 // fix nodes on mesh boundary
3180 if ( checkBoundaryNodes ) {
3181 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3182 map< SMESH_TLink, int >::iterator link_nb;
3183 // put all elements links to linkNbMap
3184 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3185 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3186 const SMDS_MeshElement* elem = (*elemIt);
3187 int nbn = elem->NbCornerNodes();
3188 // loop on elem links: insert them in linkNbMap
3189 for ( int iN = 0; iN < nbn; ++iN ) {
3190 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3191 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3192 SMESH_TLink link( n1, n2 );
3193 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3197 // remove nodes that are in links encountered only once from setMovableNodes
3198 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3199 if ( link_nb->second == 1 ) {
3200 setMovableNodes.erase( link_nb->first.node1() );
3201 setMovableNodes.erase( link_nb->first.node2() );
3206 // -----------------------------------------------------
3207 // for nodes on seam edge, compute one more UV ( uvMap2 );
3208 // find movable nodes linked to nodes on seam and which
3209 // are to be smoothed using the second UV ( uvMap2 )
3210 // -----------------------------------------------------
3212 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3213 if ( !surface.IsNull() ) {
3214 TopExp_Explorer eExp( face, TopAbs_EDGE );
3215 for ( ; eExp.More(); eExp.Next() ) {
3216 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3217 if ( !BRep_Tool::IsClosed( edge, face ))
3219 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3220 if ( !sm ) continue;
3221 // find out which parameter varies for a node on seam
3224 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3225 if ( pcurve.IsNull() ) continue;
3226 uv1 = pcurve->Value( f );
3228 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3229 if ( pcurve.IsNull() ) continue;
3230 uv2 = pcurve->Value( f );
3231 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3233 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3234 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3236 // get nodes on seam and its vertices
3237 list< const SMDS_MeshNode* > seamNodes;
3238 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3239 while ( nSeamIt->more() ) {
3240 const SMDS_MeshNode* node = nSeamIt->next();
3241 if ( !isQuadratic || !IsMedium( node ))
3242 seamNodes.push_back( node );
3244 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3245 for ( ; vExp.More(); vExp.Next() ) {
3246 sm = aMesh->MeshElements( vExp.Current() );
3248 nSeamIt = sm->GetNodes();
3249 while ( nSeamIt->more() )
3250 seamNodes.push_back( nSeamIt->next() );
3253 // loop on nodes on seam
3254 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3255 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3256 const SMDS_MeshNode* nSeam = *noSeIt;
3257 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3258 if ( n_uv == uvMap.end() )
3261 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3262 // set the second UV
3263 listUV.push_back( *n_uv->second );
3264 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3265 if ( uvMap2.empty() )
3266 uvMap2 = uvMap; // copy the uvMap contents
3267 uvMap2[ nSeam ] = &listUV.back();
3269 // collect movable nodes linked to ones on seam in nodesNearSeam
3270 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3271 while ( eIt->more() ) {
3272 const SMDS_MeshElement* e = eIt->next();
3273 int nbUseMap1 = 0, nbUseMap2 = 0;
3274 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3275 int nn = 0, nbn = e->NbNodes();
3276 if(e->IsQuadratic()) nbn = nbn/2;
3277 while ( nn++ < nbn )
3279 const SMDS_MeshNode* n =
3280 static_cast<const SMDS_MeshNode*>( nIt->next() );
3282 setMovableNodes.find( n ) == setMovableNodes.end() )
3284 // add only nodes being closer to uv2 than to uv1
3285 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3286 0.5 * ( n->Y() + nSeam->Y() ),
3287 0.5 * ( n->Z() + nSeam->Z() ));
3289 getClosestUV( projector, pMid, uv );
3290 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3291 nodesNearSeam.insert( n );
3297 // for centroidalSmooth all element nodes must
3298 // be on one side of a seam
3299 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3300 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3302 while ( nn++ < nbn ) {
3303 const SMDS_MeshNode* n =
3304 static_cast<const SMDS_MeshNode*>( nIt->next() );
3305 setMovableNodes.erase( n );
3309 } // loop on nodes on seam
3310 } // loop on edge of a face
3311 } // if ( !face.IsNull() )
3313 if ( setMovableNodes.empty() ) {
3314 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3315 continue; // goto next face
3323 double maxRatio = -1., maxDisplacement = -1.;
3324 set<const SMDS_MeshNode*>::iterator nodeToMove;
3325 for ( it = 0; it < theNbIterations; it++ ) {
3326 maxDisplacement = 0.;
3327 nodeToMove = setMovableNodes.begin();
3328 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3329 const SMDS_MeshNode* node = (*nodeToMove);
3330 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3333 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3334 if ( theSmoothMethod == LAPLACIAN )
3335 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3337 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3339 // node displacement
3340 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3341 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3342 if ( aDispl > maxDisplacement )
3343 maxDisplacement = aDispl;
3345 // no node movement => exit
3346 //if ( maxDisplacement < 1.e-16 ) {
3347 if ( maxDisplacement < disttol ) {
3348 MESSAGE("-- no node movement --");
3352 // check elements quality
3354 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3355 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3356 const SMDS_MeshElement* elem = (*elemIt);
3357 if ( !elem || elem->GetType() != SMDSAbs_Face )
3359 SMESH::Controls::TSequenceOfXYZ aPoints;
3360 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3361 double aValue = aQualityFunc.GetValue( aPoints );
3362 if ( aValue > maxRatio )
3366 if ( maxRatio <= theTgtAspectRatio ) {
3367 MESSAGE("-- quality achived --");
3370 if (it+1 == theNbIterations) {
3371 MESSAGE("-- Iteration limit exceeded --");
3373 } // smoothing iterations
3375 MESSAGE(" Face id: " << *fId <<
3376 " Nb iterstions: " << it <<
3377 " Displacement: " << maxDisplacement <<
3378 " Aspect Ratio " << maxRatio);
3380 // ---------------------------------------
3381 // new nodes positions are computed,
3382 // record movement in DS and set new UV
3383 // ---------------------------------------
3384 nodeToMove = setMovableNodes.begin();
3385 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3386 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3387 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3388 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3389 if ( node_uv != uvMap.end() ) {
3390 gp_XY* uv = node_uv->second;
3392 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3396 // move medium nodes of quadratic elements
3399 SMESH_MesherHelper helper( *GetMesh() );
3400 if ( !face.IsNull() )
3401 helper.SetSubShape( face );
3402 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3403 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3404 const SMDS_VtkFace* QF =
3405 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3406 if(QF && QF->IsQuadratic()) {
3407 vector<const SMDS_MeshNode*> Ns;
3408 Ns.reserve(QF->NbNodes()+1);
3409 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3410 while ( anIter->more() )
3411 Ns.push_back( cast2Node(anIter->next()) );
3412 Ns.push_back( Ns[0] );
3414 for(int i=0; i<QF->NbNodes(); i=i+2) {
3415 if ( !surface.IsNull() ) {
3416 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3417 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3418 gp_XY uv = ( uv1 + uv2 ) / 2.;
3419 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3420 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3423 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3424 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3425 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3427 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3428 fabs( Ns[i+1]->Y() - y ) > disttol ||
3429 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3430 // we have to move i+1 node
3431 aMesh->MoveNode( Ns[i+1], x, y, z );
3438 } // loop on face ids
3442 //=======================================================================
3443 //function : isReverse
3444 //purpose : Return true if normal of prevNodes is not co-directied with
3445 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3446 // iNotSame is where prevNodes and nextNodes are different.
3447 // If result is true then future volume orientation is OK
3448 //=======================================================================
3450 static bool isReverse(const SMDS_MeshElement* face,
3451 const vector<const SMDS_MeshNode*>& prevNodes,
3452 const vector<const SMDS_MeshNode*>& nextNodes,
3456 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3457 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3458 gp_XYZ extrDir( pN - pP ), faceNorm;
3459 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3461 return faceNorm * extrDir < 0.0;
3464 //=======================================================================
3466 * \brief Create elements by sweeping an element
3467 * \param elem - element to sweep
3468 * \param newNodesItVec - nodes generated from each node of the element
3469 * \param newElems - generated elements
3470 * \param nbSteps - number of sweeping steps
3471 * \param srcElements - to append elem for each generated element
3473 //=======================================================================
3475 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3476 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3477 list<const SMDS_MeshElement*>& newElems,
3479 SMESH_SequenceOfElemPtr& srcElements)
3481 //MESSAGE("sweepElement " << nbSteps);
3482 SMESHDS_Mesh* aMesh = GetMeshDS();
3484 const int nbNodes = elem->NbNodes();
3485 const int nbCorners = elem->NbCornerNodes();
3486 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3487 polyhedron creation !!! */
3488 // Loop on elem nodes:
3489 // find new nodes and detect same nodes indices
3490 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3491 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3492 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3493 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3495 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3496 vector<int> sames(nbNodes);
3497 vector<bool> isSingleNode(nbNodes);
3499 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3500 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3501 const SMDS_MeshNode* node = nnIt->first;
3502 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3503 if ( listNewNodes.empty() )
3506 itNN [ iNode ] = listNewNodes.begin();
3507 prevNod[ iNode ] = node;
3508 nextNod[ iNode ] = listNewNodes.front();
3510 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3511 corner node of linear */
3512 if ( prevNod[ iNode ] != nextNod [ iNode ])
3513 nbDouble += !isSingleNode[iNode];
3515 if( iNode < nbCorners ) { // check corners only
3516 if ( prevNod[ iNode ] == nextNod [ iNode ])
3517 sames[nbSame++] = iNode;
3519 iNotSameNode = iNode;
3523 if ( nbSame == nbNodes || nbSame > 2) {
3524 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3528 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3530 // fix nodes order to have bottom normal external
3531 if ( baseType == SMDSEntity_Polygon )
3533 std::reverse( itNN.begin(), itNN.end() );
3534 std::reverse( prevNod.begin(), prevNod.end() );
3535 std::reverse( midlNod.begin(), midlNod.end() );
3536 std::reverse( nextNod.begin(), nextNod.end() );
3537 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3541 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3542 SMDS_MeshCell::applyInterlace( ind, itNN );
3543 SMDS_MeshCell::applyInterlace( ind, prevNod );
3544 SMDS_MeshCell::applyInterlace( ind, nextNod );
3545 SMDS_MeshCell::applyInterlace( ind, midlNod );
3546 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3549 sames[nbSame] = iNotSameNode;
3550 for ( int j = 0; j <= nbSame; ++j )
3551 for ( size_t i = 0; i < ind.size(); ++i )
3552 if ( ind[i] == sames[j] )
3557 iNotSameNode = sames[nbSame];
3562 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3564 iSameNode = sames[ nbSame-1 ];
3565 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3566 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3567 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3570 // make new elements
3571 for (int iStep = 0; iStep < nbSteps; iStep++ )
3574 for ( iNode = 0; iNode < nbNodes; iNode++ )
3576 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3577 nextNod[ iNode ] = *itNN[ iNode ]++;
3580 SMDS_MeshElement* aNewElem = 0;
3581 /*if(!elem->IsPoly())*/ {
3582 switch ( baseType ) {
3584 case SMDSEntity_Node: { // sweep NODE
3585 if ( nbSame == 0 ) {
3586 if ( isSingleNode[0] )
3587 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3589 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3595 case SMDSEntity_Edge: { // sweep EDGE
3596 if ( nbDouble == 0 )
3598 if ( nbSame == 0 ) // ---> quadrangle
3599 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3600 nextNod[ 1 ], nextNod[ 0 ] );
3601 else // ---> triangle
3602 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3603 nextNod[ iNotSameNode ] );
3605 else // ---> polygon
3607 vector<const SMDS_MeshNode*> poly_nodes;
3608 poly_nodes.push_back( prevNod[0] );
3609 poly_nodes.push_back( prevNod[1] );
3610 if ( prevNod[1] != nextNod[1] )
3612 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3613 poly_nodes.push_back( nextNod[1] );
3615 if ( prevNod[0] != nextNod[0] )
3617 poly_nodes.push_back( nextNod[0] );
3618 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3620 switch ( poly_nodes.size() ) {
3622 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3625 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3626 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3629 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3634 case SMDSEntity_Triangle: // TRIANGLE --->
3636 if ( nbDouble > 0 ) break;
3637 if ( nbSame == 0 ) // ---> pentahedron
3638 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3639 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3641 else if ( nbSame == 1 ) // ---> pyramid
3642 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3643 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3644 nextNod[ iSameNode ]);
3646 else // 2 same nodes: ---> tetrahedron
3647 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3648 nextNod[ iNotSameNode ]);
3651 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3655 if ( nbDouble+nbSame == 2 )
3657 if(nbSame==0) { // ---> quadratic quadrangle
3658 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3659 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3661 else { //(nbSame==1) // ---> quadratic triangle
3663 return; // medium node on axis
3665 else if(sames[0]==0)
3666 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3667 nextNod[2], midlNod[1], prevNod[2]);
3669 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3670 midlNod[0], nextNod[2], prevNod[2]);
3673 else if ( nbDouble == 3 )
3675 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3676 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3677 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3684 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3685 if ( nbDouble > 0 ) break;
3687 if ( nbSame == 0 ) // ---> hexahedron
3688 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3689 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3691 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3692 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3693 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3694 nextNod[ iSameNode ]);
3695 newElems.push_back( aNewElem );
3696 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3697 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3698 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3700 else if ( nbSame == 2 ) { // ---> pentahedron
3701 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3702 // iBeforeSame is same too
3703 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3704 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3705 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3707 // iAfterSame is same too
3708 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3709 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3710 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3714 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3715 if ( nbDouble+nbSame != 3 ) break;
3717 // ---> pentahedron with 15 nodes
3718 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3719 nextNod[0], nextNod[1], nextNod[2],
3720 prevNod[3], prevNod[4], prevNod[5],
3721 nextNod[3], nextNod[4], nextNod[5],
3722 midlNod[0], midlNod[1], midlNod[2]);
3724 else if(nbSame==1) {
3725 // ---> 2d order pyramid of 13 nodes
3726 int apex = iSameNode;
3727 int i0 = ( apex + 1 ) % nbCorners;
3728 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3732 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3733 nextNod[i0], nextNod[i1], prevNod[apex],
3734 prevNod[i01], midlNod[i0],
3735 nextNod[i01], midlNod[i1],
3736 prevNod[i1a], prevNod[i0a],
3737 nextNod[i0a], nextNod[i1a]);
3739 else if(nbSame==2) {
3740 // ---> 2d order tetrahedron of 10 nodes
3741 int n1 = iNotSameNode;
3742 int n2 = ( n1 + 1 ) % nbCorners;
3743 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3747 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3748 prevNod[n12], prevNod[n23], prevNod[n31],
3749 midlNod[n1], nextNod[n12], nextNod[n31]);
3753 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3755 if ( nbDouble != 4 ) break;
3756 // ---> hexahedron with 20 nodes
3757 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3758 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3759 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3760 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3761 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3763 else if(nbSame==1) {
3764 // ---> pyramid + pentahedron - can not be created since it is needed
3765 // additional middle node at the center of face
3766 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3769 else if( nbSame == 2 ) {
3770 if ( nbDouble != 2 ) break;
3771 // ---> 2d order Pentahedron with 15 nodes
3773 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3774 // iBeforeSame is same too
3781 // iAfterSame is same too
3791 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3792 prevNod[n4], prevNod[n5], nextNod[n5],
3793 prevNod[n12], midlNod[n2], nextNod[n12],
3794 prevNod[n45], midlNod[n5], nextNod[n45],
3795 prevNod[n14], prevNod[n25], nextNod[n25]);
3799 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3801 if( nbSame == 0 && nbDouble == 9 ) {
3802 // ---> tri-quadratic hexahedron with 27 nodes
3803 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3804 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3805 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3806 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3807 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3808 prevNod[8], // bottom center
3809 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3810 nextNod[8], // top center
3811 midlNod[8]);// elem center
3819 case SMDSEntity_Polygon: { // sweep POLYGON
3821 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3822 // ---> hexagonal prism
3823 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3824 prevNod[3], prevNod[4], prevNod[5],
3825 nextNod[0], nextNod[1], nextNod[2],
3826 nextNod[3], nextNod[4], nextNod[5]);
3835 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3837 if ( baseType != SMDSEntity_Polygon )
3839 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3840 SMDS_MeshCell::applyInterlace( ind, prevNod );
3841 SMDS_MeshCell::applyInterlace( ind, nextNod );
3842 SMDS_MeshCell::applyInterlace( ind, midlNod );
3843 SMDS_MeshCell::applyInterlace( ind, itNN );
3844 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3845 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3847 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3848 vector<int> quantities (nbNodes + 2);
3849 polyedre_nodes.clear();
3853 for (int inode = 0; inode < nbNodes; inode++)
3854 polyedre_nodes.push_back( prevNod[inode] );
3855 quantities.push_back( nbNodes );
3858 polyedre_nodes.push_back( nextNod[0] );
3859 for (int inode = nbNodes; inode-1; --inode )
3860 polyedre_nodes.push_back( nextNod[inode-1] );
3861 quantities.push_back( nbNodes );
3864 for (int iface = 0; iface < nbNodes; iface++)
3866 const int prevNbNodes = polyedre_nodes.size();
3867 int inextface = (iface+1) % nbNodes;
3868 polyedre_nodes.push_back( prevNod[inextface] );
3869 polyedre_nodes.push_back( prevNod[iface] );
3870 if ( prevNod[iface] != nextNod[iface] )
3872 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
3873 polyedre_nodes.push_back( nextNod[iface] );
3875 if ( prevNod[inextface] != nextNod[inextface] )
3877 polyedre_nodes.push_back( nextNod[inextface] );
3878 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
3880 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
3881 if ( nbFaceNodes > 2 )
3882 quantities.push_back( nbFaceNodes );
3883 else // degenerated face
3884 polyedre_nodes.resize( prevNbNodes );
3886 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3890 newElems.push_back( aNewElem );
3891 myLastCreatedElems.Append(aNewElem);
3892 srcElements.Append( elem );
3895 // set new prev nodes
3896 for ( iNode = 0; iNode < nbNodes; iNode++ )
3897 prevNod[ iNode ] = nextNod[ iNode ];
3902 //=======================================================================
3904 * \brief Create 1D and 2D elements around swept elements
3905 * \param mapNewNodes - source nodes and ones generated from them
3906 * \param newElemsMap - source elements and ones generated from them
3907 * \param elemNewNodesMap - nodes generated from each node of each element
3908 * \param elemSet - all swept elements
3909 * \param nbSteps - number of sweeping steps
3910 * \param srcElements - to append elem for each generated element
3912 //=======================================================================
3914 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3915 TElemOfElemListMap & newElemsMap,
3916 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3917 TIDSortedElemSet& elemSet,
3919 SMESH_SequenceOfElemPtr& srcElements)
3921 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3922 SMESHDS_Mesh* aMesh = GetMeshDS();
3924 // Find nodes belonging to only one initial element - sweep them to get edges.
3926 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3927 for ( ; nList != mapNewNodes.end(); nList++ )
3929 const SMDS_MeshNode* node =
3930 static_cast<const SMDS_MeshNode*>( nList->first );
3931 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3932 int nbInitElems = 0;
3933 const SMDS_MeshElement* el = 0;
3934 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3935 while ( eIt->more() && nbInitElems < 2 ) {
3937 SMDSAbs_ElementType type = el->GetType();
3938 if ( type == SMDSAbs_Volume || type < highType ) continue;
3939 if ( type > highType ) {
3943 nbInitElems += elemSet.count(el);
3945 if ( nbInitElems < 2 ) {
3946 bool NotCreateEdge = el && el->IsMediumNode(node);
3947 if(!NotCreateEdge) {
3948 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3949 list<const SMDS_MeshElement*> newEdges;
3950 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3955 // Make a ceiling for each element ie an equal element of last new nodes.
3956 // Find free links of faces - make edges and sweep them into faces.
3958 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3959 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3960 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
3962 const SMDS_MeshElement* elem = itElem->first;
3963 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3965 if(itElem->second.size()==0) continue;
3967 const bool isQuadratic = elem->IsQuadratic();
3969 if ( elem->GetType() == SMDSAbs_Edge ) {
3970 // create a ceiling edge
3971 if ( !isQuadratic ) {
3972 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3973 vecNewNodes[ 1 ]->second.back())) {
3974 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3975 vecNewNodes[ 1 ]->second.back()));
3976 srcElements.Append( myLastCreatedElems.Last() );
3980 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3981 vecNewNodes[ 1 ]->second.back(),
3982 vecNewNodes[ 2 ]->second.back())) {
3983 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3984 vecNewNodes[ 1 ]->second.back(),
3985 vecNewNodes[ 2 ]->second.back()));
3986 srcElements.Append( myLastCreatedElems.Last() );
3990 if ( elem->GetType() != SMDSAbs_Face )
3993 bool hasFreeLinks = false;
3995 TIDSortedElemSet avoidSet;
3996 avoidSet.insert( elem );
3998 set<const SMDS_MeshNode*> aFaceLastNodes;
3999 int iNode, nbNodes = vecNewNodes.size();
4000 if ( !isQuadratic ) {
4001 // loop on the face nodes
4002 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4003 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4004 // look for free links of the face
4005 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4006 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4007 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4008 // check if a link is free
4009 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4010 hasFreeLinks = true;
4011 // make an edge and a ceiling for a new edge
4012 if ( !aMesh->FindEdge( n1, n2 )) {
4013 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4014 srcElements.Append( myLastCreatedElems.Last() );
4016 n1 = vecNewNodes[ iNode ]->second.back();
4017 n2 = vecNewNodes[ iNext ]->second.back();
4018 if ( !aMesh->FindEdge( n1, n2 )) {
4019 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4020 srcElements.Append( myLastCreatedElems.Last() );
4025 else { // elem is quadratic face
4026 int nbn = nbNodes/2;
4027 for ( iNode = 0; iNode < nbn; iNode++ ) {
4028 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4029 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4030 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4031 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4032 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4033 // check if a link is free
4034 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4035 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4036 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4037 hasFreeLinks = true;
4038 // make an edge and a ceiling for a new edge
4040 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4041 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4042 srcElements.Append( myLastCreatedElems.Last() );
4044 n1 = vecNewNodes[ iNode ]->second.back();
4045 n2 = vecNewNodes[ iNext ]->second.back();
4046 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4047 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4048 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4049 srcElements.Append( myLastCreatedElems.Last() );
4053 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4054 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4058 // sweep free links into faces
4060 if ( hasFreeLinks ) {
4061 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4062 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4064 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4065 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4066 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4067 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4069 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4070 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4071 std::advance( v, volNb );
4072 // find indices of free faces of a volume and their source edges
4073 list< int > freeInd;
4074 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4075 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4076 int iF, nbF = vTool.NbFaces();
4077 for ( iF = 0; iF < nbF; iF ++ ) {
4078 if (vTool.IsFreeFace( iF ) &&
4079 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4080 initNodeSet != faceNodeSet) // except an initial face
4082 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4084 freeInd.push_back( iF );
4085 // find source edge of a free face iF
4086 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4087 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4088 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4089 initNodeSet.begin(), initNodeSet.end(),
4090 commonNodes.begin());
4091 if ( (*v)->IsQuadratic() )
4092 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4094 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4096 if ( !srcEdges.back() )
4098 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4099 << iF << " of volume #" << vTool.ID() << endl;
4104 if ( freeInd.empty() )
4107 // create faces for all steps;
4108 // if such a face has been already created by sweep of edge,
4109 // assure that its orientation is OK
4110 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4111 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4112 vTool.SetExternalNormal();
4113 const int nextShift = vTool.IsForward() ? +1 : -1;
4114 list< int >::iterator ind = freeInd.begin();
4115 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4116 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4118 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4119 int nbn = vTool.NbFaceNodes( *ind );
4120 const SMDS_MeshElement * f = 0;
4121 if ( nbn == 3 ) ///// triangle
4123 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4125 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4127 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4129 nodes[ 1 + nextShift ] };
4131 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4133 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4137 else if ( nbn == 4 ) ///// quadrangle
4139 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4141 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4143 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4144 nodes[ 2 ], nodes[ 2+nextShift ] };
4146 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4148 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4149 newOrder[ 2 ], newOrder[ 3 ]));
4152 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4154 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4156 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4158 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4160 nodes[2 + 2*nextShift],
4161 nodes[3 - 2*nextShift],
4163 nodes[3 + 2*nextShift]};
4165 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4167 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4175 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4177 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4178 nodes[1], nodes[3], nodes[5], nodes[7] );
4180 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4182 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4183 nodes[4 - 2*nextShift],
4185 nodes[4 + 2*nextShift],
4187 nodes[5 - 2*nextShift],
4189 nodes[5 + 2*nextShift] };
4191 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4193 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4194 newOrder[ 2 ], newOrder[ 3 ],
4195 newOrder[ 4 ], newOrder[ 5 ],
4196 newOrder[ 6 ], newOrder[ 7 ]));
4199 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4201 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4202 SMDSAbs_Face, /*noMedium=*/false);
4204 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4206 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4207 nodes[4 - 2*nextShift],
4209 nodes[4 + 2*nextShift],
4211 nodes[5 - 2*nextShift],
4213 nodes[5 + 2*nextShift],
4216 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4218 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4219 newOrder[ 2 ], newOrder[ 3 ],
4220 newOrder[ 4 ], newOrder[ 5 ],
4221 newOrder[ 6 ], newOrder[ 7 ],
4225 else //////// polygon
4227 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4228 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4230 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4232 if ( !vTool.IsForward() )
4233 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4235 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4237 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4241 while ( srcElements.Length() < myLastCreatedElems.Length() )
4242 srcElements.Append( *srcEdge );
4244 } // loop on free faces
4246 // go to the next volume
4248 while ( iVol++ < nbVolumesByStep ) v++;
4251 } // loop on volumes of one step
4252 } // sweep free links into faces
4254 // Make a ceiling face with a normal external to a volume
4256 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4258 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4260 lastVol.SetExternalNormal();
4261 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4262 int nbn = lastVol.NbFaceNodes( iF );
4264 if (!hasFreeLinks ||
4265 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4266 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4268 else if ( nbn == 4 )
4270 if (!hasFreeLinks ||
4271 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4272 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4274 else if ( nbn == 6 && isQuadratic )
4276 if (!hasFreeLinks ||
4277 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4278 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4279 nodes[1], nodes[3], nodes[5]));
4281 else if ( nbn == 8 && isQuadratic )
4283 if (!hasFreeLinks ||
4284 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4285 nodes[1], nodes[3], nodes[5], nodes[7]) )
4286 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4287 nodes[1], nodes[3], nodes[5], nodes[7]));
4289 else if ( nbn == 9 && isQuadratic )
4291 if (!hasFreeLinks ||
4292 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4293 SMDSAbs_Face, /*noMedium=*/false) )
4294 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4295 nodes[1], nodes[3], nodes[5], nodes[7],
4299 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4300 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4301 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4304 while ( srcElements.Length() < myLastCreatedElems.Length() )
4305 srcElements.Append( myLastCreatedElems.Last() );
4307 } // loop on swept elements
4310 //=======================================================================
4311 //function : RotationSweep
4313 //=======================================================================
4315 SMESH_MeshEditor::PGroupIDs
4316 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4317 const gp_Ax1& theAxis,
4318 const double theAngle,
4319 const int theNbSteps,
4320 const double theTol,
4321 const bool theMakeGroups,
4322 const bool theMakeWalls)
4324 myLastCreatedElems.Clear();
4325 myLastCreatedNodes.Clear();
4327 // source elements for each generated one
4328 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4330 MESSAGE( "RotationSweep()");
4332 aTrsf.SetRotation( theAxis, theAngle );
4334 aTrsf2.SetRotation( theAxis, theAngle/2. );
4336 gp_Lin aLine( theAxis );
4337 double aSqTol = theTol * theTol;
4339 SMESHDS_Mesh* aMesh = GetMeshDS();
4341 TNodeOfNodeListMap mapNewNodes;
4342 TElemOfVecOfNnlmiMap mapElemNewNodes;
4343 TElemOfElemListMap newElemsMap;
4345 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4346 myMesh->NbFaces(ORDER_QUADRATIC) +
4347 myMesh->NbVolumes(ORDER_QUADRATIC) );
4349 TIDSortedElemSet::iterator itElem;
4350 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4351 const SMDS_MeshElement* elem = *itElem;
4352 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4354 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4355 newNodesItVec.reserve( elem->NbNodes() );
4357 // loop on elem nodes
4358 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4359 while ( itN->more() )
4361 // check if a node has been already sweeped
4362 const SMDS_MeshNode* node = cast2Node( itN->next() );
4364 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4366 aXYZ.Coord( coord[0], coord[1], coord[2] );
4367 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4369 TNodeOfNodeListMapItr nIt =
4370 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4371 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4372 if ( listNewNodes.empty() )
4374 // check if we are to create medium nodes between corner ones
4375 bool needMediumNodes = false;
4376 if ( isQuadraticMesh )
4378 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4379 while (it->more() && !needMediumNodes )
4381 const SMDS_MeshElement* invElem = it->next();
4382 if ( invElem != elem && !theElems.count( invElem )) continue;
4383 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4384 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4385 needMediumNodes = true;
4390 const SMDS_MeshNode * newNode = node;
4391 for ( int i = 0; i < theNbSteps; i++ ) {
4393 if ( needMediumNodes ) // create a medium node
4395 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4396 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4397 myLastCreatedNodes.Append(newNode);
4398 srcNodes.Append( node );
4399 listNewNodes.push_back( newNode );
4400 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4403 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4405 // create a corner node
4406 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4407 myLastCreatedNodes.Append(newNode);
4408 srcNodes.Append( node );
4409 listNewNodes.push_back( newNode );
4412 listNewNodes.push_back( newNode );
4413 // if ( needMediumNodes )
4414 // listNewNodes.push_back( newNode );
4418 newNodesItVec.push_back( nIt );
4420 // make new elements
4421 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4425 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4427 PGroupIDs newGroupIDs;
4428 if ( theMakeGroups )
4429 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4435 //=======================================================================
4436 //function : CreateNode
4438 //=======================================================================
4439 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4442 const double tolnode,
4443 SMESH_SequenceOfNode& aNodes)
4445 // myLastCreatedElems.Clear();
4446 // myLastCreatedNodes.Clear();
4449 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4451 // try to search in sequence of existing nodes
4452 // if aNodes.Length()>0 we 'nave to use given sequence
4453 // else - use all nodes of mesh
4454 if(aNodes.Length()>0) {
4456 for(i=1; i<=aNodes.Length(); i++) {
4457 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4458 if(P1.Distance(P2)<tolnode)
4459 return aNodes.Value(i);
4463 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4464 while(itn->more()) {
4465 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4466 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4467 if(P1.Distance(P2)<tolnode)
4472 // create new node and return it
4473 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4474 //myLastCreatedNodes.Append(NewNode);
4479 //=======================================================================
4480 //function : ExtrusionSweep
4482 //=======================================================================
4484 SMESH_MeshEditor::PGroupIDs
4485 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4486 const gp_Vec& theStep,
4487 const int theNbSteps,
4488 TElemOfElemListMap& newElemsMap,
4489 const bool theMakeGroups,
4491 const double theTolerance)
4493 ExtrusParam aParams;
4494 aParams.myDir = gp_Dir(theStep);
4495 aParams.myNodes.Clear();
4496 aParams.mySteps = new TColStd_HSequenceOfReal;
4498 for(i=1; i<=theNbSteps; i++)
4499 aParams.mySteps->Append(theStep.Magnitude());
4502 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4506 //=======================================================================
4507 //function : ExtrusionSweep
4509 //=======================================================================
4511 SMESH_MeshEditor::PGroupIDs
4512 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4513 ExtrusParam& theParams,
4514 TElemOfElemListMap& newElemsMap,
4515 const bool theMakeGroups,
4517 const double theTolerance)
4519 myLastCreatedElems.Clear();
4520 myLastCreatedNodes.Clear();
4522 // source elements for each generated one
4523 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4525 SMESHDS_Mesh* aMesh = GetMeshDS();
4527 int nbsteps = theParams.mySteps->Length();
4529 TNodeOfNodeListMap mapNewNodes;
4530 //TNodeOfNodeVecMap mapNewNodes;
4531 TElemOfVecOfNnlmiMap mapElemNewNodes;
4532 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4534 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4535 myMesh->NbFaces(ORDER_QUADRATIC) +
4536 myMesh->NbVolumes(ORDER_QUADRATIC) );
4538 TIDSortedElemSet::iterator itElem;
4539 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4540 // check element type
4541 const SMDS_MeshElement* elem = *itElem;
4542 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4545 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4546 newNodesItVec.reserve( elem->NbNodes() );
4548 // loop on elem nodes
4549 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4550 while ( itN->more() )
4552 // check if a node has been already sweeped
4553 const SMDS_MeshNode* node = cast2Node( itN->next() );
4554 TNodeOfNodeListMap::iterator nIt =
4555 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4556 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4557 if ( listNewNodes.empty() )
4561 // check if we are to create medium nodes between corner ones
4562 bool needMediumNodes = false;
4563 if ( isQuadraticMesh )
4565 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4566 while (it->more() && !needMediumNodes )
4568 const SMDS_MeshElement* invElem = it->next();
4569 if ( invElem != elem && !theElems.count( invElem )) continue;
4570 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4571 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4572 needMediumNodes = true;
4576 double coord[] = { node->X(), node->Y(), node->Z() };
4577 for ( int i = 0; i < nbsteps; i++ )
4579 if ( needMediumNodes ) // create a medium node
4581 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4582 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4583 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4584 if( theFlags & EXTRUSION_FLAG_SEW ) {
4585 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4586 theTolerance, theParams.myNodes);
4587 listNewNodes.push_back( newNode );
4590 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4591 myLastCreatedNodes.Append(newNode);
4592 srcNodes.Append( node );
4593 listNewNodes.push_back( newNode );
4596 // create a corner node
4597 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4598 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4599 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4600 if( theFlags & EXTRUSION_FLAG_SEW ) {
4601 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4602 theTolerance, theParams.myNodes);
4603 listNewNodes.push_back( newNode );
4606 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4607 myLastCreatedNodes.Append(newNode);
4608 srcNodes.Append( node );
4609 listNewNodes.push_back( newNode );
4613 newNodesItVec.push_back( nIt );
4615 // make new elements
4616 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4619 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4620 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4622 PGroupIDs newGroupIDs;
4623 if ( theMakeGroups )
4624 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4629 //=======================================================================
4630 //function : ExtrusionAlongTrack
4632 //=======================================================================
4633 SMESH_MeshEditor::Extrusion_Error
4634 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4635 SMESH_subMesh* theTrack,
4636 const SMDS_MeshNode* theN1,
4637 const bool theHasAngles,
4638 list<double>& theAngles,
4639 const bool theLinearVariation,
4640 const bool theHasRefPoint,
4641 const gp_Pnt& theRefPoint,
4642 const bool theMakeGroups)
4644 MESSAGE("ExtrusionAlongTrack");
4645 myLastCreatedElems.Clear();
4646 myLastCreatedNodes.Clear();
4649 std::list<double> aPrms;
4650 TIDSortedElemSet::iterator itElem;
4653 TopoDS_Edge aTrackEdge;
4654 TopoDS_Vertex aV1, aV2;
4656 SMDS_ElemIteratorPtr aItE;
4657 SMDS_NodeIteratorPtr aItN;
4658 SMDSAbs_ElementType aTypeE;
4660 TNodeOfNodeListMap mapNewNodes;
4663 aNbE = theElements.size();
4666 return EXTR_NO_ELEMENTS;
4668 // 1.1 Track Pattern
4671 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4673 aItE = pSubMeshDS->GetElements();
4674 while ( aItE->more() ) {
4675 const SMDS_MeshElement* pE = aItE->next();
4676 aTypeE = pE->GetType();
4677 // Pattern must contain links only
4678 if ( aTypeE != SMDSAbs_Edge )
4679 return EXTR_PATH_NOT_EDGE;
4682 list<SMESH_MeshEditor_PathPoint> fullList;
4684 const TopoDS_Shape& aS = theTrack->GetSubShape();
4685 // Sub-shape for the Pattern must be an Edge or Wire
4686 if( aS.ShapeType() == TopAbs_EDGE ) {
4687 aTrackEdge = TopoDS::Edge( aS );
4688 // the Edge must not be degenerated
4689 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4690 return EXTR_BAD_PATH_SHAPE;
4691 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4692 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4693 const SMDS_MeshNode* aN1 = aItN->next();
4694 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4695 const SMDS_MeshNode* aN2 = aItN->next();
4696 // starting node must be aN1 or aN2
4697 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4698 return EXTR_BAD_STARTING_NODE;
4699 aItN = pSubMeshDS->GetNodes();
4700 while ( aItN->more() ) {
4701 const SMDS_MeshNode* pNode = aItN->next();
4702 const SMDS_EdgePosition* pEPos =
4703 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4704 double aT = pEPos->GetUParameter();
4705 aPrms.push_back( aT );
4707 //Extrusion_Error err =
4708 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4709 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4710 list< SMESH_subMesh* > LSM;
4711 TopTools_SequenceOfShape Edges;
4712 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4713 while(itSM->more()) {
4714 SMESH_subMesh* SM = itSM->next();
4716 const TopoDS_Shape& aS = SM->GetSubShape();
4719 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4720 int startNid = theN1->GetID();
4721 TColStd_MapOfInteger UsedNums;
4723 int NbEdges = Edges.Length();
4725 for(; i<=NbEdges; i++) {
4727 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4728 for(; itLSM!=LSM.end(); itLSM++) {
4730 if(UsedNums.Contains(k)) continue;
4731 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4732 SMESH_subMesh* locTrack = *itLSM;
4733 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4734 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4735 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4736 const SMDS_MeshNode* aN1 = aItN->next();
4737 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4738 const SMDS_MeshNode* aN2 = aItN->next();
4739 // starting node must be aN1 or aN2
4740 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4741 // 2. Collect parameters on the track edge
4743 aItN = locMeshDS->GetNodes();
4744 while ( aItN->more() ) {
4745 const SMDS_MeshNode* pNode = aItN->next();
4746 const SMDS_EdgePosition* pEPos =
4747 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4748 double aT = pEPos->GetUParameter();
4749 aPrms.push_back( aT );
4751 list<SMESH_MeshEditor_PathPoint> LPP;
4752 //Extrusion_Error err =
4753 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4754 LLPPs.push_back(LPP);
4756 // update startN for search following egde
4757 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4758 else startNid = aN1->GetID();
4762 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4763 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4764 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4765 for(; itPP!=firstList.end(); itPP++) {
4766 fullList.push_back( *itPP );
4768 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4769 fullList.pop_back();
4771 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4772 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4773 itPP = currList.begin();
4774 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4775 gp_Dir D1 = PP1.Tangent();
4776 gp_Dir D2 = PP2.Tangent();
4777 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4778 (D1.Z()+D2.Z())/2 ) );
4779 PP1.SetTangent(Dnew);
4780 fullList.push_back(PP1);
4782 for(; itPP!=firstList.end(); itPP++) {
4783 fullList.push_back( *itPP );
4785 PP1 = fullList.back();
4786 fullList.pop_back();
4788 // if wire not closed
4789 fullList.push_back(PP1);
4793 return EXTR_BAD_PATH_SHAPE;
4796 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4797 theHasRefPoint, theRefPoint, theMakeGroups);
4801 //=======================================================================
4802 //function : ExtrusionAlongTrack
4804 //=======================================================================
4805 SMESH_MeshEditor::Extrusion_Error
4806 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4807 SMESH_Mesh* theTrack,
4808 const SMDS_MeshNode* theN1,
4809 const bool theHasAngles,
4810 list<double>& theAngles,
4811 const bool theLinearVariation,
4812 const bool theHasRefPoint,
4813 const gp_Pnt& theRefPoint,
4814 const bool theMakeGroups)
4816 myLastCreatedElems.Clear();
4817 myLastCreatedNodes.Clear();
4820 std::list<double> aPrms;
4821 TIDSortedElemSet::iterator itElem;
4824 TopoDS_Edge aTrackEdge;
4825 TopoDS_Vertex aV1, aV2;
4827 SMDS_ElemIteratorPtr aItE;
4828 SMDS_NodeIteratorPtr aItN;
4829 SMDSAbs_ElementType aTypeE;
4831 TNodeOfNodeListMap mapNewNodes;
4834 aNbE = theElements.size();
4837 return EXTR_NO_ELEMENTS;
4839 // 1.1 Track Pattern
4842 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4844 aItE = pMeshDS->elementsIterator();
4845 while ( aItE->more() ) {
4846 const SMDS_MeshElement* pE = aItE->next();
4847 aTypeE = pE->GetType();
4848 // Pattern must contain links only
4849 if ( aTypeE != SMDSAbs_Edge )
4850 return EXTR_PATH_NOT_EDGE;
4853 list<SMESH_MeshEditor_PathPoint> fullList;
4855 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4857 if( aS == SMESH_Mesh::PseudoShape() ) {
4858 //Mesh without shape
4859 const SMDS_MeshNode* currentNode = NULL;
4860 const SMDS_MeshNode* prevNode = theN1;
4861 std::vector<const SMDS_MeshNode*> aNodesList;
4862 aNodesList.push_back(theN1);
4863 int nbEdges = 0, conn=0;
4864 const SMDS_MeshElement* prevElem = NULL;
4865 const SMDS_MeshElement* currentElem = NULL;
4866 int totalNbEdges = theTrack->NbEdges();
4867 SMDS_ElemIteratorPtr nIt;
4868 bool isClosed = false;
4871 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4872 return EXTR_BAD_STARTING_NODE;
4875 conn = nbEdgeConnectivity(theN1);
4877 return EXTR_PATH_NOT_EDGE;
4879 aItE = theN1->GetInverseElementIterator();
4880 prevElem = aItE->next();
4881 currentElem = prevElem;
4883 if(totalNbEdges == 1 ) {
4884 nIt = currentElem->nodesIterator();
4885 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4886 if(currentNode == prevNode)
4887 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4888 aNodesList.push_back(currentNode);
4890 nIt = currentElem->nodesIterator();
4891 while( nIt->more() ) {
4892 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4893 if(currentNode == prevNode)
4894 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4895 aNodesList.push_back(currentNode);
4897 //case of the closed mesh
4898 if(currentNode == theN1) {
4904 conn = nbEdgeConnectivity(currentNode);
4906 return EXTR_PATH_NOT_EDGE;
4907 }else if( conn == 1 && nbEdges > 0 ) {
4912 prevNode = currentNode;
4913 aItE = currentNode->GetInverseElementIterator();
4914 currentElem = aItE->next();
4915 if( currentElem == prevElem)
4916 currentElem = aItE->next();
4917 nIt = currentElem->nodesIterator();
4918 prevElem = currentElem;
4924 if(nbEdges != totalNbEdges)
4925 return EXTR_PATH_NOT_EDGE;
4927 TopTools_SequenceOfShape Edges;
4928 double x1,x2,y1,y2,z1,z2;
4929 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4930 int startNid = theN1->GetID();
4931 for(int i = 1; i < aNodesList.size(); i++) {
4932 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
4933 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
4934 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
4935 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
4936 list<SMESH_MeshEditor_PathPoint> LPP;
4938 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
4939 LLPPs.push_back(LPP);
4940 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
4941 else startNid = aNodesList[i-1]->GetID();
4945 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4946 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4947 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4948 for(; itPP!=firstList.end(); itPP++) {
4949 fullList.push_back( *itPP );
4952 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4953 SMESH_MeshEditor_PathPoint PP2;
4954 fullList.pop_back();
4956 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4957 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4958 itPP = currList.begin();
4959 PP2 = currList.front();
4960 gp_Dir D1 = PP1.Tangent();
4961 gp_Dir D2 = PP2.Tangent();
4962 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4963 (D1.Z()+D2.Z())/2 ) );
4964 PP1.SetTangent(Dnew);
4965 fullList.push_back(PP1);
4967 for(; itPP!=currList.end(); itPP++) {
4968 fullList.push_back( *itPP );
4970 PP1 = fullList.back();
4971 fullList.pop_back();
4973 fullList.push_back(PP1);
4975 } // Sub-shape for the Pattern must be an Edge or Wire
4976 else if( aS.ShapeType() == TopAbs_EDGE ) {
4977 aTrackEdge = TopoDS::Edge( aS );
4978 // the Edge must not be degenerated
4979 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4980 return EXTR_BAD_PATH_SHAPE;
4981 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4982 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4983 const SMDS_MeshNode* aN1 = aItN->next();
4984 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4985 const SMDS_MeshNode* aN2 = aItN->next();
4986 // starting node must be aN1 or aN2
4987 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4988 return EXTR_BAD_STARTING_NODE;
4989 aItN = pMeshDS->nodesIterator();
4990 while ( aItN->more() ) {
4991 const SMDS_MeshNode* pNode = aItN->next();
4992 if( pNode==aN1 || pNode==aN2 ) continue;
4993 const SMDS_EdgePosition* pEPos =
4994 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4995 double aT = pEPos->GetUParameter();
4996 aPrms.push_back( aT );
4998 //Extrusion_Error err =
4999 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5001 else if( aS.ShapeType() == TopAbs_WIRE ) {
5002 list< SMESH_subMesh* > LSM;
5003 TopTools_SequenceOfShape Edges;
5004 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5005 for(; eExp.More(); eExp.Next()) {
5006 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5007 if( BRep_Tool::Degenerated(E) ) continue;
5008 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5014 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5015 int startNid = theN1->GetID();
5016 TColStd_MapOfInteger UsedNums;
5017 int NbEdges = Edges.Length();
5019 for(; i<=NbEdges; i++) {
5021 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5022 for(; itLSM!=LSM.end(); itLSM++) {
5024 if(UsedNums.Contains(k)) continue;
5025 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5026 SMESH_subMesh* locTrack = *itLSM;
5027 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5028 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5029 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5030 const SMDS_MeshNode* aN1 = aItN->next();
5031 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5032 const SMDS_MeshNode* aN2 = aItN->next();
5033 // starting node must be aN1 or aN2
5034 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5035 // 2. Collect parameters on the track edge
5037 aItN = locMeshDS->GetNodes();
5038 while ( aItN->more() ) {
5039 const SMDS_MeshNode* pNode = aItN->next();
5040 const SMDS_EdgePosition* pEPos =
5041 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5042 double aT = pEPos->GetUParameter();
5043 aPrms.push_back( aT );
5045 list<SMESH_MeshEditor_PathPoint> LPP;
5046 //Extrusion_Error err =
5047 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5048 LLPPs.push_back(LPP);
5050 // update startN for search following egde
5051 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5052 else startNid = aN1->GetID();
5056 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5057 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5058 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5059 for(; itPP!=firstList.end(); itPP++) {
5060 fullList.push_back( *itPP );
5062 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5063 fullList.pop_back();
5065 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5066 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5067 itPP = currList.begin();
5068 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5069 gp_Dir D1 = PP1.Tangent();
5070 gp_Dir D2 = PP2.Tangent();
5071 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5072 (D1.Z()+D2.Z())/2 ) );
5073 PP1.SetTangent(Dnew);
5074 fullList.push_back(PP1);
5076 for(; itPP!=currList.end(); itPP++) {
5077 fullList.push_back( *itPP );
5079 PP1 = fullList.back();
5080 fullList.pop_back();
5082 // if wire not closed
5083 fullList.push_back(PP1);
5087 return EXTR_BAD_PATH_SHAPE;
5090 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5091 theHasRefPoint, theRefPoint, theMakeGroups);
5095 //=======================================================================
5096 //function : MakeEdgePathPoints
5097 //purpose : auxilary for ExtrusionAlongTrack
5098 //=======================================================================
5099 SMESH_MeshEditor::Extrusion_Error
5100 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5101 const TopoDS_Edge& aTrackEdge,
5103 list<SMESH_MeshEditor_PathPoint>& LPP)
5105 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5107 aTolVec2=aTolVec*aTolVec;
5109 TopoDS_Vertex aV1, aV2;
5110 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5111 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5112 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5113 // 2. Collect parameters on the track edge
5114 aPrms.push_front( aT1 );
5115 aPrms.push_back( aT2 );
5118 if( FirstIsStart ) {
5129 SMESH_MeshEditor_PathPoint aPP;
5130 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5131 std::list<double>::iterator aItD = aPrms.begin();
5132 for(; aItD != aPrms.end(); ++aItD) {
5136 aC3D->D1( aT, aP3D, aVec );
5137 aL2 = aVec.SquareMagnitude();
5138 if ( aL2 < aTolVec2 )
5139 return EXTR_CANT_GET_TANGENT;
5140 gp_Dir aTgt( aVec );
5142 aPP.SetTangent( aTgt );
5143 aPP.SetParameter( aT );
5150 //=======================================================================
5151 //function : MakeExtrElements
5152 //purpose : auxilary for ExtrusionAlongTrack
5153 //=======================================================================
5154 SMESH_MeshEditor::Extrusion_Error
5155 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5156 list<SMESH_MeshEditor_PathPoint>& fullList,
5157 const bool theHasAngles,
5158 list<double>& theAngles,
5159 const bool theLinearVariation,
5160 const bool theHasRefPoint,
5161 const gp_Pnt& theRefPoint,
5162 const bool theMakeGroups)
5164 MESSAGE("MakeExtrElements");
5165 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5166 int aNbTP = fullList.size();
5167 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5169 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5170 LinearAngleVariation(aNbTP-1, theAngles);
5172 vector<double> aAngles( aNbTP );
5174 for(; j<aNbTP; ++j) {
5177 if ( theHasAngles ) {
5179 std::list<double>::iterator aItD = theAngles.begin();
5180 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5182 aAngles[j] = anAngle;
5185 // fill vector of path points with angles
5186 //aPPs.resize(fullList.size());
5188 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5189 for(; itPP!=fullList.end(); itPP++) {
5191 SMESH_MeshEditor_PathPoint PP = *itPP;
5192 PP.SetAngle(aAngles[j]);
5196 TNodeOfNodeListMap mapNewNodes;
5197 TElemOfVecOfNnlmiMap mapElemNewNodes;
5198 TElemOfElemListMap newElemsMap;
5199 TIDSortedElemSet::iterator itElem;
5202 SMDSAbs_ElementType aTypeE;
5203 // source elements for each generated one
5204 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5206 // 3. Center of rotation aV0
5207 gp_Pnt aV0 = theRefPoint;
5209 if ( !theHasRefPoint ) {
5211 aGC.SetCoord( 0.,0.,0. );
5213 itElem = theElements.begin();
5214 for ( ; itElem != theElements.end(); itElem++ ) {
5215 const SMDS_MeshElement* elem = *itElem;
5217 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5218 while ( itN->more() ) {
5219 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5224 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5225 list<const SMDS_MeshNode*> aLNx;
5226 mapNewNodes[node] = aLNx;
5228 gp_XYZ aXYZ( aX, aY, aZ );
5236 } // if (!theHasRefPoint) {
5237 mapNewNodes.clear();
5239 // 4. Processing the elements
5240 SMESHDS_Mesh* aMesh = GetMeshDS();
5242 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5243 // check element type
5244 const SMDS_MeshElement* elem = *itElem;
5245 aTypeE = elem->GetType();
5246 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5249 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5250 newNodesItVec.reserve( elem->NbNodes() );
5252 // loop on elem nodes
5254 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5255 while ( itN->more() )
5258 // check if a node has been already processed
5259 const SMDS_MeshNode* node =
5260 static_cast<const SMDS_MeshNode*>( itN->next() );
5261 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5262 if ( nIt == mapNewNodes.end() ) {
5263 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5264 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5267 aX = node->X(); aY = node->Y(); aZ = node->Z();
5269 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5270 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5271 gp_Ax1 anAx1, anAxT1T0;
5272 gp_Dir aDT1x, aDT0x, aDT1T0;
5277 aPN0.SetCoord(aX, aY, aZ);
5279 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5281 aDT0x= aPP0.Tangent();
5282 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5284 for ( j = 1; j < aNbTP; ++j ) {
5285 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5287 aDT1x = aPP1.Tangent();
5288 aAngle1x = aPP1.Angle();
5290 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5292 gp_Vec aV01x( aP0x, aP1x );
5293 aTrsf.SetTranslation( aV01x );
5296 aV1x = aV0x.Transformed( aTrsf );
5297 aPN1 = aPN0.Transformed( aTrsf );
5299 // rotation 1 [ T1,T0 ]
5300 aAngleT1T0=-aDT1x.Angle( aDT0x );
5301 if (fabs(aAngleT1T0) > aTolAng) {
5303 anAxT1T0.SetLocation( aV1x );
5304 anAxT1T0.SetDirection( aDT1T0 );
5305 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5307 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5311 if ( theHasAngles ) {
5312 anAx1.SetLocation( aV1x );
5313 anAx1.SetDirection( aDT1x );
5314 aTrsfRot.SetRotation( anAx1, aAngle1x );
5316 aPN1 = aPN1.Transformed( aTrsfRot );
5320 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5321 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5322 // create additional node
5323 double x = ( aPN1.X() + aPN0.X() )/2.;
5324 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5325 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5326 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5327 myLastCreatedNodes.Append(newNode);
5328 srcNodes.Append( node );
5329 listNewNodes.push_back( newNode );
5334 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5335 myLastCreatedNodes.Append(newNode);
5336 srcNodes.Append( node );
5337 listNewNodes.push_back( newNode );
5347 // if current elem is quadratic and current node is not medium
5348 // we have to check - may be it is needed to insert additional nodes
5349 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5350 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5351 if(listNewNodes.size()==aNbTP-1) {
5352 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5353 gp_XYZ P(node->X(), node->Y(), node->Z());
5354 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5356 for(i=0; i<aNbTP-1; i++) {
5357 const SMDS_MeshNode* N = *it;
5358 double x = ( N->X() + P.X() )/2.;
5359 double y = ( N->Y() + P.Y() )/2.;
5360 double z = ( N->Z() + P.Z() )/2.;
5361 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5362 srcNodes.Append( node );
5363 myLastCreatedNodes.Append(newN);
5366 P = gp_XYZ(N->X(),N->Y(),N->Z());
5368 listNewNodes.clear();
5369 for(i=0; i<2*(aNbTP-1); i++) {
5370 listNewNodes.push_back(aNodes[i]);
5376 newNodesItVec.push_back( nIt );
5378 // make new elements
5379 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5380 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5381 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5384 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5386 if ( theMakeGroups )
5387 generateGroups( srcNodes, srcElems, "extruded");
5393 //=======================================================================
5394 //function : LinearAngleVariation
5395 //purpose : auxilary for ExtrusionAlongTrack
5396 //=======================================================================
5397 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5398 list<double>& Angles)
5400 int nbAngles = Angles.size();
5401 if( nbSteps > nbAngles ) {
5402 vector<double> theAngles(nbAngles);
5403 list<double>::iterator it = Angles.begin();
5405 for(; it!=Angles.end(); it++) {
5407 theAngles[i] = (*it);
5410 double rAn2St = double( nbAngles ) / double( nbSteps );
5411 double angPrev = 0, angle;
5412 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5413 double angCur = rAn2St * ( iSt+1 );
5414 double angCurFloor = floor( angCur );
5415 double angPrevFloor = floor( angPrev );
5416 if ( angPrevFloor == angCurFloor )
5417 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5419 int iP = int( angPrevFloor );
5420 double angPrevCeil = ceil(angPrev);
5421 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5423 int iC = int( angCurFloor );
5424 if ( iC < nbAngles )
5425 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5427 iP = int( angPrevCeil );
5429 angle += theAngles[ iC ];
5431 res.push_back(angle);
5436 for(; it!=res.end(); it++)
5437 Angles.push_back( *it );
5442 //================================================================================
5444 * \brief Move or copy theElements applying theTrsf to their nodes
5445 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5446 * \param theTrsf - transformation to apply
5447 * \param theCopy - if true, create translated copies of theElems
5448 * \param theMakeGroups - if true and theCopy, create translated groups
5449 * \param theTargetMesh - mesh to copy translated elements into
5450 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5452 //================================================================================
5454 SMESH_MeshEditor::PGroupIDs
5455 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5456 const gp_Trsf& theTrsf,
5458 const bool theMakeGroups,
5459 SMESH_Mesh* theTargetMesh)
5461 myLastCreatedElems.Clear();
5462 myLastCreatedNodes.Clear();
5464 bool needReverse = false;
5465 string groupPostfix;
5466 switch ( theTrsf.Form() ) {
5468 MESSAGE("gp_PntMirror");
5470 groupPostfix = "mirrored";
5473 MESSAGE("gp_Ax1Mirror");
5474 groupPostfix = "mirrored";
5477 MESSAGE("gp_Ax2Mirror");
5479 groupPostfix = "mirrored";
5482 MESSAGE("gp_Rotation");
5483 groupPostfix = "rotated";
5485 case gp_Translation:
5486 MESSAGE("gp_Translation");
5487 groupPostfix = "translated";
5490 MESSAGE("gp_Scale");
5491 groupPostfix = "scaled";
5493 case gp_CompoundTrsf: // different scale by axis
5494 MESSAGE("gp_CompoundTrsf");
5495 groupPostfix = "scaled";
5499 needReverse = false;
5500 groupPostfix = "transformed";
5503 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5504 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5505 SMESHDS_Mesh* aMesh = GetMeshDS();
5508 // map old node to new one
5509 TNodeNodeMap nodeMap;
5511 // elements sharing moved nodes; those of them which have all
5512 // nodes mirrored but are not in theElems are to be reversed
5513 TIDSortedElemSet inverseElemSet;
5515 // source elements for each generated one
5516 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5518 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5519 TIDSortedElemSet orphanNode;
5521 if ( theElems.empty() ) // transform the whole mesh
5524 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5525 while ( eIt->more() ) theElems.insert( eIt->next() );
5527 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5528 while ( nIt->more() )
5530 const SMDS_MeshNode* node = nIt->next();
5531 if ( node->NbInverseElements() == 0)
5532 orphanNode.insert( node );
5536 // loop on elements to transform nodes : first orphan nodes then elems
5537 TIDSortedElemSet::iterator itElem;
5538 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5539 for (int i=0; i<2; i++)
5540 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5541 const SMDS_MeshElement* elem = *itElem;
5545 // loop on elem nodes
5546 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5547 while ( itN->more() ) {
5549 const SMDS_MeshNode* node = cast2Node( itN->next() );
5550 // check if a node has been already transformed
5551 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5552 nodeMap.insert( make_pair ( node, node ));
5553 if ( !n2n_isnew.second )
5557 coord[0] = node->X();
5558 coord[1] = node->Y();
5559 coord[2] = node->Z();
5560 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5561 if ( theTargetMesh ) {
5562 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5563 n2n_isnew.first->second = newNode;
5564 myLastCreatedNodes.Append(newNode);
5565 srcNodes.Append( node );
5567 else if ( theCopy ) {
5568 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5569 n2n_isnew.first->second = newNode;
5570 myLastCreatedNodes.Append(newNode);
5571 srcNodes.Append( node );
5574 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5575 // node position on shape becomes invalid
5576 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5577 ( SMDS_SpacePosition::originSpacePosition() );
5580 // keep inverse elements
5581 if ( !theCopy && !theTargetMesh && needReverse ) {
5582 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5583 while ( invElemIt->more() ) {
5584 const SMDS_MeshElement* iel = invElemIt->next();
5585 inverseElemSet.insert( iel );
5591 // either create new elements or reverse mirrored ones
5592 if ( !theCopy && !needReverse && !theTargetMesh )
5595 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5596 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5597 theElems.insert( *invElemIt );
5599 // Replicate or reverse elements
5601 std::vector<int> iForw;
5602 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5604 const SMDS_MeshElement* elem = *itElem;
5605 if ( !elem || elem->GetType() == SMDSAbs_Node )
5608 int nbNodes = elem->NbNodes();
5609 int elemType = elem->GetType();
5611 if (elem->IsPoly()) {
5613 // polygon or polyhedral volume
5614 switch ( elemType ) {
5617 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5619 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5620 while (itN->more()) {
5621 const SMDS_MeshNode* node =
5622 static_cast<const SMDS_MeshNode*>(itN->next());
5623 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5624 if (nodeMapIt == nodeMap.end())
5625 break; // not all nodes transformed
5627 // reverse mirrored faces and volumes
5628 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5630 poly_nodes[iNode] = (*nodeMapIt).second;
5634 if ( iNode != nbNodes )
5635 continue; // not all nodes transformed
5637 if ( theTargetMesh ) {
5638 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5639 srcElems.Append( elem );
5641 else if ( theCopy ) {
5642 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5643 srcElems.Append( elem );
5646 aMesh->ChangePolygonNodes(elem, poly_nodes);
5650 case SMDSAbs_Volume:
5652 const SMDS_VtkVolume* aPolyedre =
5653 dynamic_cast<const SMDS_VtkVolume*>( elem );
5655 MESSAGE("Warning: bad volumic element");
5659 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5660 vector<int> quantities;
5662 bool allTransformed = true;
5663 int nbFaces = aPolyedre->NbFaces();
5664 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5665 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5666 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5667 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5668 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5669 if (nodeMapIt == nodeMap.end()) {
5670 allTransformed = false; // not all nodes transformed
5672 poly_nodes.push_back((*nodeMapIt).second);
5674 if ( needReverse && allTransformed )
5675 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5677 quantities.push_back(nbFaceNodes);
5679 if ( !allTransformed )
5680 continue; // not all nodes transformed
5682 if ( theTargetMesh ) {
5683 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5684 srcElems.Append( elem );
5686 else if ( theCopy ) {
5687 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5688 srcElems.Append( elem );
5691 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5703 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5704 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5705 const std::vector<int>& i = needReverse ? iRev : iForw;
5707 // find transformed nodes
5708 vector<const SMDS_MeshNode*> nodes(nbNodes);
5710 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5711 while ( itN->more() ) {
5712 const SMDS_MeshNode* node =
5713 static_cast<const SMDS_MeshNode*>( itN->next() );
5714 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5715 if ( nodeMapIt == nodeMap.end() )
5716 break; // not all nodes transformed
5717 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5719 if ( iNode != nbNodes )
5720 continue; // not all nodes transformed
5722 if ( theTargetMesh ) {
5723 if ( SMDS_MeshElement* copy =
5724 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5725 myLastCreatedElems.Append( copy );
5726 srcElems.Append( elem );
5729 else if ( theCopy ) {
5730 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5731 srcElems.Append( elem );
5734 // reverse element as it was reversed by transformation
5736 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5739 } // loop on elements
5741 PGroupIDs newGroupIDs;
5743 if ( ( theMakeGroups && theCopy ) ||
5744 ( theMakeGroups && theTargetMesh ) )
5745 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5750 //=======================================================================
5752 * \brief Create groups of elements made during transformation
5753 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5754 * \param elemGens - elements making corresponding myLastCreatedElems
5755 * \param postfix - to append to names of new groups
5757 //=======================================================================
5759 SMESH_MeshEditor::PGroupIDs
5760 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5761 const SMESH_SequenceOfElemPtr& elemGens,
5762 const std::string& postfix,
5763 SMESH_Mesh* targetMesh)
5765 PGroupIDs newGroupIDs( new list<int> );
5766 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5768 // Sort existing groups by types and collect their names
5770 // to store an old group and a generated new one
5771 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5772 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5774 set< string > groupNames;
5776 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5777 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5778 while ( groupIt->more() ) {
5779 SMESH_Group * group = groupIt->next();
5780 if ( !group ) continue;
5781 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5782 if ( !groupDS || groupDS->IsEmpty() ) continue;
5783 groupNames.insert( group->GetName() );
5784 groupDS->SetStoreName( group->GetName() );
5785 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5790 // loop on nodes and elements
5791 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5793 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5794 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5795 if ( gens.Length() != elems.Length() )
5796 throw SALOME_Exception(LOCALIZED("invalid args"));
5798 // loop on created elements
5799 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5801 const SMDS_MeshElement* sourceElem = gens( iElem );
5802 if ( !sourceElem ) {
5803 MESSAGE("generateGroups(): NULL source element");
5806 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5807 if ( groupsOldNew.empty() ) {
5808 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5809 ++iElem; // skip all elements made by sourceElem
5812 // collect all elements made by sourceElem
5813 list< const SMDS_MeshElement* > resultElems;
5814 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5815 if ( resElem != sourceElem )
5816 resultElems.push_back( resElem );
5817 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5818 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5819 if ( resElem != sourceElem )
5820 resultElems.push_back( resElem );
5821 // do not generate element groups from node ones
5822 // if ( sourceElem->GetType() == SMDSAbs_Node &&
5823 // elems( iElem )->GetType() != SMDSAbs_Node )
5826 // add resultElems to groups made by ones the sourceElem belongs to
5827 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5828 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5830 SMESHDS_GroupBase* oldGroup = gOldNew->first;
5831 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5833 SMDS_MeshGroup* & newGroup = gOldNew->second;
5834 if ( !newGroup )// create a new group
5837 string name = oldGroup->GetStoreName();
5838 if ( !targetMesh ) {
5842 while ( !groupNames.insert( name ).second ) // name exists
5848 TCollection_AsciiString nbStr(nb+1);
5849 name.resize( name.rfind('_')+1 );
5850 name += nbStr.ToCString();
5857 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5859 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5860 newGroup = & groupDS->SMDSGroup();
5861 newGroupIDs->push_back( id );
5864 // fill in a new group
5865 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5866 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5867 newGroup->Add( *resElemIt );
5870 } // loop on created elements
5871 }// loop on nodes and elements
5876 //================================================================================
5878 * \brief Return list of group of nodes close to each other within theTolerance
5879 * Search among theNodes or in the whole mesh if theNodes is empty using
5880 * an Octree algorithm
5882 //================================================================================
5884 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
5885 const double theTolerance,
5886 TListOfListOfNodes & theGroupsOfNodes)
5888 myLastCreatedElems.Clear();
5889 myLastCreatedNodes.Clear();
5891 if ( theNodes.empty() )
5892 { // get all nodes in the mesh
5893 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
5894 while ( nIt->more() )
5895 theNodes.insert( theNodes.end(),nIt->next());
5898 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
5902 //=======================================================================
5904 * \brief Implementation of search for the node closest to point
5906 //=======================================================================
5908 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
5910 //---------------------------------------------------------------------
5912 * \brief Constructor
5914 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
5916 myMesh = ( SMESHDS_Mesh* ) theMesh;
5918 TIDSortedNodeSet nodes;
5920 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
5921 while ( nIt->more() )
5922 nodes.insert( nodes.end(), nIt->next() );
5924 myOctreeNode = new SMESH_OctreeNode(nodes) ;
5926 // get max size of a leaf box
5927 SMESH_OctreeNode* tree = myOctreeNode;
5928 while ( !tree->isLeaf() )
5930 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5934 myHalfLeafSize = tree->maxSize() / 2.;
5937 //---------------------------------------------------------------------
5939 * \brief Move node and update myOctreeNode accordingly
5941 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
5943 myOctreeNode->UpdateByMoveNode( node, toPnt );
5944 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
5947 //---------------------------------------------------------------------
5949 * \brief Do it's job
5951 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
5953 map<double, const SMDS_MeshNode*> dist2Nodes;
5954 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
5955 if ( !dist2Nodes.empty() )
5956 return dist2Nodes.begin()->second;
5957 list<const SMDS_MeshNode*> nodes;
5958 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
5960 double minSqDist = DBL_MAX;
5961 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
5963 // sort leafs by their distance from thePnt
5964 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
5965 TDistTreeMap treeMap;
5966 list< SMESH_OctreeNode* > treeList;
5967 list< SMESH_OctreeNode* >::iterator trIt;
5968 treeList.push_back( myOctreeNode );
5970 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5971 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
5972 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
5974 SMESH_OctreeNode* tree = *trIt;
5975 if ( !tree->isLeaf() ) // put children to the queue
5977 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
5978 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5979 while ( cIt->more() )
5980 treeList.push_back( cIt->next() );
5982 else if ( tree->NbNodes() ) // put a tree to the treeMap
5984 const Bnd_B3d& box = tree->getBox();
5985 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
5986 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
5987 if ( !it_in.second ) // not unique distance to box center
5988 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
5991 // find distance after which there is no sense to check tree's
5992 double sqLimit = DBL_MAX;
5993 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
5994 if ( treeMap.size() > 5 ) {
5995 SMESH_OctreeNode* closestTree = sqDist_tree->second;
5996 const Bnd_B3d& box = closestTree->getBox();
5997 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
5998 sqLimit = limit * limit;
6000 // get all nodes from trees
6001 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6002 if ( sqDist_tree->first > sqLimit )
6004 SMESH_OctreeNode* tree = sqDist_tree->second;
6005 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6008 // find closest among nodes
6009 minSqDist = DBL_MAX;
6010 const SMDS_MeshNode* closestNode = 0;
6011 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6012 for ( ; nIt != nodes.end(); ++nIt ) {
6013 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6014 if ( minSqDist > sqDist ) {
6022 //---------------------------------------------------------------------
6026 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6028 //---------------------------------------------------------------------
6030 * \brief Return the node tree
6032 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6035 SMESH_OctreeNode* myOctreeNode;
6036 SMESHDS_Mesh* myMesh;
6037 double myHalfLeafSize; // max size of a leaf box
6040 //=======================================================================
6042 * \brief Return SMESH_NodeSearcher
6044 //=======================================================================
6046 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6048 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6051 // ========================================================================
6052 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6054 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6055 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6056 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6058 //=======================================================================
6060 * \brief Octal tree of bounding boxes of elements
6062 //=======================================================================
6064 class ElementBndBoxTree : public SMESH_Octree
6068 ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(), double tolerance = NodeRadius );
6069 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
6070 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6071 ~ElementBndBoxTree();
6074 ElementBndBoxTree() {}
6075 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6076 void buildChildrenData();
6077 Bnd_B3d* buildRootBox();
6079 //!< Bounding box of element
6080 struct ElementBox : public Bnd_B3d
6082 const SMDS_MeshElement* _element;
6083 int _refCount; // an ElementBox can be included in several tree branches
6084 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6086 vector< ElementBox* > _elements;
6089 //================================================================================
6091 * \brief ElementBndBoxTree creation
6093 //================================================================================
6095 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6096 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6098 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6099 _elements.reserve( nbElems );
6101 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6102 while ( elemIt->more() )
6103 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6105 if ( _elements.size() > MaxNbElemsInLeaf )
6111 //================================================================================
6115 //================================================================================
6117 ElementBndBoxTree::~ElementBndBoxTree()
6119 for ( int i = 0; i < _elements.size(); ++i )
6120 if ( --_elements[i]->_refCount <= 0 )
6121 delete _elements[i];
6124 //================================================================================
6126 * \brief Return the maximal box
6128 //================================================================================
6130 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6132 Bnd_B3d* box = new Bnd_B3d;
6133 for ( int i = 0; i < _elements.size(); ++i )
6134 box->Add( *_elements[i] );
6138 //================================================================================
6140 * \brief Redistrubute element boxes among children
6142 //================================================================================
6144 void ElementBndBoxTree::buildChildrenData()
6146 for ( int i = 0; i < _elements.size(); ++i )
6148 for (int j = 0; j < 8; j++)
6150 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6152 _elements[i]->_refCount++;
6153 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6156 _elements[i]->_refCount--;
6160 for (int j = 0; j < 8; j++)
6162 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6163 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6164 child->myIsLeaf = true;
6166 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6167 child->_elements.resize( child->_elements.size() ); // compact
6171 //================================================================================
6173 * \brief Return elements which can include the point
6175 //================================================================================
6177 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6178 TIDSortedElemSet& foundElems)
6180 if ( level() && getBox().IsOut( point.XYZ() ))
6185 for ( int i = 0; i < _elements.size(); ++i )
6186 if ( !_elements[i]->IsOut( point.XYZ() ))
6187 foundElems.insert( _elements[i]->_element );
6191 for (int i = 0; i < 8; i++)
6192 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6196 //================================================================================
6198 * \brief Return elements which can be intersected by the line
6200 //================================================================================
6202 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6203 TIDSortedElemSet& foundElems)
6205 if ( level() && getBox().IsOut( line ))
6210 for ( int i = 0; i < _elements.size(); ++i )
6211 if ( !_elements[i]->IsOut( line ))
6212 foundElems.insert( _elements[i]->_element );
6216 for (int i = 0; i < 8; i++)
6217 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6221 //================================================================================
6223 * \brief Construct the element box
6225 //================================================================================
6227 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6231 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6232 while ( nIt->more() )
6233 Add( SMESH_TNodeXYZ( cast2Node( nIt->next() )));
6234 Enlarge( tolerance );
6239 //=======================================================================
6241 * \brief Implementation of search for the elements by point and
6242 * of classification of point in 2D mesh
6244 //=======================================================================
6246 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6248 SMESHDS_Mesh* _mesh;
6249 SMDS_ElemIteratorPtr _meshPartIt;
6250 ElementBndBoxTree* _ebbTree;
6251 SMESH_NodeSearcherImpl* _nodeSearcher;
6252 SMDSAbs_ElementType _elementType;
6254 bool _outerFacesFound;
6255 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6257 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6258 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6259 ~SMESH_ElementSearcherImpl()
6261 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6262 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6264 virtual int FindElementsByPoint(const gp_Pnt& point,
6265 SMDSAbs_ElementType type,
6266 vector< const SMDS_MeshElement* >& foundElements);
6267 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6269 void GetElementsNearLine( const gp_Ax1& line,
6270 SMDSAbs_ElementType type,
6271 vector< const SMDS_MeshElement* >& foundElems);
6272 double getTolerance();
6273 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6274 const double tolerance, double & param);
6275 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6276 bool isOuterBoundary(const SMDS_MeshElement* face) const
6278 return _outerFaces.empty() || _outerFaces.count(face);
6280 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6282 const SMDS_MeshElement* _face;
6284 bool _coincides; //!< the line lays in face plane
6285 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6286 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6288 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6291 TIDSortedElemSet _faces;
6292 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6293 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6297 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6299 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6300 << ", _coincides="<<i._coincides << ")";
6303 //=======================================================================
6305 * \brief define tolerance for search
6307 //=======================================================================
6309 double SMESH_ElementSearcherImpl::getTolerance()
6311 if ( _tolerance < 0 )
6313 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6316 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6318 double boxSize = _nodeSearcher->getTree()->maxSize();
6319 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6321 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6323 double boxSize = _ebbTree->maxSize();
6324 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6326 if ( _tolerance == 0 )
6328 // define tolerance by size of a most complex element
6329 int complexType = SMDSAbs_Volume;
6330 while ( complexType > SMDSAbs_All &&
6331 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6333 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6335 if ( complexType == int( SMDSAbs_Node ))
6337 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6339 if ( meshInfo.NbNodes() > 2 )
6340 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6344 SMDS_ElemIteratorPtr elemIt =
6345 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6346 const SMDS_MeshElement* elem = elemIt->next();
6347 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6348 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6350 while ( nodeIt->more() )
6352 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6353 elemSize = max( dist, elemSize );
6356 _tolerance = 1e-4 * elemSize;
6362 //================================================================================
6364 * \brief Find intersection of the line and an edge of face and return parameter on line
6366 //================================================================================
6368 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6369 const SMDS_MeshElement* face,
6376 GeomAPI_ExtremaCurveCurve anExtCC;
6377 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6379 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6380 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6382 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6383 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6384 anExtCC.Init( lineCurve, edge);
6385 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6387 Quantity_Parameter pl, pe;
6388 anExtCC.LowerDistanceParameters( pl, pe );
6390 if ( ++nbInts == 2 )
6394 if ( nbInts > 0 ) param /= nbInts;
6397 //================================================================================
6399 * \brief Find all faces belonging to the outer boundary of mesh
6401 //================================================================================
6403 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6405 if ( _outerFacesFound ) return;
6407 // Collect all outer faces by passing from one outer face to another via their links
6408 // and BTW find out if there are internal faces at all.
6410 // checked links and links where outer boundary meets internal one
6411 set< SMESH_TLink > visitedLinks, seamLinks;
6413 // links to treat with already visited faces sharing them
6414 list < TFaceLink > startLinks;
6416 // load startLinks with the first outerFace
6417 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6418 _outerFaces.insert( outerFace );
6420 TIDSortedElemSet emptySet;
6421 while ( !startLinks.empty() )
6423 const SMESH_TLink& link = startLinks.front()._link;
6424 TIDSortedElemSet& faces = startLinks.front()._faces;
6426 outerFace = *faces.begin();
6427 // find other faces sharing the link
6428 const SMDS_MeshElement* f;
6429 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6432 // select another outer face among the found
6433 const SMDS_MeshElement* outerFace2 = 0;
6434 if ( faces.size() == 2 )
6436 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6438 else if ( faces.size() > 2 )
6440 seamLinks.insert( link );
6442 // link direction within the outerFace
6443 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6444 SMESH_TNodeXYZ( link.node2()));
6445 int i1 = outerFace->GetNodeIndex( link.node1() );
6446 int i2 = outerFace->GetNodeIndex( link.node2() );
6447 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6448 if ( rev ) n1n2.Reverse();
6450 gp_XYZ ofNorm, fNorm;
6451 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6453 // direction from the link inside outerFace
6454 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6455 // sort all other faces by angle with the dirInOF
6456 map< double, const SMDS_MeshElement* > angle2Face;
6457 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6458 for ( ; face != faces.end(); ++face )
6460 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6462 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6463 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6464 if ( angle < 0 ) angle += 2. * M_PI;
6465 angle2Face.insert( make_pair( angle, *face ));
6467 if ( !angle2Face.empty() )
6468 outerFace2 = angle2Face.begin()->second;
6471 // store the found outer face and add its links to continue seaching from
6474 _outerFaces.insert( outerFace );
6475 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6476 for ( int i = 0; i < nbNodes; ++i )
6478 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6479 if ( visitedLinks.insert( link2 ).second )
6480 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6483 startLinks.pop_front();
6485 _outerFacesFound = true;
6487 if ( !seamLinks.empty() )
6489 // There are internal boundaries touching the outher one,
6490 // find all faces of internal boundaries in order to find
6491 // faces of boundaries of holes, if any.
6496 _outerFaces.clear();
6500 //=======================================================================
6502 * \brief Find elements of given type where the given point is IN or ON.
6503 * Returns nb of found elements and elements them-selves.
6505 * 'ALL' type means elements of any type excluding nodes and 0D elements
6507 //=======================================================================
6509 int SMESH_ElementSearcherImpl::
6510 FindElementsByPoint(const gp_Pnt& point,
6511 SMDSAbs_ElementType type,
6512 vector< const SMDS_MeshElement* >& foundElements)
6514 foundElements.clear();
6516 double tolerance = getTolerance();
6518 // =================================================================================
6519 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6521 if ( !_nodeSearcher )
6522 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6524 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6525 if ( !closeNode ) return foundElements.size();
6527 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6528 return foundElements.size(); // to far from any node
6530 if ( type == SMDSAbs_Node )
6532 foundElements.push_back( closeNode );
6536 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6537 while ( elemIt->more() )
6538 foundElements.push_back( elemIt->next() );
6541 // =================================================================================
6542 else // elements more complex than 0D
6544 if ( !_ebbTree || _elementType != type )
6546 if ( _ebbTree ) delete _ebbTree;
6547 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6549 TIDSortedElemSet suspectElems;
6550 _ebbTree->getElementsNearPoint( point, suspectElems );
6551 TIDSortedElemSet::iterator elem = suspectElems.begin();
6552 for ( ; elem != suspectElems.end(); ++elem )
6553 if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6554 foundElements.push_back( *elem );
6556 return foundElements.size();
6559 //================================================================================
6561 * \brief Classify the given point in the closed 2D mesh
6563 //================================================================================
6565 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6567 double tolerance = getTolerance();
6568 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6570 if ( _ebbTree ) delete _ebbTree;
6571 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6573 // Algo: analyse transition of a line starting at the point through mesh boundary;
6574 // try three lines parallel to axis of the coordinate system and perform rough
6575 // analysis. If solution is not clear perform thorough analysis.
6577 const int nbAxes = 3;
6578 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6579 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6580 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6581 multimap< int, int > nbInt2Axis; // to find the simplest case
6582 for ( int axis = 0; axis < nbAxes; ++axis )
6584 gp_Ax1 lineAxis( point, axisDir[axis]);
6585 gp_Lin line ( lineAxis );
6587 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6588 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6590 // Intersect faces with the line
6592 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6593 TIDSortedElemSet::iterator face = suspectFaces.begin();
6594 for ( ; face != suspectFaces.end(); ++face )
6598 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6599 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6601 // perform intersection
6602 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6603 if ( !intersection.IsDone() )
6605 if ( intersection.IsInQuadric() )
6607 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6609 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6611 gp_Pnt intersectionPoint = intersection.Point(1);
6612 if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6613 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6616 // Analyse intersections roughly
6618 int nbInter = u2inters.size();
6622 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6623 if ( nbInter == 1 ) // not closed mesh
6624 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6626 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6629 if ( (f<0) == (l<0) )
6632 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6633 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6634 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6637 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6639 if ( _outerFacesFound ) break; // pass to thorough analysis
6641 } // three attempts - loop on CS axes
6643 // Analyse intersections thoroughly.
6644 // We make two loops maximum, on the first one we only exclude touching intersections,
6645 // on the second, if situation is still unclear, we gather and use information on
6646 // position of faces (internal or outer). If faces position is already gathered,
6647 // we make the second loop right away.
6649 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6651 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6652 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6654 int axis = nb_axis->second;
6655 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6657 gp_Ax1 lineAxis( point, axisDir[axis]);
6658 gp_Lin line ( lineAxis );
6660 // add tangent intersections to u2inters
6662 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6663 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6664 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6665 u2inters.insert(make_pair( param, *tgtInt ));
6666 tangentInters[ axis ].clear();
6668 // Count intersections before and after the point excluding touching ones.
6669 // If hasPositionInfo we count intersections of outer boundary only
6671 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6672 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6673 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6674 bool ok = ! u_int1->second._coincides;
6675 while ( ok && u_int1 != u2inters.end() )
6677 double u = u_int1->first;
6678 bool touchingInt = false;
6679 if ( ++u_int2 != u2inters.end() )
6681 // skip intersections at the same point (if the line passes through edge or node)
6683 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6689 // skip tangent intersections
6691 const SMDS_MeshElement* prevFace = u_int1->second._face;
6692 while ( ok && u_int2->second._coincides )
6694 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6700 ok = ( u_int2 != u2inters.end() );
6705 // skip intersections at the same point after tangent intersections
6708 double u2 = u_int2->first;
6710 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6716 // decide if we skipped a touching intersection
6717 if ( nbSamePnt + nbTgt > 0 )
6719 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6720 map< double, TInters >::iterator u_int = u_int1;
6721 for ( ; u_int != u_int2; ++u_int )
6723 if ( u_int->second._coincides ) continue;
6724 double dot = u_int->second._faceNorm * line.Direction();
6725 if ( dot > maxDot ) maxDot = dot;
6726 if ( dot < minDot ) minDot = dot;
6728 touchingInt = ( minDot*maxDot < 0 );
6733 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6744 u_int1 = u_int2; // to next intersection
6746 } // loop on intersections with one line
6750 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6753 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6756 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6757 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6759 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6762 if ( (f<0) == (l<0) )
6765 if ( hasPositionInfo )
6766 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
6768 } // loop on intersections of the tree lines - thorough analysis
6770 if ( !hasPositionInfo )
6772 // gather info on faces position - is face in the outer boundary or not
6773 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
6774 findOuterBoundary( u2inters.begin()->second._face );
6777 } // two attempts - with and w/o faces position info in the mesh
6779 return TopAbs_UNKNOWN;
6782 //=======================================================================
6784 * \brief Return elements possibly intersecting the line
6786 //=======================================================================
6788 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
6789 SMDSAbs_ElementType type,
6790 vector< const SMDS_MeshElement* >& foundElems)
6792 if ( !_ebbTree || _elementType != type )
6794 if ( _ebbTree ) delete _ebbTree;
6795 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6797 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
6798 _ebbTree->getElementsNearLine( line, suspectFaces );
6799 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
6802 //=======================================================================
6804 * \brief Return SMESH_ElementSearcher
6806 //=======================================================================
6808 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
6810 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
6813 //=======================================================================
6815 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
6817 //=======================================================================
6819 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
6821 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
6824 //=======================================================================
6826 * \brief Return true if the point is IN or ON of the element
6828 //=======================================================================
6830 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
6832 if ( element->GetType() == SMDSAbs_Volume)
6834 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
6837 // get ordered nodes
6839 vector< gp_XYZ > xyz;
6840 vector<const SMDS_MeshNode*> nodeList;
6842 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
6843 if ( element->IsQuadratic() ) {
6844 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
6845 nodeIt = f->interlacedNodesElemIterator();
6846 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
6847 nodeIt = e->interlacedNodesElemIterator();
6849 while ( nodeIt->more() )
6851 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
6852 xyz.push_back( SMESH_TNodeXYZ(node) );
6853 nodeList.push_back(node);
6856 int i, nbNodes = element->NbNodes();
6858 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
6860 // compute face normal
6861 gp_Vec faceNorm(0,0,0);
6862 xyz.push_back( xyz.front() );
6863 nodeList.push_back( nodeList.front() );
6864 for ( i = 0; i < nbNodes; ++i )
6866 gp_Vec edge1( xyz[i+1], xyz[i]);
6867 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
6868 faceNorm += edge1 ^ edge2;
6870 double normSize = faceNorm.Magnitude();
6871 if ( normSize <= tol )
6873 // degenerated face: point is out if it is out of all face edges
6874 for ( i = 0; i < nbNodes; ++i )
6876 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
6877 if ( !isOut( &edge, point, tol ))
6882 faceNorm /= normSize;
6884 // check if the point lays on face plane
6885 gp_Vec n2p( xyz[0], point );
6886 if ( fabs( n2p * faceNorm ) > tol )
6887 return true; // not on face plane
6889 // check if point is out of face boundary:
6890 // define it by closest transition of a ray point->infinity through face boundary
6891 // on the face plane.
6892 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
6893 // to find intersections of the ray with the boundary.
6895 gp_Vec plnNorm = ray ^ faceNorm;
6896 normSize = plnNorm.Magnitude();
6897 if ( normSize <= tol ) return false; // point coincides with the first node
6898 plnNorm /= normSize;
6899 // for each node of the face, compute its signed distance to the plane
6900 vector<double> dist( nbNodes + 1);
6901 for ( i = 0; i < nbNodes; ++i )
6903 gp_Vec n2p( xyz[i], point );
6904 dist[i] = n2p * plnNorm;
6906 dist.back() = dist.front();
6907 // find the closest intersection
6909 double rClosest, distClosest = 1e100;;
6911 for ( i = 0; i < nbNodes; ++i )
6914 if ( fabs( dist[i]) < tol )
6916 else if ( fabs( dist[i+1]) < tol )
6918 else if ( dist[i] * dist[i+1] < 0 )
6919 r = dist[i] / ( dist[i] - dist[i+1] );
6921 continue; // no intersection
6922 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
6923 gp_Vec p2int ( point, pInt);
6924 if ( p2int * ray > -tol ) // right half-space
6926 double intDist = p2int.SquareMagnitude();
6927 if ( intDist < distClosest )
6932 distClosest = intDist;
6937 return true; // no intesections - out
6939 // analyse transition
6940 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
6941 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
6942 gp_Vec p2int ( point, pClosest );
6943 bool out = (edgeNorm * p2int) < -tol;
6944 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
6947 // ray pass through a face node; analyze transition through an adjacent edge
6948 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
6949 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
6950 gp_Vec edgeAdjacent( p1, p2 );
6951 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
6952 bool out2 = (edgeNorm2 * p2int) < -tol;
6954 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
6955 return covexCorner ? (out || out2) : (out && out2);
6957 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
6959 // point is out of edge if it is NOT ON any straight part of edge
6960 // (we consider quadratic edge as being composed of two straight parts)
6961 for ( i = 1; i < nbNodes; ++i )
6963 gp_Vec edge( xyz[i-1], xyz[i]);
6964 gp_Vec n1p ( xyz[i-1], point);
6965 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
6968 gp_Vec n2p( xyz[i], point );
6969 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
6971 return false; // point is ON this part
6975 // Node or 0D element -------------------------------------------------------------------------
6977 gp_Vec n2p ( xyz[0], point );
6978 return n2p.Magnitude() <= tol;
6983 //=======================================================================
6984 //function : SimplifyFace
6986 //=======================================================================
6987 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
6988 vector<const SMDS_MeshNode *>& poly_nodes,
6989 vector<int>& quantities) const
6991 int nbNodes = faceNodes.size();
6996 set<const SMDS_MeshNode*> nodeSet;
6998 // get simple seq of nodes
6999 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7000 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7001 int iSimple = 0, nbUnique = 0;
7003 simpleNodes[iSimple++] = faceNodes[0];
7005 for (int iCur = 1; iCur < nbNodes; iCur++) {
7006 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7007 simpleNodes[iSimple++] = faceNodes[iCur];
7008 if (nodeSet.insert( faceNodes[iCur] ).second)
7012 int nbSimple = iSimple;
7013 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7023 bool foundLoop = (nbSimple > nbUnique);
7026 set<const SMDS_MeshNode*> loopSet;
7027 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7028 const SMDS_MeshNode* n = simpleNodes[iSimple];
7029 if (!loopSet.insert( n ).second) {
7033 int iC = 0, curLast = iSimple;
7034 for (; iC < curLast; iC++) {
7035 if (simpleNodes[iC] == n) break;
7037 int loopLen = curLast - iC;
7039 // create sub-element
7041 quantities.push_back(loopLen);
7042 for (; iC < curLast; iC++) {
7043 poly_nodes.push_back(simpleNodes[iC]);
7046 // shift the rest nodes (place from the first loop position)
7047 for (iC = curLast + 1; iC < nbSimple; iC++) {
7048 simpleNodes[iC - loopLen] = simpleNodes[iC];
7050 nbSimple -= loopLen;
7053 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7054 } // while (foundLoop)
7058 quantities.push_back(iSimple);
7059 for (int i = 0; i < iSimple; i++)
7060 poly_nodes.push_back(simpleNodes[i]);
7066 //=======================================================================
7067 //function : MergeNodes
7068 //purpose : In each group, the cdr of nodes are substituted by the first one
7070 //=======================================================================
7072 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7074 MESSAGE("MergeNodes");
7075 myLastCreatedElems.Clear();
7076 myLastCreatedNodes.Clear();
7078 SMESHDS_Mesh* aMesh = GetMeshDS();
7080 TNodeNodeMap nodeNodeMap; // node to replace - new node
7081 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7082 list< int > rmElemIds, rmNodeIds;
7084 // Fill nodeNodeMap and elems
7086 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7087 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7088 list<const SMDS_MeshNode*>& nodes = *grIt;
7089 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7090 const SMDS_MeshNode* nToKeep = *nIt;
7091 //MESSAGE("node to keep " << nToKeep->GetID());
7092 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7093 const SMDS_MeshNode* nToRemove = *nIt;
7094 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7095 if ( nToRemove != nToKeep ) {
7096 //MESSAGE(" node to remove " << nToRemove->GetID());
7097 rmNodeIds.push_back( nToRemove->GetID() );
7098 AddToSameGroups( nToKeep, nToRemove, aMesh );
7101 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7102 while ( invElemIt->more() ) {
7103 const SMDS_MeshElement* elem = invElemIt->next();
7108 // Change element nodes or remove an element
7110 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7111 for ( ; eIt != elems.end(); eIt++ ) {
7112 const SMDS_MeshElement* elem = *eIt;
7113 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7114 int nbNodes = elem->NbNodes();
7115 int aShapeId = FindShape( elem );
7117 set<const SMDS_MeshNode*> nodeSet;
7118 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7119 int iUnique = 0, iCur = 0, nbRepl = 0;
7120 vector<int> iRepl( nbNodes );
7122 // get new seq of nodes
7123 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7124 while ( itN->more() ) {
7125 const SMDS_MeshNode* n =
7126 static_cast<const SMDS_MeshNode*>( itN->next() );
7128 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7129 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7131 // BUG 0020185: begin
7133 bool stopRecur = false;
7134 set<const SMDS_MeshNode*> nodesRecur;
7135 nodesRecur.insert(n);
7136 while (!stopRecur) {
7137 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7138 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7139 n = (*nnIt_i).second;
7140 if (!nodesRecur.insert(n).second) {
7141 // error: recursive dependancy
7151 curNodes[ iCur ] = n;
7152 bool isUnique = nodeSet.insert( n ).second;
7154 uniqueNodes[ iUnique++ ] = n;
7156 iRepl[ nbRepl++ ] = iCur;
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 const int id = elem->GetID();
9038 const int nbNodes = elem->NbNodes();
9039 const SMDSAbs_ElementType aType = elem->GetType();
9040 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9041 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9042 if ( aGeomType == SMDSEntity_Polyhedra )
9043 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9044 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9045 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9047 // remove a linear element
9048 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9050 const SMDS_MeshElement* NewElem = 0;
9056 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9064 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9067 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9070 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9075 case SMDSAbs_Volume :
9079 case SMDSEntity_Tetra:
9080 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9082 case SMDSEntity_Pyramid:
9083 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9085 case SMDSEntity_Penta:
9086 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9088 case SMDSEntity_Hexa:
9089 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9090 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9092 case SMDSEntity_Hexagonal_Prism:
9094 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9101 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9103 theSm->AddElement( NewElem );
9108 //=======================================================================
9109 //function : ConvertToQuadratic
9111 //=======================================================================
9113 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9115 SMESHDS_Mesh* meshDS = GetMeshDS();
9117 SMESH_MesherHelper aHelper(*myMesh);
9118 aHelper.SetIsQuadratic( true );
9120 int nbCheckedElems = 0;
9121 if ( myMesh->HasShapeToMesh() )
9123 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9125 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9126 while ( smIt->more() ) {
9127 SMESH_subMesh* sm = smIt->next();
9128 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9129 aHelper.SetSubShape( sm->GetSubShape() );
9130 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9135 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9136 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9138 SMESHDS_SubMesh *smDS = 0;
9139 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9140 while(aEdgeItr->more())
9142 const SMDS_MeshEdge* edge = aEdgeItr->next();
9143 if(edge && !edge->IsQuadratic())
9145 int id = edge->GetID();
9146 //MESSAGE("edge->GetID() " << id);
9147 const SMDS_MeshNode* n1 = edge->GetNode(0);
9148 const SMDS_MeshNode* n2 = edge->GetNode(1);
9150 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9152 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9153 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9156 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9157 while(aFaceItr->more())
9159 const SMDS_MeshFace* face = aFaceItr->next();
9160 if(!face || face->IsQuadratic() ) continue;
9162 const int id = face->GetID();
9163 const SMDSAbs_EntityType type = face->GetEntityType();
9164 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9166 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9168 SMDS_MeshFace * NewFace = 0;
9171 case SMDSEntity_Triangle:
9172 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9174 case SMDSEntity_Quadrangle:
9175 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9178 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9180 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9182 vector<int> nbNodeInFaces;
9183 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9184 while(aVolumeItr->more())
9186 const SMDS_MeshVolume* volume = aVolumeItr->next();
9187 if(!volume || volume->IsQuadratic() ) continue;
9189 const int id = volume->GetID();
9190 const SMDSAbs_EntityType type = volume->GetEntityType();
9191 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9192 if ( type == SMDSEntity_Polyhedra )
9193 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9194 else if ( type == SMDSEntity_Hexagonal_Prism )
9195 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9197 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9199 SMDS_MeshVolume * NewVolume = 0;
9202 case SMDSEntity_Tetra:
9203 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9205 case SMDSEntity_Hexa:
9206 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9207 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9209 case SMDSEntity_Pyramid:
9210 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9211 nodes[3], nodes[4], id, theForce3d);
9213 case SMDSEntity_Penta:
9214 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9215 nodes[3], nodes[4], nodes[5], id, theForce3d);
9217 case SMDSEntity_Hexagonal_Prism:
9219 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9221 ReplaceElemInGroups(volume, NewVolume, meshDS);
9226 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9227 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9228 aHelper.FixQuadraticElements();
9232 //================================================================================
9234 * \brief Makes given elements quadratic
9235 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9236 * \param theElements - elements to make quadratic
9238 //================================================================================
9240 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9241 TIDSortedElemSet& theElements)
9243 if ( theElements.empty() ) return;
9245 // we believe that all theElements are of the same type
9246 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9248 // get all nodes shared by theElements
9249 TIDSortedNodeSet allNodes;
9250 TIDSortedElemSet::iterator eIt = theElements.begin();
9251 for ( ; eIt != theElements.end(); ++eIt )
9252 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9254 // complete theElements with elements of lower dim whose all nodes are in allNodes
9256 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9257 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9258 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9259 for ( ; nIt != allNodes.end(); ++nIt )
9261 const SMDS_MeshNode* n = *nIt;
9262 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9263 while ( invIt->more() )
9265 const SMDS_MeshElement* e = invIt->next();
9266 if ( e->IsQuadratic() )
9268 quadAdjacentElems[ e->GetType() ].insert( e );
9271 if ( e->GetType() >= elemType )
9273 continue; // same type of more complex linear element
9276 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9277 continue; // e is already checked
9281 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9282 while ( nodeIt->more() && allIn )
9283 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9285 theElements.insert(e );
9289 SMESH_MesherHelper helper(*myMesh);
9290 helper.SetIsQuadratic( true );
9292 // add links of quadratic adjacent elements to the helper
9294 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9295 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9296 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9298 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9300 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9301 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9302 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9304 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9306 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9307 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9308 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9310 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9313 // make quadratic elements instead of linear ones
9315 SMESHDS_Mesh* meshDS = GetMeshDS();
9316 SMESHDS_SubMesh* smDS = 0;
9317 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9319 const SMDS_MeshElement* elem = *eIt;
9320 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9323 const int id = elem->GetID();
9324 const SMDSAbs_ElementType type = elem->GetType();
9325 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9327 if ( !smDS || !smDS->Contains( elem ))
9328 smDS = meshDS->MeshElements( elem->getshapeId() );
9329 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9331 SMDS_MeshElement * newElem = 0;
9332 switch( nodes.size() )
9334 case 4: // cases for most frequently used element types go first (for optimization)
9335 if ( type == SMDSAbs_Volume )
9336 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9338 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9341 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9342 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9345 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9348 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9351 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9352 nodes[4], id, theForce3d);
9355 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9356 nodes[4], nodes[5], id, theForce3d);
9360 ReplaceElemInGroups( elem, newElem, meshDS);
9361 if( newElem && smDS )
9362 smDS->AddElement( newElem );
9365 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9366 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9367 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9368 helper.FixQuadraticElements();
9372 //=======================================================================
9374 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9375 * \return int - nb of checked elements
9377 //=======================================================================
9379 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9380 SMDS_ElemIteratorPtr theItr,
9381 const int theShapeID)
9384 SMESHDS_Mesh* meshDS = GetMeshDS();
9386 while( theItr->more() )
9388 const SMDS_MeshElement* elem = theItr->next();
9390 if( elem && elem->IsQuadratic())
9392 int id = elem->GetID();
9393 int nbCornerNodes = elem->NbCornerNodes();
9394 SMDSAbs_ElementType aType = elem->GetType();
9396 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9398 //remove a quadratic element
9399 if ( !theSm || !theSm->Contains( elem ))
9400 theSm = meshDS->MeshElements( elem->getshapeId() );
9401 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9403 // remove medium nodes
9404 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9405 if ( nodes[i]->NbInverseElements() == 0 )
9406 meshDS->RemoveFreeNode( nodes[i], theSm );
9408 // add a linear element
9409 nodes.resize( nbCornerNodes );
9410 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9411 ReplaceElemInGroups(elem, newElem, meshDS);
9412 if( theSm && newElem )
9413 theSm->AddElement( newElem );
9419 //=======================================================================
9420 //function : ConvertFromQuadratic
9422 //=======================================================================
9424 bool SMESH_MeshEditor::ConvertFromQuadratic()
9426 int nbCheckedElems = 0;
9427 if ( myMesh->HasShapeToMesh() )
9429 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9431 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9432 while ( smIt->more() ) {
9433 SMESH_subMesh* sm = smIt->next();
9434 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9435 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9441 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9442 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9444 SMESHDS_SubMesh *aSM = 0;
9445 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9453 //================================================================================
9455 * \brief Return true if all medium nodes of the element are in the node set
9457 //================================================================================
9459 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9461 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9462 if ( !nodeSet.count( elem->GetNode(i) ))
9468 //================================================================================
9470 * \brief Makes given elements linear
9472 //================================================================================
9474 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9476 if ( theElements.empty() ) return;
9478 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9479 set<int> mediumNodeIDs;
9480 TIDSortedElemSet::iterator eIt = theElements.begin();
9481 for ( ; eIt != theElements.end(); ++eIt )
9483 const SMDS_MeshElement* e = *eIt;
9484 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9485 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9488 // replace given elements by linear ones
9489 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9490 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9491 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9493 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9494 // except those elements sharing medium nodes of quadratic element whose medium nodes
9495 // are not all in mediumNodeIDs
9497 // get remaining medium nodes
9498 TIDSortedNodeSet mediumNodes;
9499 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9500 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9501 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9502 mediumNodes.insert( mediumNodes.end(), n );
9504 // find more quadratic elements to convert
9505 TIDSortedElemSet moreElemsToConvert;
9506 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9507 for ( ; nIt != mediumNodes.end(); ++nIt )
9509 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9510 while ( invIt->more() )
9512 const SMDS_MeshElement* e = invIt->next();
9513 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9515 // find a more complex element including e and
9516 // whose medium nodes are not in mediumNodes
9517 bool complexFound = false;
9518 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9520 SMDS_ElemIteratorPtr invIt2 =
9521 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9522 while ( invIt2->more() )
9524 const SMDS_MeshElement* eComplex = invIt2->next();
9525 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9527 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9528 if ( nbCommonNodes == e->NbNodes())
9530 complexFound = true;
9531 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9537 if ( !complexFound )
9538 moreElemsToConvert.insert( e );
9542 elemIt = SMDS_ElemIteratorPtr
9543 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9544 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9547 //=======================================================================
9548 //function : SewSideElements
9550 //=======================================================================
9552 SMESH_MeshEditor::Sew_Error
9553 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9554 TIDSortedElemSet& theSide2,
9555 const SMDS_MeshNode* theFirstNode1,
9556 const SMDS_MeshNode* theFirstNode2,
9557 const SMDS_MeshNode* theSecondNode1,
9558 const SMDS_MeshNode* theSecondNode2)
9560 myLastCreatedElems.Clear();
9561 myLastCreatedNodes.Clear();
9563 MESSAGE ("::::SewSideElements()");
9564 if ( theSide1.size() != theSide2.size() )
9565 return SEW_DIFF_NB_OF_ELEMENTS;
9567 Sew_Error aResult = SEW_OK;
9569 // 1. Build set of faces representing each side
9570 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9571 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9573 // =======================================================================
9574 // 1. Build set of faces representing each side:
9575 // =======================================================================
9576 // a. build set of nodes belonging to faces
9577 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9578 // c. create temporary faces representing side of volumes if correspondent
9579 // face does not exist
9581 SMESHDS_Mesh* aMesh = GetMeshDS();
9582 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9583 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9584 TIDSortedElemSet faceSet1, faceSet2;
9585 set<const SMDS_MeshElement*> volSet1, volSet2;
9586 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9587 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9588 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9589 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9590 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9591 int iSide, iFace, iNode;
9593 list<const SMDS_MeshElement* > tempFaceList;
9594 for ( iSide = 0; iSide < 2; iSide++ ) {
9595 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9596 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9597 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9598 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9599 set<const SMDS_MeshElement*>::iterator vIt;
9600 TIDSortedElemSet::iterator eIt;
9601 set<const SMDS_MeshNode*>::iterator nIt;
9603 // check that given nodes belong to given elements
9604 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9605 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9606 int firstIndex = -1, secondIndex = -1;
9607 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9608 const SMDS_MeshElement* elem = *eIt;
9609 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9610 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9611 if ( firstIndex > -1 && secondIndex > -1 ) break;
9613 if ( firstIndex < 0 || secondIndex < 0 ) {
9614 // we can simply return until temporary faces created
9615 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9618 // -----------------------------------------------------------
9619 // 1a. Collect nodes of existing faces
9620 // and build set of face nodes in order to detect missing
9621 // faces corresponding to sides of volumes
9622 // -----------------------------------------------------------
9624 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9626 // loop on the given element of a side
9627 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9628 //const SMDS_MeshElement* elem = *eIt;
9629 const SMDS_MeshElement* elem = *eIt;
9630 if ( elem->GetType() == SMDSAbs_Face ) {
9631 faceSet->insert( elem );
9632 set <const SMDS_MeshNode*> faceNodeSet;
9633 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9634 while ( nodeIt->more() ) {
9635 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9636 nodeSet->insert( n );
9637 faceNodeSet.insert( n );
9639 setOfFaceNodeSet.insert( faceNodeSet );
9641 else if ( elem->GetType() == SMDSAbs_Volume )
9642 volSet->insert( elem );
9644 // ------------------------------------------------------------------------------
9645 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9646 // ------------------------------------------------------------------------------
9648 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9649 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9650 while ( fIt->more() ) { // loop on faces sharing a node
9651 const SMDS_MeshElement* f = fIt->next();
9652 if ( faceSet->find( f ) == faceSet->end() ) {
9653 // check if all nodes are in nodeSet and
9654 // complete setOfFaceNodeSet if they are
9655 set <const SMDS_MeshNode*> faceNodeSet;
9656 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9657 bool allInSet = true;
9658 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9659 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9660 if ( nodeSet->find( n ) == nodeSet->end() )
9663 faceNodeSet.insert( n );
9666 faceSet->insert( f );
9667 setOfFaceNodeSet.insert( faceNodeSet );
9673 // -------------------------------------------------------------------------
9674 // 1c. Create temporary faces representing sides of volumes if correspondent
9675 // face does not exist
9676 // -------------------------------------------------------------------------
9678 if ( !volSet->empty() ) {
9679 //int nodeSetSize = nodeSet->size();
9681 // loop on given volumes
9682 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9683 SMDS_VolumeTool vol (*vIt);
9684 // loop on volume faces: find free faces
9685 // --------------------------------------
9686 list<const SMDS_MeshElement* > freeFaceList;
9687 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9688 if ( !vol.IsFreeFace( iFace ))
9690 // check if there is already a face with same nodes in a face set
9691 const SMDS_MeshElement* aFreeFace = 0;
9692 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9693 int nbNodes = vol.NbFaceNodes( iFace );
9694 set <const SMDS_MeshNode*> faceNodeSet;
9695 vol.GetFaceNodes( iFace, faceNodeSet );
9696 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9698 // no such a face is given but it still can exist, check it
9699 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9700 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9703 // create a temporary face
9704 if ( nbNodes == 3 ) {
9705 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9706 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9708 else if ( nbNodes == 4 ) {
9709 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9710 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9713 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9714 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9715 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9718 tempFaceList.push_back( aFreeFace );
9722 freeFaceList.push_back( aFreeFace );
9724 } // loop on faces of a volume
9726 // choose one of several free faces of a volume
9727 // --------------------------------------------
9728 if ( freeFaceList.size() > 1 ) {
9729 // choose a face having max nb of nodes shared by other elems of a side
9730 int maxNbNodes = -1;
9731 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9732 while ( fIt != freeFaceList.end() ) { // loop on free faces
9733 int nbSharedNodes = 0;
9734 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9735 while ( nodeIt->more() ) { // loop on free face nodes
9736 const SMDS_MeshNode* n =
9737 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9738 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9739 while ( invElemIt->more() ) {
9740 const SMDS_MeshElement* e = invElemIt->next();
9741 nbSharedNodes += faceSet->count( e );
9742 nbSharedNodes += elemSet->count( e );
9745 if ( nbSharedNodes > maxNbNodes ) {
9746 maxNbNodes = nbSharedNodes;
9747 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9749 else if ( nbSharedNodes == maxNbNodes ) {
9753 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9756 if ( freeFaceList.size() > 1 )
9758 // could not choose one face, use another way
9759 // choose a face most close to the bary center of the opposite side
9760 gp_XYZ aBC( 0., 0., 0. );
9761 set <const SMDS_MeshNode*> addedNodes;
9762 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9763 eIt = elemSet2->begin();
9764 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9765 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9766 while ( nodeIt->more() ) { // loop on free face nodes
9767 const SMDS_MeshNode* n =
9768 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9769 if ( addedNodes.insert( n ).second )
9770 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9773 aBC /= addedNodes.size();
9774 double minDist = DBL_MAX;
9775 fIt = freeFaceList.begin();
9776 while ( fIt != freeFaceList.end() ) { // loop on free faces
9778 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9779 while ( nodeIt->more() ) { // loop on free face nodes
9780 const SMDS_MeshNode* n =
9781 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9782 gp_XYZ p( n->X(),n->Y(),n->Z() );
9783 dist += ( aBC - p ).SquareModulus();
9785 if ( dist < minDist ) {
9787 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9790 fIt = freeFaceList.erase( fIt++ );
9793 } // choose one of several free faces of a volume
9795 if ( freeFaceList.size() == 1 ) {
9796 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9797 faceSet->insert( aFreeFace );
9798 // complete a node set with nodes of a found free face
9799 // for ( iNode = 0; iNode < ; iNode++ )
9800 // nodeSet->insert( fNodes[ iNode ] );
9803 } // loop on volumes of a side
9805 // // complete a set of faces if new nodes in a nodeSet appeared
9806 // // ----------------------------------------------------------
9807 // if ( nodeSetSize != nodeSet->size() ) {
9808 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9809 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9810 // while ( fIt->more() ) { // loop on faces sharing a node
9811 // const SMDS_MeshElement* f = fIt->next();
9812 // if ( faceSet->find( f ) == faceSet->end() ) {
9813 // // check if all nodes are in nodeSet and
9814 // // complete setOfFaceNodeSet if they are
9815 // set <const SMDS_MeshNode*> faceNodeSet;
9816 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9817 // bool allInSet = true;
9818 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9819 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9820 // if ( nodeSet->find( n ) == nodeSet->end() )
9821 // allInSet = false;
9823 // faceNodeSet.insert( n );
9825 // if ( allInSet ) {
9826 // faceSet->insert( f );
9827 // setOfFaceNodeSet.insert( faceNodeSet );
9833 } // Create temporary faces, if there are volumes given
9836 if ( faceSet1.size() != faceSet2.size() ) {
9837 // delete temporary faces: they are in reverseElements of actual nodes
9838 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9839 // while ( tmpFaceIt->more() )
9840 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9841 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9842 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9843 // aMesh->RemoveElement(*tmpFaceIt);
9844 MESSAGE("Diff nb of faces");
9845 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9848 // ============================================================
9849 // 2. Find nodes to merge:
9850 // bind a node to remove to a node to put instead
9851 // ============================================================
9853 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9854 if ( theFirstNode1 != theFirstNode2 )
9855 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9856 if ( theSecondNode1 != theSecondNode2 )
9857 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9859 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9860 set< long > linkIdSet; // links to process
9861 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9863 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9864 list< NLink > linkList[2];
9865 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9866 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9867 // loop on links in linkList; find faces by links and append links
9868 // of the found faces to linkList
9869 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9870 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9872 NLink link[] = { *linkIt[0], *linkIt[1] };
9873 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9874 if ( !linkIdSet.count( linkID ) )
9877 // by links, find faces in the face sets,
9878 // and find indices of link nodes in the found faces;
9879 // in a face set, there is only one or no face sharing a link
9880 // ---------------------------------------------------------------
9882 const SMDS_MeshElement* face[] = { 0, 0 };
9883 vector<const SMDS_MeshNode*> fnodes[2];
9884 int iLinkNode[2][2];
9885 TIDSortedElemSet avoidSet;
9886 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9887 const SMDS_MeshNode* n1 = link[iSide].first;
9888 const SMDS_MeshNode* n2 = link[iSide].second;
9889 //cout << "Side " << iSide << " ";
9890 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9891 // find a face by two link nodes
9892 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
9893 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
9896 //cout << " F " << face[ iSide]->GetID() <<endl;
9897 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9898 // put face nodes to fnodes
9899 if ( face[ iSide ]->IsQuadratic() )
9901 // use interlaced nodes iterator
9902 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9903 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9904 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9905 while ( nIter->more() )
9906 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9910 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9911 face[ iSide ]->end_nodes() );
9913 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9917 // check similarity of elements of the sides
9918 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9919 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9920 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9921 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9924 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9926 break; // do not return because it's necessary to remove tmp faces
9929 // set nodes to merge
9930 // -------------------
9932 if ( face[0] && face[1] ) {
9933 const int nbNodes = face[0]->NbNodes();
9934 if ( nbNodes != face[1]->NbNodes() ) {
9935 MESSAGE("Diff nb of face nodes");
9936 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9937 break; // do not return because it s necessary to remove tmp faces
9939 bool reverse[] = { false, false }; // order of nodes in the link
9940 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9941 // analyse link orientation in faces
9942 int i1 = iLinkNode[ iSide ][ 0 ];
9943 int i2 = iLinkNode[ iSide ][ 1 ];
9944 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9946 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9947 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9948 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9950 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9951 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9954 // add other links of the faces to linkList
9955 // -----------------------------------------
9957 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9958 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9959 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9960 if ( !iter_isnew.second ) { // already in a set: no need to process
9961 linkIdSet.erase( iter_isnew.first );
9963 else // new in set == encountered for the first time: add
9965 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9966 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9967 linkList[0].push_back ( NLink( n1, n2 ));
9968 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9973 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9976 } // loop on link lists
9978 if ( aResult == SEW_OK &&
9979 ( //linkIt[0] != linkList[0].end() ||
9980 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9981 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9982 " " << (faceSetPtr[1]->empty()));
9983 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9986 // ====================================================================
9987 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9988 // ====================================================================
9990 // delete temporary faces
9991 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9992 // while ( tmpFaceIt->more() )
9993 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9994 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9995 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9996 aMesh->RemoveElement(*tmpFaceIt);
9998 if ( aResult != SEW_OK)
10001 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10002 // loop on nodes replacement map
10003 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10004 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10005 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10006 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10007 nodeIDsToRemove.push_back( nToRemove->GetID() );
10008 // loop on elements sharing nToRemove
10009 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10010 while ( invElemIt->more() ) {
10011 const SMDS_MeshElement* e = invElemIt->next();
10012 // get a new suite of nodes: make replacement
10013 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10014 vector< const SMDS_MeshNode*> nodes( nbNodes );
10015 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10016 while ( nIt->more() ) {
10017 const SMDS_MeshNode* n =
10018 static_cast<const SMDS_MeshNode*>( nIt->next() );
10019 nnIt = nReplaceMap.find( n );
10020 if ( nnIt != nReplaceMap.end() ) {
10022 n = (*nnIt).second;
10026 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10027 // elemIDsToRemove.push_back( e->GetID() );
10031 SMDSAbs_ElementType etyp = e->GetType();
10032 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10035 myLastCreatedElems.Append(newElem);
10036 AddToSameGroups(newElem, e, aMesh);
10037 int aShapeId = e->getshapeId();
10040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10043 aMesh->RemoveElement(e);
10048 Remove( nodeIDsToRemove, true );
10053 //================================================================================
10055 * \brief Find corresponding nodes in two sets of faces
10056 * \param theSide1 - first face set
10057 * \param theSide2 - second first face
10058 * \param theFirstNode1 - a boundary node of set 1
10059 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10060 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10061 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10062 * \param nReplaceMap - output map of corresponding nodes
10063 * \return bool - is a success or not
10065 //================================================================================
10068 //#define DEBUG_MATCHING_NODES
10071 SMESH_MeshEditor::Sew_Error
10072 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10073 set<const SMDS_MeshElement*>& theSide2,
10074 const SMDS_MeshNode* theFirstNode1,
10075 const SMDS_MeshNode* theFirstNode2,
10076 const SMDS_MeshNode* theSecondNode1,
10077 const SMDS_MeshNode* theSecondNode2,
10078 TNodeNodeMap & nReplaceMap)
10080 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10082 nReplaceMap.clear();
10083 if ( theFirstNode1 != theFirstNode2 )
10084 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10085 if ( theSecondNode1 != theSecondNode2 )
10086 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10088 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10089 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10091 list< NLink > linkList[2];
10092 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10093 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10095 // loop on links in linkList; find faces by links and append links
10096 // of the found faces to linkList
10097 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10098 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10099 NLink link[] = { *linkIt[0], *linkIt[1] };
10100 if ( linkSet.find( link[0] ) == linkSet.end() )
10103 // by links, find faces in the face sets,
10104 // and find indices of link nodes in the found faces;
10105 // in a face set, there is only one or no face sharing a link
10106 // ---------------------------------------------------------------
10108 const SMDS_MeshElement* face[] = { 0, 0 };
10109 list<const SMDS_MeshNode*> notLinkNodes[2];
10110 //bool reverse[] = { false, false }; // order of notLinkNodes
10112 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10114 const SMDS_MeshNode* n1 = link[iSide].first;
10115 const SMDS_MeshNode* n2 = link[iSide].second;
10116 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10117 set< const SMDS_MeshElement* > facesOfNode1;
10118 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10120 // during a loop of the first node, we find all faces around n1,
10121 // during a loop of the second node, we find one face sharing both n1 and n2
10122 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10123 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10124 while ( fIt->more() ) { // loop on faces sharing a node
10125 const SMDS_MeshElement* f = fIt->next();
10126 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10127 ! facesOfNode1.insert( f ).second ) // f encounters twice
10129 if ( face[ iSide ] ) {
10130 MESSAGE( "2 faces per link " );
10131 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10134 faceSet->erase( f );
10136 // get not link nodes
10137 int nbN = f->NbNodes();
10138 if ( f->IsQuadratic() )
10140 nbNodes[ iSide ] = nbN;
10141 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10142 int i1 = f->GetNodeIndex( n1 );
10143 int i2 = f->GetNodeIndex( n2 );
10144 int iEnd = nbN, iBeg = -1, iDelta = 1;
10145 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10147 std::swap( iEnd, iBeg ); iDelta = -1;
10152 if ( i == iEnd ) i = iBeg + iDelta;
10153 if ( i == i1 ) break;
10154 nodes.push_back ( f->GetNode( i ) );
10160 // check similarity of elements of the sides
10161 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10162 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10163 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10164 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10167 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10171 // set nodes to merge
10172 // -------------------
10174 if ( face[0] && face[1] ) {
10175 if ( nbNodes[0] != nbNodes[1] ) {
10176 MESSAGE("Diff nb of face nodes");
10177 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10179 #ifdef DEBUG_MATCHING_NODES
10180 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10181 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10182 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10184 int nbN = nbNodes[0];
10186 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10187 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10188 for ( int i = 0 ; i < nbN - 2; ++i ) {
10189 #ifdef DEBUG_MATCHING_NODES
10190 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10192 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10196 // add other links of the face 1 to linkList
10197 // -----------------------------------------
10199 const SMDS_MeshElement* f0 = face[0];
10200 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10201 for ( int i = 0; i < nbN; i++ )
10203 const SMDS_MeshNode* n2 = f0->GetNode( i );
10204 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10205 linkSet.insert( SMESH_TLink( n1, n2 ));
10206 if ( !iter_isnew.second ) { // already in a set: no need to process
10207 linkSet.erase( iter_isnew.first );
10209 else // new in set == encountered for the first time: add
10211 #ifdef DEBUG_MATCHING_NODES
10212 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10213 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10215 linkList[0].push_back ( NLink( n1, n2 ));
10216 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10221 } // loop on link lists
10226 //================================================================================
10228 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10229 \param theElems - the list of elements (edges or faces) to be replicated
10230 The nodes for duplication could be found from these elements
10231 \param theNodesNot - list of nodes to NOT replicate
10232 \param theAffectedElems - the list of elements (cells and edges) to which the
10233 replicated nodes should be associated to.
10234 \return TRUE if operation has been completed successfully, FALSE otherwise
10236 //================================================================================
10238 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10239 const TIDSortedElemSet& theNodesNot,
10240 const TIDSortedElemSet& theAffectedElems )
10242 myLastCreatedElems.Clear();
10243 myLastCreatedNodes.Clear();
10245 if ( theElems.size() == 0 )
10248 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10253 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10254 // duplicate elements and nodes
10255 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10256 // replce nodes by duplications
10257 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10261 //================================================================================
10263 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10264 \param theMeshDS - mesh instance
10265 \param theElems - the elements replicated or modified (nodes should be changed)
10266 \param theNodesNot - nodes to NOT replicate
10267 \param theNodeNodeMap - relation of old node to new created node
10268 \param theIsDoubleElem - flag os to replicate element or modify
10269 \return TRUE if operation has been completed successfully, FALSE otherwise
10271 //================================================================================
10273 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10274 const TIDSortedElemSet& theElems,
10275 const TIDSortedElemSet& theNodesNot,
10276 std::map< const SMDS_MeshNode*,
10277 const SMDS_MeshNode* >& theNodeNodeMap,
10278 const bool theIsDoubleElem )
10280 MESSAGE("doubleNodes");
10281 // iterate on through element and duplicate them (by nodes duplication)
10283 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10284 for ( ; elemItr != theElems.end(); ++elemItr )
10286 const SMDS_MeshElement* anElem = *elemItr;
10290 bool isDuplicate = false;
10291 // duplicate nodes to duplicate element
10292 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10293 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10295 while ( anIter->more() )
10298 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10299 SMDS_MeshNode* aNewNode = aCurrNode;
10300 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10301 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10302 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10305 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10306 theNodeNodeMap[ aCurrNode ] = aNewNode;
10307 myLastCreatedNodes.Append( aNewNode );
10309 isDuplicate |= (aCurrNode != aNewNode);
10310 newNodes[ ind++ ] = aNewNode;
10312 if ( !isDuplicate )
10315 if ( theIsDoubleElem )
10316 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10319 MESSAGE("ChangeElementNodes");
10320 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10327 //================================================================================
10329 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10330 \param theNodes - identifiers of nodes to be doubled
10331 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10332 nodes. If list of element identifiers is empty then nodes are doubled but
10333 they not assigned to elements
10334 \return TRUE if operation has been completed successfully, FALSE otherwise
10336 //================================================================================
10338 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10339 const std::list< int >& theListOfModifiedElems )
10341 MESSAGE("DoubleNodes");
10342 myLastCreatedElems.Clear();
10343 myLastCreatedNodes.Clear();
10345 if ( theListOfNodes.size() == 0 )
10348 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10352 // iterate through nodes and duplicate them
10354 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10356 std::list< int >::const_iterator aNodeIter;
10357 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10359 int aCurr = *aNodeIter;
10360 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10366 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10369 anOldNodeToNewNode[ aNode ] = aNewNode;
10370 myLastCreatedNodes.Append( aNewNode );
10374 // Create map of new nodes for modified elements
10376 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10378 std::list< int >::const_iterator anElemIter;
10379 for ( anElemIter = theListOfModifiedElems.begin();
10380 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10382 int aCurr = *anElemIter;
10383 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10387 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10389 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10391 while ( anIter->more() )
10393 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10394 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10396 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10397 aNodeArr[ ind++ ] = aNewNode;
10400 aNodeArr[ ind++ ] = aCurrNode;
10402 anElemToNodes[ anElem ] = aNodeArr;
10405 // Change nodes of elements
10407 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10408 anElemToNodesIter = anElemToNodes.begin();
10409 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10411 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10412 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10415 MESSAGE("ChangeElementNodes");
10416 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10425 //================================================================================
10427 \brief Check if element located inside shape
10428 \return TRUE if IN or ON shape, FALSE otherwise
10430 //================================================================================
10432 template<class Classifier>
10433 bool isInside(const SMDS_MeshElement* theElem,
10434 Classifier& theClassifier,
10435 const double theTol)
10437 gp_XYZ centerXYZ (0, 0, 0);
10438 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10439 while (aNodeItr->more())
10440 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10442 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10443 theClassifier.Perform(aPnt, theTol);
10444 TopAbs_State aState = theClassifier.State();
10445 return (aState == TopAbs_IN || aState == TopAbs_ON );
10448 //================================================================================
10450 * \brief Classifier of the 3D point on the TopoDS_Face
10451 * with interaface suitable for isInside()
10453 //================================================================================
10455 struct _FaceClassifier
10457 Extrema_ExtPS _extremum;
10458 BRepAdaptor_Surface _surface;
10459 TopAbs_State _state;
10461 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10463 _extremum.Initialize( _surface,
10464 _surface.FirstUParameter(), _surface.LastUParameter(),
10465 _surface.FirstVParameter(), _surface.LastVParameter(),
10466 _surface.Tolerance(), _surface.Tolerance() );
10468 void Perform(const gp_Pnt& aPnt, double theTol)
10470 _state = TopAbs_OUT;
10471 _extremum.Perform(aPnt);
10472 if ( _extremum.IsDone() )
10473 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10474 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10475 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10477 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10480 TopAbs_State State() const
10487 //================================================================================
10489 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10490 \param theElems - group of of elements (edges or faces) to be replicated
10491 \param theNodesNot - group of nodes not to replicate
10492 \param theShape - shape to detect affected elements (element which geometric center
10493 located on or inside shape).
10494 The replicated nodes should be associated to affected elements.
10495 \return TRUE if operation has been completed successfully, FALSE otherwise
10497 //================================================================================
10499 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10500 const TIDSortedElemSet& theNodesNot,
10501 const TopoDS_Shape& theShape )
10503 if ( theShape.IsNull() )
10506 const double aTol = Precision::Confusion();
10507 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10508 auto_ptr<_FaceClassifier> aFaceClassifier;
10509 if ( theShape.ShapeType() == TopAbs_SOLID )
10511 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10512 bsc3d->PerformInfinitePoint(aTol);
10514 else if (theShape.ShapeType() == TopAbs_FACE )
10516 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10519 // iterates on indicated elements and get elements by back references from their nodes
10520 TIDSortedElemSet anAffected;
10521 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10522 for ( ; elemItr != theElems.end(); ++elemItr )
10524 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10528 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10529 while ( nodeItr->more() )
10531 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10532 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10534 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10535 while ( backElemItr->more() )
10537 const SMDS_MeshElement* curElem = backElemItr->next();
10538 if ( curElem && theElems.find(curElem) == theElems.end() &&
10540 isInside( curElem, *bsc3d, aTol ) :
10541 isInside( curElem, *aFaceClassifier, aTol )))
10542 anAffected.insert( curElem );
10546 return DoubleNodes( theElems, theNodesNot, anAffected );
10550 * \brief compute an oriented angle between two planes defined by four points.
10551 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10552 * @param p0 base of the rotation axe
10553 * @param p1 extremity of the rotation axe
10554 * @param g1 belongs to the first plane
10555 * @param g2 belongs to the second plane
10557 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10559 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10560 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10561 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10562 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10563 gp_Vec vref(p0, p1);
10566 gp_Vec n1 = vref.Crossed(v1);
10567 gp_Vec n2 = vref.Crossed(v2);
10568 return n2.AngleWithRef(n1, vref);
10572 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10573 * The list of groups must describe a partition of the mesh volumes.
10574 * The nodes of the internal faces at the boundaries of the groups are doubled.
10575 * In option, the internal faces are replaced by flat elements.
10576 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10577 * The flat elements are stored in groups of volumes.
10578 * @param theElems - list of groups of volumes, where a group of volume is a set of
10579 * SMDS_MeshElements sorted by Id.
10580 * @param createJointElems - if TRUE, create the elements
10581 * @return TRUE if operation has been completed successfully, FALSE otherwise
10583 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10584 bool createJointElems)
10586 MESSAGE("----------------------------------------------");
10587 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10588 MESSAGE("----------------------------------------------");
10590 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10591 meshDS->BuildDownWardConnectivity(true);
10593 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10595 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10596 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10597 // build the list of nodes shared by 2 or more domains, with their domain indexes
10599 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10600 std::map<int,int>celldom; // cell vtkId --> domain
10601 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10602 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10603 faceDomains.clear();
10605 cellDomains.clear();
10606 nodeDomains.clear();
10607 std::map<int,int> emptyMap;
10608 std::set<int> emptySet;
10611 for (int idom = 0; idom < theElems.size(); idom++)
10614 // --- build a map (face to duplicate --> volume to modify)
10615 // with all the faces shared by 2 domains (group of elements)
10616 // and corresponding volume of this domain, for each shared face.
10617 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10619 //MESSAGE("Domain " << idom);
10620 const TIDSortedElemSet& domain = theElems[idom];
10621 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10622 for (; elemItr != domain.end(); ++elemItr)
10624 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10627 int vtkId = anElem->getVtkId();
10628 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10629 int neighborsVtkIds[NBMAXNEIGHBORS];
10630 int downIds[NBMAXNEIGHBORS];
10631 unsigned char downTypes[NBMAXNEIGHBORS];
10632 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10633 for (int n = 0; n < nbNeighbors; n++)
10635 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10636 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10637 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10639 DownIdType face(downIds[n], downTypes[n]);
10640 if (!faceDomains.count(face))
10641 faceDomains[face] = emptyMap; // create an empty entry for face
10642 if (!faceDomains[face].count(idom))
10644 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10645 celldom[vtkId] = idom;
10646 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10653 //MESSAGE("Number of shared faces " << faceDomains.size());
10654 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10656 // --- explore the shared faces domain by domain,
10657 // explore the nodes of the face and see if they belong to a cell in the domain,
10658 // which has only a node or an edge on the border (not a shared face)
10660 for (int idomain = 0; idomain < theElems.size(); idomain++)
10662 //MESSAGE("Domain " << idomain);
10663 const TIDSortedElemSet& domain = theElems[idomain];
10664 itface = faceDomains.begin();
10665 for (; itface != faceDomains.end(); ++itface)
10667 std::map<int, int> domvol = itface->second;
10668 if (!domvol.count(idomain))
10670 DownIdType face = itface->first;
10671 //MESSAGE(" --- face " << face.cellId);
10672 std::set<int> oldNodes;
10674 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10675 std::set<int>::iterator itn = oldNodes.begin();
10676 for (; itn != oldNodes.end(); ++itn)
10679 //MESSAGE(" node " << oldId);
10680 std::set<int> cells;
10682 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10683 for (int i=0; i<l.ncells; i++)
10685 int vtkId = l.cells[i];
10686 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10687 if (!domain.count(anElem))
10689 int vtkType = grid->GetCellType(vtkId);
10690 int downId = grid->CellIdToDownId(vtkId);
10693 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10694 continue; // not OK at this stage of the algorithm:
10695 //no cells created after BuildDownWardConnectivity
10697 DownIdType aCell(downId, vtkType);
10698 if (celldom.count(vtkId))
10700 cellDomains[aCell][idomain] = vtkId;
10701 celldom[vtkId] = idomain;
10702 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10708 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10709 // for each shared face, get the nodes
10710 // for each node, for each domain of the face, create a clone of the node
10712 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10713 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10714 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10716 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10717 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10718 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10720 for (int idomain = 0; idomain < theElems.size(); idomain++)
10722 itface = faceDomains.begin();
10723 for (; itface != faceDomains.end(); ++itface)
10725 std::map<int, int> domvol = itface->second;
10726 if (!domvol.count(idomain))
10728 DownIdType face = itface->first;
10729 //MESSAGE(" --- face " << face.cellId);
10730 std::set<int> oldNodes;
10732 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10733 bool isMultipleDetected = false;
10734 std::set<int>::iterator itn = oldNodes.begin();
10735 for (; itn != oldNodes.end(); ++itn)
10738 //MESSAGE(" node " << oldId);
10739 if (!nodeDomains.count(oldId))
10740 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10741 if (nodeDomains[oldId].empty())
10742 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10743 std::map<int, int>::iterator itdom = domvol.begin();
10744 for (; itdom != domvol.end(); ++itdom)
10746 int idom = itdom->first;
10747 //MESSAGE(" domain " << idom);
10748 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10750 if (nodeDomains[oldId].size() >= 2) // a multiple node
10752 vector<int> orderedDoms;
10753 //MESSAGE("multiple node " << oldId);
10754 isMultipleDetected =true;
10755 if (mutipleNodes.count(oldId))
10756 orderedDoms = mutipleNodes[oldId];
10759 map<int,int>::iterator it = nodeDomains[oldId].begin();
10760 for (; it != nodeDomains[oldId].end(); ++it)
10761 orderedDoms.push_back(it->first);
10763 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10764 //stringstream txt;
10765 //for (int i=0; i<orderedDoms.size(); i++)
10766 // txt << orderedDoms[i] << " ";
10767 //MESSAGE("orderedDoms " << txt.str());
10768 mutipleNodes[oldId] = orderedDoms;
10770 double *coords = grid->GetPoint(oldId);
10771 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10772 int newId = newNode->getVtkId();
10773 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10774 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
10776 if (nodeDomains[oldId].size() >= 3)
10778 //MESSAGE("confirm multiple node " << oldId);
10779 isMultipleDetected =true;
10783 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
10785 //MESSAGE("multiple Nodes detected on a shared face");
10786 int downId = itface->first.cellId;
10787 unsigned char cellType = itface->first.cellType;
10788 // --- shared edge or shared face ?
10789 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10792 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10793 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10794 if (mutipleNodes.count(nodes[i]))
10795 if (!mutipleNodesToFace.count(nodes[i]))
10796 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10798 else // shared face (between two volumes)
10800 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10801 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10802 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10803 for (int ie =0; ie < nbEdges; ie++)
10806 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10807 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10809 vector<int> vn0 = mutipleNodes[nodes[0]];
10810 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10811 sort( vn0.begin(), vn0.end() );
10812 sort( vn1.begin(), vn1.end() );
10815 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10816 double *coords = grid->GetPoint(nodes[0]);
10817 gp_Pnt p0(coords[0], coords[1], coords[2]);
10818 coords = grid->GetPoint(nodes[nbNodes - 1]);
10819 gp_Pnt p1(coords[0], coords[1], coords[2]);
10821 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10822 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10823 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10824 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10825 for (int id=0; id < vn0.size(); id++)
10827 int idom = vn0[id];
10828 for (int ivol=0; ivol<nbvol; ivol++)
10830 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10831 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10832 if (theElems[idom].count(elem))
10834 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10835 domvol[idom] = svol;
10836 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10838 vtkIdType npts = 0;
10839 vtkIdType* pts = 0;
10840 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10841 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10844 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10845 angleDom[idom] = 0;
10849 gp_Pnt g(values[0], values[1], values[2]);
10850 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10851 //MESSAGE(" angle=" << angleDom[idom]);
10857 map<double, int> sortedDom; // sort domains by angle
10858 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10859 sortedDom[ia->second] = ia->first;
10860 vector<int> vnodes;
10862 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10864 vdom.push_back(ib->second);
10865 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10867 for (int ino = 0; ino < nbNodes; ino++)
10868 vnodes.push_back(nodes[ino]);
10869 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10878 // --- iterate on shared faces (volumes to modify, face to extrude)
10879 // get node id's of the face (id SMDS = id VTK)
10880 // create flat element with old and new nodes if requested
10882 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10883 // (domain1 X domain2) = domain1 + MAXINT*domain2
10885 std::map<int, std::map<long,int> > nodeQuadDomains;
10886 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10888 if (createJointElems)
10890 itface = faceDomains.begin();
10891 for (; itface != faceDomains.end(); ++itface)
10893 DownIdType face = itface->first;
10894 std::set<int> oldNodes;
10895 std::set<int>::iterator itn;
10897 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10899 std::map<int, int> domvol = itface->second;
10900 std::map<int, int>::iterator itdom = domvol.begin();
10901 int dom1 = itdom->first;
10902 int vtkVolId = itdom->second;
10904 int dom2 = itdom->first;
10905 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10907 stringstream grpname;
10910 grpname << dom1 << "_" << dom2;
10912 grpname << dom2 << "_" << dom1;
10914 string namegrp = grpname.str();
10915 if (!mapOfJunctionGroups.count(namegrp))
10916 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10917 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10919 sgrp->Add(vol->GetID());
10923 // --- create volumes on multiple domain intersection if requested
10924 // iterate on mutipleNodesToFace
10925 // iterate on edgesMultiDomains
10927 if (createJointElems)
10929 // --- iterate on mutipleNodesToFace
10931 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
10932 for (; itn != mutipleNodesToFace.end(); ++itn)
10934 int node = itn->first;
10935 vector<int> orderDom = itn->second;
10936 vector<vtkIdType> orderedNodes;
10937 for (int idom = 0; idom <orderDom.size(); idom++)
10938 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
10939 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
10941 stringstream grpname;
10943 grpname << 0 << "_" << 0;
10945 string namegrp = grpname.str();
10946 if (!mapOfJunctionGroups.count(namegrp))
10947 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
10948 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10950 sgrp->Add(face->GetID());
10953 // --- iterate on edgesMultiDomains
10955 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
10956 for (; ite != edgesMultiDomains.end(); ++ite)
10958 vector<int> nodes = ite->first;
10959 vector<int> orderDom = ite->second;
10960 vector<vtkIdType> orderedNodes;
10961 if (nodes.size() == 2)
10963 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
10964 for (int ino=0; ino < nodes.size(); ino++)
10965 if (orderDom.size() == 3)
10966 for (int idom = 0; idom <orderDom.size(); idom++)
10967 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10969 for (int idom = orderDom.size()-1; idom >=0; idom--)
10970 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10971 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
10973 stringstream grpname;
10975 grpname << 0 << "_" << 0;
10977 string namegrp = grpname.str();
10978 if (!mapOfJunctionGroups.count(namegrp))
10979 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10980 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10982 sgrp->Add(vol->GetID());
10986 MESSAGE("Quadratic multiple joints not implemented");
10987 // TODO quadratic nodes
10992 // --- list the explicit faces and edges of the mesh that need to be modified,
10993 // i.e. faces and edges built with one or more duplicated nodes.
10994 // associate these faces or edges to their corresponding domain.
10995 // only the first domain found is kept when a face or edge is shared
10997 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
10998 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
10999 faceOrEdgeDom.clear();
11002 for (int idomain = 0; idomain < theElems.size(); idomain++)
11004 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11005 for (; itnod != nodeDomains.end(); ++itnod)
11007 int oldId = itnod->first;
11008 //MESSAGE(" node " << oldId);
11009 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11010 for (int i = 0; i < l.ncells; i++)
11012 int vtkId = l.cells[i];
11013 int vtkType = grid->GetCellType(vtkId);
11014 int downId = grid->CellIdToDownId(vtkId);
11016 continue; // new cells: not to be modified
11017 DownIdType aCell(downId, vtkType);
11018 int volParents[1000];
11019 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11020 for (int j = 0; j < nbvol; j++)
11021 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11022 if (!feDom.count(vtkId))
11024 feDom[vtkId] = idomain;
11025 faceOrEdgeDom[aCell] = emptyMap;
11026 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11027 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11028 // << " type " << vtkType << " downId " << downId);
11034 // --- iterate on shared faces (volumes to modify, face to extrude)
11035 // get node id's of the face
11036 // replace old nodes by new nodes in volumes, and update inverse connectivity
11038 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11039 for (int m=0; m<3; m++)
11041 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11042 itface = (*amap).begin();
11043 for (; itface != (*amap).end(); ++itface)
11045 DownIdType face = itface->first;
11046 std::set<int> oldNodes;
11047 std::set<int>::iterator itn;
11049 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11050 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11051 std::map<int, int> localClonedNodeIds;
11053 std::map<int, int> domvol = itface->second;
11054 std::map<int, int>::iterator itdom = domvol.begin();
11055 for (; itdom != domvol.end(); ++itdom)
11057 int idom = itdom->first;
11058 int vtkVolId = itdom->second;
11059 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11060 localClonedNodeIds.clear();
11061 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11064 if (nodeDomains[oldId].count(idom))
11066 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11067 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11070 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11075 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11076 grid->BuildLinks();
11084 * \brief Double nodes on some external faces and create flat elements.
11085 * Flat elements are mainly used by some types of mechanic calculations.
11087 * Each group of the list must be constituted of faces.
11088 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11089 * @param theElems - list of groups of faces, where a group of faces is a set of
11090 * SMDS_MeshElements sorted by Id.
11091 * @return TRUE if operation has been completed successfully, FALSE otherwise
11093 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11095 MESSAGE("-------------------------------------------------");
11096 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11097 MESSAGE("-------------------------------------------------");
11099 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11101 // --- For each group of faces
11102 // duplicate the nodes, create a flat element based on the face
11103 // replace the nodes of the faces by their clones
11105 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11106 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11107 clonedNodes.clear();
11108 intermediateNodes.clear();
11109 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11110 mapOfJunctionGroups.clear();
11112 for (int idom = 0; idom < theElems.size(); idom++)
11114 const TIDSortedElemSet& domain = theElems[idom];
11115 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11116 for (; elemItr != domain.end(); ++elemItr)
11118 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11119 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11122 // MESSAGE("aFace=" << aFace->GetID());
11123 bool isQuad = aFace->IsQuadratic();
11124 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11126 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11128 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11129 while (nodeIt->more())
11131 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11132 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11134 ln2.push_back(node);
11136 ln0.push_back(node);
11138 const SMDS_MeshNode* clone = 0;
11139 if (!clonedNodes.count(node))
11141 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11142 clonedNodes[node] = clone;
11145 clone = clonedNodes[node];
11148 ln3.push_back(clone);
11150 ln1.push_back(clone);
11152 const SMDS_MeshNode* inter = 0;
11153 if (isQuad && (!isMedium))
11155 if (!intermediateNodes.count(node))
11157 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11158 intermediateNodes[node] = inter;
11161 inter = intermediateNodes[node];
11162 ln4.push_back(inter);
11166 // --- extrude the face
11168 vector<const SMDS_MeshNode*> ln;
11169 SMDS_MeshVolume* vol = 0;
11170 vtkIdType aType = aFace->GetVtkType();
11174 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11175 // MESSAGE("vol prism " << vol->GetID());
11176 ln.push_back(ln1[0]);
11177 ln.push_back(ln1[1]);
11178 ln.push_back(ln1[2]);
11181 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11182 // MESSAGE("vol hexa " << vol->GetID());
11183 ln.push_back(ln1[0]);
11184 ln.push_back(ln1[1]);
11185 ln.push_back(ln1[2]);
11186 ln.push_back(ln1[3]);
11188 case VTK_QUADRATIC_TRIANGLE:
11189 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11190 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11191 // MESSAGE("vol quad prism " << vol->GetID());
11192 ln.push_back(ln1[0]);
11193 ln.push_back(ln1[1]);
11194 ln.push_back(ln1[2]);
11195 ln.push_back(ln3[0]);
11196 ln.push_back(ln3[1]);
11197 ln.push_back(ln3[2]);
11199 case VTK_QUADRATIC_QUAD:
11200 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11201 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11202 // ln4[0], ln4[1], ln4[2], ln4[3]);
11203 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11204 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11205 ln4[0], ln4[1], ln4[2], ln4[3]);
11206 // MESSAGE("vol quad hexa " << vol->GetID());
11207 ln.push_back(ln1[0]);
11208 ln.push_back(ln1[1]);
11209 ln.push_back(ln1[2]);
11210 ln.push_back(ln1[3]);
11211 ln.push_back(ln3[0]);
11212 ln.push_back(ln3[1]);
11213 ln.push_back(ln3[2]);
11214 ln.push_back(ln3[3]);
11224 stringstream grpname;
11228 string namegrp = grpname.str();
11229 if (!mapOfJunctionGroups.count(namegrp))
11230 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11231 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11233 sgrp->Add(vol->GetID());
11236 // --- modify the face
11238 aFace->ChangeNodes(&ln[0], ln.size());
11244 //================================================================================
11246 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11247 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11248 * \return TRUE if operation has been completed successfully, FALSE otherwise
11250 //================================================================================
11252 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11254 // iterates on volume elements and detect all free faces on them
11255 SMESHDS_Mesh* aMesh = GetMeshDS();
11258 //bool res = false;
11259 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11260 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11263 const SMDS_MeshVolume* volume = vIt->next();
11264 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11265 vTool.SetExternalNormal();
11266 //const bool isPoly = volume->IsPoly();
11267 const int iQuad = volume->IsQuadratic();
11268 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11270 if (!vTool.IsFreeFace(iface))
11273 vector<const SMDS_MeshNode *> nodes;
11274 int nbFaceNodes = vTool.NbFaceNodes(iface);
11275 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11277 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11278 nodes.push_back(faceNodes[inode]);
11279 if (iQuad) { // add medium nodes
11280 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11281 nodes.push_back(faceNodes[inode]);
11282 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11283 nodes.push_back(faceNodes[8]);
11285 // add new face based on volume nodes
11286 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11288 continue; // face already exsist
11290 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11294 return ( nbFree==(nbExisted+nbCreated) );
11299 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11301 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11303 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11306 //================================================================================
11308 * \brief Creates missing boundary elements
11309 * \param elements - elements whose boundary is to be checked
11310 * \param dimension - defines type of boundary elements to create
11311 * \param group - a group to store created boundary elements in
11312 * \param targetMesh - a mesh to store created boundary elements in
11313 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11314 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11315 * boundary elements will be copied into the targetMesh
11316 * \param toAddExistingBondary - if true, not only new but also pre-existing
11317 * boundary elements will be added into the new group
11318 * \param aroundElements - if true, elements will be created on boundary of given
11319 * elements else, on boundary of the whole mesh.
11320 * \return nb of added boundary elements
11322 //================================================================================
11324 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11325 Bnd_Dimension dimension,
11326 SMESH_Group* group/*=0*/,
11327 SMESH_Mesh* targetMesh/*=0*/,
11328 bool toCopyElements/*=false*/,
11329 bool toCopyExistingBoundary/*=false*/,
11330 bool toAddExistingBondary/*= false*/,
11331 bool aroundElements/*= false*/)
11333 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11334 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11335 // hope that all elements are of the same type, do not check them all
11336 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11337 throw SALOME_Exception(LOCALIZED("wrong element type"));
11340 toCopyElements = toCopyExistingBoundary = false;
11342 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11343 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11344 int nbAddedBnd = 0;
11346 // editor adding present bnd elements and optionally holding elements to add to the group
11347 SMESH_MeshEditor* presentEditor;
11348 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11349 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11351 SMESH_MesherHelper helper( *myMesh );
11352 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11353 SMDS_VolumeTool vTool;
11354 TIDSortedElemSet avoidSet;
11355 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11358 typedef vector<const SMDS_MeshNode*> TConnectivity;
11360 SMDS_ElemIteratorPtr eIt;
11361 if (elements.empty())
11362 eIt = aMesh->elementsIterator(elemType);
11364 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11366 while (eIt->more())
11368 const SMDS_MeshElement* elem = eIt->next();
11369 const int iQuad = elem->IsQuadratic();
11371 // ------------------------------------------------------------------------------------
11372 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11373 // ------------------------------------------------------------------------------------
11374 vector<const SMDS_MeshElement*> presentBndElems;
11375 vector<TConnectivity> missingBndElems;
11376 TConnectivity nodes;
11377 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11379 vTool.SetExternalNormal();
11380 const SMDS_MeshElement* otherVol = 0;
11381 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11383 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11384 ( !aroundElements || elements.count( otherVol )))
11386 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11387 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11388 if ( missType == SMDSAbs_Edge ) // boundary edges
11390 nodes.resize( 2+iQuad );
11391 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11393 for ( int j = 0; j < nodes.size(); ++j )
11395 if ( const SMDS_MeshElement* edge =
11396 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11397 presentBndElems.push_back( edge );
11399 missingBndElems.push_back( nodes );
11402 else // boundary face
11405 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11406 nodes.push_back( nn[inode] );
11407 if (iQuad) // add medium nodes
11408 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11409 nodes.push_back( nn[inode] );
11410 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11412 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11414 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11415 SMDSAbs_Face, /*noMedium=*/false ))
11416 presentBndElems.push_back( f );
11418 missingBndElems.push_back( nodes );
11420 if ( targetMesh != myMesh )
11422 // add 1D elements on face boundary to be added to a new mesh
11423 const SMDS_MeshElement* edge;
11424 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11427 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11429 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11430 if ( edge && avoidSet.insert( edge ).second )
11431 presentBndElems.push_back( edge );
11437 else // elem is a face ------------------------------------------
11439 avoidSet.clear(), avoidSet.insert( elem );
11440 int nbNodes = elem->NbCornerNodes();
11441 nodes.resize( 2 /*+ iQuad*/);
11442 for ( int i = 0; i < nbNodes; i++ )
11444 nodes[0] = elem->GetNode(i);
11445 nodes[1] = elem->GetNode((i+1)%nbNodes);
11446 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11447 continue; // not free link
11450 //nodes[2] = elem->GetNode( i + nbNodes );
11451 if ( const SMDS_MeshElement* edge =
11452 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11453 presentBndElems.push_back( edge );
11455 missingBndElems.push_back( nodes );
11459 // ---------------------------------
11460 // 2. Add missing boundary elements
11461 // ---------------------------------
11462 if ( targetMesh != myMesh )
11463 // instead of making a map of nodes in this mesh and targetMesh,
11464 // we create nodes with same IDs.
11465 for ( int i = 0; i < missingBndElems.size(); ++i )
11467 TConnectivity& srcNodes = missingBndElems[i];
11468 TConnectivity nodes( srcNodes.size() );
11469 for ( inode = 0; inode < nodes.size(); ++inode )
11470 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11471 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11473 /*noMedium=*/false))
11475 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11479 for ( int i = 0; i < missingBndElems.size(); ++i )
11481 TConnectivity& nodes = missingBndElems[i];
11482 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11484 /*noMedium=*/false))
11486 SMDS_MeshElement* elem =
11487 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11490 // try to set a new element to a shape
11491 if ( myMesh->HasShapeToMesh() )
11494 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11495 const int nbN = nodes.size() / (iQuad+1 );
11496 for ( inode = 0; inode < nbN && ok; ++inode )
11498 pair<int, TopAbs_ShapeEnum> i_stype =
11499 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11500 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11501 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11503 if ( ok && mediumShapes.size() > 1 )
11505 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11506 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11507 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11509 if (( ok = ( stype_i->first != stype_i_0.first )))
11510 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11511 aMesh->IndexToShape( stype_i_0.second ));
11514 if ( ok && mediumShapes.begin()->first == missShapeType )
11515 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11519 // ----------------------------------
11520 // 3. Copy present boundary elements
11521 // ----------------------------------
11522 if ( toCopyExistingBoundary )
11523 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11525 const SMDS_MeshElement* e = presentBndElems[i];
11526 TConnectivity nodes( e->NbNodes() );
11527 for ( inode = 0; inode < nodes.size(); ++inode )
11528 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11529 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11531 else // store present elements to add them to a group
11532 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11534 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11537 } // loop on given elements
11539 // ---------------------------------------------
11540 // 4. Fill group with boundary elements
11541 // ---------------------------------------------
11544 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11545 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11546 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11548 tgtEditor.myLastCreatedElems.Clear();
11549 tgtEditor2.myLastCreatedElems.Clear();
11551 // -----------------------
11552 // 5. Copy given elements
11553 // -----------------------
11554 if ( toCopyElements && targetMesh != myMesh )
11556 if (elements.empty())
11557 eIt = aMesh->elementsIterator(elemType);
11559 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11560 while (eIt->more())
11562 const SMDS_MeshElement* elem = eIt->next();
11563 TConnectivity nodes( elem->NbNodes() );
11564 for ( inode = 0; inode < nodes.size(); ++inode )
11565 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11566 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11568 tgtEditor.myLastCreatedElems.Clear();