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 --->
3754 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 // ---> 2d order Pentahedron with 15 nodes
3772 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3773 // iBeforeSame is same too
3780 // iAfterSame is same too
3790 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3791 prevNod[n4], prevNod[n5], nextNod[n5],
3792 prevNod[n12], midlNod[n2], nextNod[n12],
3793 prevNod[n45], midlNod[n5], nextNod[n45],
3794 prevNod[n14], prevNod[n25], nextNod[n25]);
3798 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3800 if( nbSame == 0 && nbDouble == 9 ) {
3801 // ---> tri-quadratic hexahedron with 27 nodes
3802 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3803 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3804 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3805 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3806 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3807 prevNod[8], // bottom center
3808 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3809 nextNod[8], // top center
3810 midlNod[8]);// elem center
3818 case SMDSEntity_Polygon: { // sweep POLYGON
3820 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3821 // ---> hexagonal prism
3822 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3823 prevNod[3], prevNod[4], prevNod[5],
3824 nextNod[0], nextNod[1], nextNod[2],
3825 nextNod[3], nextNod[4], nextNod[5]);
3834 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3836 if ( baseType != SMDSEntity_Polygon )
3838 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3839 SMDS_MeshCell::applyInterlace( ind, prevNod );
3840 SMDS_MeshCell::applyInterlace( ind, nextNod );
3841 SMDS_MeshCell::applyInterlace( ind, midlNod );
3842 SMDS_MeshCell::applyInterlace( ind, itNN );
3843 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3844 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3846 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3847 vector<int> quantities (nbNodes + 2);
3848 polyedre_nodes.clear();
3852 for (int inode = 0; inode < nbNodes; inode++)
3853 polyedre_nodes.push_back( prevNod[inode] );
3854 quantities.push_back( nbNodes );
3857 polyedre_nodes.push_back( nextNod[0] );
3858 for (int inode = nbNodes; inode-1; --inode )
3859 polyedre_nodes.push_back( nextNod[inode-1] );
3860 quantities.push_back( nbNodes );
3863 for (int iface = 0; iface < nbNodes; iface++)
3865 const int prevNbNodes = polyedre_nodes.size();
3866 int inextface = (iface+1) % nbNodes;
3867 polyedre_nodes.push_back( prevNod[inextface] );
3868 polyedre_nodes.push_back( prevNod[iface] );
3869 if ( prevNod[iface] != nextNod[iface] )
3871 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
3872 polyedre_nodes.push_back( nextNod[iface] );
3874 if ( prevNod[inextface] != nextNod[inextface] )
3876 polyedre_nodes.push_back( nextNod[inextface] );
3877 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
3879 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
3880 if ( nbFaceNodes > 2 )
3881 quantities.push_back( nbFaceNodes );
3882 else // degenerated face
3883 polyedre_nodes.resize( prevNbNodes );
3885 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3889 newElems.push_back( aNewElem );
3890 myLastCreatedElems.Append(aNewElem);
3891 srcElements.Append( elem );
3894 // set new prev nodes
3895 for ( iNode = 0; iNode < nbNodes; iNode++ )
3896 prevNod[ iNode ] = nextNod[ iNode ];
3901 //=======================================================================
3903 * \brief Create 1D and 2D elements around swept elements
3904 * \param mapNewNodes - source nodes and ones generated from them
3905 * \param newElemsMap - source elements and ones generated from them
3906 * \param elemNewNodesMap - nodes generated from each node of each element
3907 * \param elemSet - all swept elements
3908 * \param nbSteps - number of sweeping steps
3909 * \param srcElements - to append elem for each generated element
3911 //=======================================================================
3913 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
3914 TElemOfElemListMap & newElemsMap,
3915 TElemOfVecOfNnlmiMap & elemNewNodesMap,
3916 TIDSortedElemSet& elemSet,
3918 SMESH_SequenceOfElemPtr& srcElements)
3920 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
3921 SMESHDS_Mesh* aMesh = GetMeshDS();
3923 // Find nodes belonging to only one initial element - sweep them to get edges.
3925 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
3926 for ( ; nList != mapNewNodes.end(); nList++ )
3928 const SMDS_MeshNode* node =
3929 static_cast<const SMDS_MeshNode*>( nList->first );
3930 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
3931 int nbInitElems = 0;
3932 const SMDS_MeshElement* el = 0;
3933 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
3934 while ( eIt->more() && nbInitElems < 2 ) {
3936 SMDSAbs_ElementType type = el->GetType();
3937 if ( type == SMDSAbs_Volume || type < highType ) continue;
3938 if ( type > highType ) {
3942 nbInitElems += elemSet.count(el);
3944 if ( nbInitElems < 2 ) {
3945 bool NotCreateEdge = el && el->IsMediumNode(node);
3946 if(!NotCreateEdge) {
3947 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
3948 list<const SMDS_MeshElement*> newEdges;
3949 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
3954 // Make a ceiling for each element ie an equal element of last new nodes.
3955 // Find free links of faces - make edges and sweep them into faces.
3957 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
3958 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
3959 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
3961 const SMDS_MeshElement* elem = itElem->first;
3962 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
3964 if(itElem->second.size()==0) continue;
3966 const bool isQuadratic = elem->IsQuadratic();
3968 if ( elem->GetType() == SMDSAbs_Edge ) {
3969 // create a ceiling edge
3970 if ( !isQuadratic ) {
3971 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3972 vecNewNodes[ 1 ]->second.back())) {
3973 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3974 vecNewNodes[ 1 ]->second.back()));
3975 srcElements.Append( myLastCreatedElems.Last() );
3979 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
3980 vecNewNodes[ 1 ]->second.back(),
3981 vecNewNodes[ 2 ]->second.back())) {
3982 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
3983 vecNewNodes[ 1 ]->second.back(),
3984 vecNewNodes[ 2 ]->second.back()));
3985 srcElements.Append( myLastCreatedElems.Last() );
3989 if ( elem->GetType() != SMDSAbs_Face )
3992 bool hasFreeLinks = false;
3994 TIDSortedElemSet avoidSet;
3995 avoidSet.insert( elem );
3997 set<const SMDS_MeshNode*> aFaceLastNodes;
3998 int iNode, nbNodes = vecNewNodes.size();
3999 if ( !isQuadratic ) {
4000 // loop on the face nodes
4001 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4002 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4003 // look for free links of the face
4004 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4005 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4006 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4007 // check if a link is free
4008 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4009 hasFreeLinks = true;
4010 // make an edge and a ceiling for a new edge
4011 if ( !aMesh->FindEdge( n1, n2 )) {
4012 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4013 srcElements.Append( myLastCreatedElems.Last() );
4015 n1 = vecNewNodes[ iNode ]->second.back();
4016 n2 = vecNewNodes[ iNext ]->second.back();
4017 if ( !aMesh->FindEdge( n1, n2 )) {
4018 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4019 srcElements.Append( myLastCreatedElems.Last() );
4024 else { // elem is quadratic face
4025 int nbn = nbNodes/2;
4026 for ( iNode = 0; iNode < nbn; iNode++ ) {
4027 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4028 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4029 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4030 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4031 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4032 // check if a link is free
4033 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4034 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4035 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4036 hasFreeLinks = true;
4037 // make an edge and a ceiling for a new edge
4039 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4040 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4041 srcElements.Append( myLastCreatedElems.Last() );
4043 n1 = vecNewNodes[ iNode ]->second.back();
4044 n2 = vecNewNodes[ iNext ]->second.back();
4045 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4046 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4047 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4048 srcElements.Append( myLastCreatedElems.Last() );
4052 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4053 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4057 // sweep free links into faces
4059 if ( hasFreeLinks ) {
4060 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4061 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4063 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4064 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4065 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4066 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4068 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4069 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4070 std::advance( v, volNb );
4071 // find indices of free faces of a volume and their source edges
4072 list< int > freeInd;
4073 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4074 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4075 int iF, nbF = vTool.NbFaces();
4076 for ( iF = 0; iF < nbF; iF ++ ) {
4077 if (vTool.IsFreeFace( iF ) &&
4078 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4079 initNodeSet != faceNodeSet) // except an initial face
4081 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4083 freeInd.push_back( iF );
4084 // find source edge of a free face iF
4085 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4086 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4087 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4088 initNodeSet.begin(), initNodeSet.end(),
4089 commonNodes.begin());
4090 if ( (*v)->IsQuadratic() )
4091 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4093 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4095 if ( !srcEdges.back() )
4097 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4098 << iF << " of volume #" << vTool.ID() << endl;
4103 if ( freeInd.empty() )
4106 // create faces for all steps;
4107 // if such a face has been already created by sweep of edge,
4108 // assure that its orientation is OK
4109 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4110 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4111 vTool.SetExternalNormal();
4112 const int nextShift = vTool.IsForward() ? +1 : -1;
4113 list< int >::iterator ind = freeInd.begin();
4114 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4115 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4117 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4118 int nbn = vTool.NbFaceNodes( *ind );
4119 const SMDS_MeshElement * f = 0;
4120 if ( nbn == 3 ) ///// triangle
4122 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4124 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4126 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4128 nodes[ 1 + nextShift ] };
4130 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4132 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4136 else if ( nbn == 4 ) ///// quadrangle
4138 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4140 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4142 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4143 nodes[ 2 ], nodes[ 2+nextShift ] };
4145 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4147 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4148 newOrder[ 2 ], newOrder[ 3 ]));
4151 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4153 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4155 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4157 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4159 nodes[2 + 2*nextShift],
4160 nodes[3 - 2*nextShift],
4162 nodes[3 + 2*nextShift]};
4164 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4166 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4174 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4176 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4177 nodes[1], nodes[3], nodes[5], nodes[7] );
4179 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4181 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4182 nodes[4 - 2*nextShift],
4184 nodes[4 + 2*nextShift],
4186 nodes[5 - 2*nextShift],
4188 nodes[5 + 2*nextShift] };
4190 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4192 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4193 newOrder[ 2 ], newOrder[ 3 ],
4194 newOrder[ 4 ], newOrder[ 5 ],
4195 newOrder[ 6 ], newOrder[ 7 ]));
4198 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4200 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4201 SMDSAbs_Face, /*noMedium=*/false);
4203 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4205 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4206 nodes[4 - 2*nextShift],
4208 nodes[4 + 2*nextShift],
4210 nodes[5 - 2*nextShift],
4212 nodes[5 + 2*nextShift],
4215 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4217 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4218 newOrder[ 2 ], newOrder[ 3 ],
4219 newOrder[ 4 ], newOrder[ 5 ],
4220 newOrder[ 6 ], newOrder[ 7 ],
4224 else //////// polygon
4226 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4227 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4229 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4231 if ( !vTool.IsForward() )
4232 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4234 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4236 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4240 while ( srcElements.Length() < myLastCreatedElems.Length() )
4241 srcElements.Append( *srcEdge );
4243 } // loop on free faces
4245 // go to the next volume
4247 while ( iVol++ < nbVolumesByStep ) v++;
4250 } // loop on volumes of one step
4251 } // sweep free links into faces
4253 // Make a ceiling face with a normal external to a volume
4255 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4257 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4259 lastVol.SetExternalNormal();
4260 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4261 int nbn = lastVol.NbFaceNodes( iF );
4263 if (!hasFreeLinks ||
4264 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4265 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4267 else if ( nbn == 4 )
4269 if (!hasFreeLinks ||
4270 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4271 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4273 else if ( nbn == 6 && isQuadratic )
4275 if (!hasFreeLinks ||
4276 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4277 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4278 nodes[1], nodes[3], nodes[5]));
4280 else if ( nbn == 8 && isQuadratic )
4282 if (!hasFreeLinks ||
4283 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4284 nodes[1], nodes[3], nodes[5], nodes[7]) )
4285 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4286 nodes[1], nodes[3], nodes[5], nodes[7]));
4288 else if ( nbn == 9 && isQuadratic )
4290 if (!hasFreeLinks ||
4291 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4292 SMDSAbs_Face, /*noMedium=*/false) )
4293 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4294 nodes[1], nodes[3], nodes[5], nodes[7],
4298 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4299 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4300 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4303 while ( srcElements.Length() < myLastCreatedElems.Length() )
4304 srcElements.Append( myLastCreatedElems.Last() );
4306 } // loop on swept elements
4309 //=======================================================================
4310 //function : RotationSweep
4312 //=======================================================================
4314 SMESH_MeshEditor::PGroupIDs
4315 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4316 const gp_Ax1& theAxis,
4317 const double theAngle,
4318 const int theNbSteps,
4319 const double theTol,
4320 const bool theMakeGroups,
4321 const bool theMakeWalls)
4323 myLastCreatedElems.Clear();
4324 myLastCreatedNodes.Clear();
4326 // source elements for each generated one
4327 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4329 MESSAGE( "RotationSweep()");
4331 aTrsf.SetRotation( theAxis, theAngle );
4333 aTrsf2.SetRotation( theAxis, theAngle/2. );
4335 gp_Lin aLine( theAxis );
4336 double aSqTol = theTol * theTol;
4338 SMESHDS_Mesh* aMesh = GetMeshDS();
4340 TNodeOfNodeListMap mapNewNodes;
4341 TElemOfVecOfNnlmiMap mapElemNewNodes;
4342 TElemOfElemListMap newElemsMap;
4344 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4345 myMesh->NbFaces(ORDER_QUADRATIC) +
4346 myMesh->NbVolumes(ORDER_QUADRATIC) );
4348 TIDSortedElemSet::iterator itElem;
4349 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4350 const SMDS_MeshElement* elem = *itElem;
4351 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4353 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4354 newNodesItVec.reserve( elem->NbNodes() );
4356 // loop on elem nodes
4357 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4358 while ( itN->more() )
4360 // check if a node has been already sweeped
4361 const SMDS_MeshNode* node = cast2Node( itN->next() );
4363 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4365 aXYZ.Coord( coord[0], coord[1], coord[2] );
4366 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4368 TNodeOfNodeListMapItr nIt =
4369 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4370 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4371 if ( listNewNodes.empty() )
4373 // check if we are to create medium nodes between corner ones
4374 bool needMediumNodes = false;
4375 if ( isQuadraticMesh )
4377 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4378 while (it->more() && !needMediumNodes )
4380 const SMDS_MeshElement* invElem = it->next();
4381 if ( invElem != elem && !theElems.count( invElem )) continue;
4382 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4383 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4384 needMediumNodes = true;
4389 const SMDS_MeshNode * newNode = node;
4390 for ( int i = 0; i < theNbSteps; i++ ) {
4392 if ( needMediumNodes ) // create a medium node
4394 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4395 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4396 myLastCreatedNodes.Append(newNode);
4397 srcNodes.Append( node );
4398 listNewNodes.push_back( newNode );
4399 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4402 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4404 // create a corner node
4405 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4406 myLastCreatedNodes.Append(newNode);
4407 srcNodes.Append( node );
4408 listNewNodes.push_back( newNode );
4411 listNewNodes.push_back( newNode );
4412 // if ( needMediumNodes )
4413 // listNewNodes.push_back( newNode );
4417 newNodesItVec.push_back( nIt );
4419 // make new elements
4420 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4424 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4426 PGroupIDs newGroupIDs;
4427 if ( theMakeGroups )
4428 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4434 //=======================================================================
4435 //function : CreateNode
4437 //=======================================================================
4438 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4441 const double tolnode,
4442 SMESH_SequenceOfNode& aNodes)
4444 // myLastCreatedElems.Clear();
4445 // myLastCreatedNodes.Clear();
4448 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4450 // try to search in sequence of existing nodes
4451 // if aNodes.Length()>0 we 'nave to use given sequence
4452 // else - use all nodes of mesh
4453 if(aNodes.Length()>0) {
4455 for(i=1; i<=aNodes.Length(); i++) {
4456 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4457 if(P1.Distance(P2)<tolnode)
4458 return aNodes.Value(i);
4462 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4463 while(itn->more()) {
4464 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4465 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4466 if(P1.Distance(P2)<tolnode)
4471 // create new node and return it
4472 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4473 //myLastCreatedNodes.Append(NewNode);
4478 //=======================================================================
4479 //function : ExtrusionSweep
4481 //=======================================================================
4483 SMESH_MeshEditor::PGroupIDs
4484 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4485 const gp_Vec& theStep,
4486 const int theNbSteps,
4487 TElemOfElemListMap& newElemsMap,
4488 const bool theMakeGroups,
4490 const double theTolerance)
4492 ExtrusParam aParams;
4493 aParams.myDir = gp_Dir(theStep);
4494 aParams.myNodes.Clear();
4495 aParams.mySteps = new TColStd_HSequenceOfReal;
4497 for(i=1; i<=theNbSteps; i++)
4498 aParams.mySteps->Append(theStep.Magnitude());
4501 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4505 //=======================================================================
4506 //function : ExtrusionSweep
4508 //=======================================================================
4510 SMESH_MeshEditor::PGroupIDs
4511 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4512 ExtrusParam& theParams,
4513 TElemOfElemListMap& newElemsMap,
4514 const bool theMakeGroups,
4516 const double theTolerance)
4518 myLastCreatedElems.Clear();
4519 myLastCreatedNodes.Clear();
4521 // source elements for each generated one
4522 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4524 SMESHDS_Mesh* aMesh = GetMeshDS();
4526 int nbsteps = theParams.mySteps->Length();
4528 TNodeOfNodeListMap mapNewNodes;
4529 //TNodeOfNodeVecMap mapNewNodes;
4530 TElemOfVecOfNnlmiMap mapElemNewNodes;
4531 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4533 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4534 myMesh->NbFaces(ORDER_QUADRATIC) +
4535 myMesh->NbVolumes(ORDER_QUADRATIC) );
4537 TIDSortedElemSet::iterator itElem;
4538 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4539 // check element type
4540 const SMDS_MeshElement* elem = *itElem;
4541 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4544 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4545 newNodesItVec.reserve( elem->NbNodes() );
4547 // loop on elem nodes
4548 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4549 while ( itN->more() )
4551 // check if a node has been already sweeped
4552 const SMDS_MeshNode* node = cast2Node( itN->next() );
4553 TNodeOfNodeListMap::iterator nIt =
4554 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4555 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4556 if ( listNewNodes.empty() )
4560 // check if we are to create medium nodes between corner ones
4561 bool needMediumNodes = false;
4562 if ( isQuadraticMesh )
4564 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4565 while (it->more() && !needMediumNodes )
4567 const SMDS_MeshElement* invElem = it->next();
4568 if ( invElem != elem && !theElems.count( invElem )) continue;
4569 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4570 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4571 needMediumNodes = true;
4575 double coord[] = { node->X(), node->Y(), node->Z() };
4576 for ( int i = 0; i < nbsteps; i++ )
4578 if ( needMediumNodes ) // create a medium node
4580 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4581 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4582 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4583 if( theFlags & EXTRUSION_FLAG_SEW ) {
4584 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4585 theTolerance, theParams.myNodes);
4586 listNewNodes.push_back( newNode );
4589 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4590 myLastCreatedNodes.Append(newNode);
4591 srcNodes.Append( node );
4592 listNewNodes.push_back( newNode );
4595 // create a corner node
4596 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4597 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4598 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4599 if( theFlags & EXTRUSION_FLAG_SEW ) {
4600 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4601 theTolerance, theParams.myNodes);
4602 listNewNodes.push_back( newNode );
4605 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4606 myLastCreatedNodes.Append(newNode);
4607 srcNodes.Append( node );
4608 listNewNodes.push_back( newNode );
4612 newNodesItVec.push_back( nIt );
4614 // make new elements
4615 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4618 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4619 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4621 PGroupIDs newGroupIDs;
4622 if ( theMakeGroups )
4623 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4628 //=======================================================================
4629 //function : ExtrusionAlongTrack
4631 //=======================================================================
4632 SMESH_MeshEditor::Extrusion_Error
4633 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4634 SMESH_subMesh* theTrack,
4635 const SMDS_MeshNode* theN1,
4636 const bool theHasAngles,
4637 list<double>& theAngles,
4638 const bool theLinearVariation,
4639 const bool theHasRefPoint,
4640 const gp_Pnt& theRefPoint,
4641 const bool theMakeGroups)
4643 MESSAGE("ExtrusionAlongTrack");
4644 myLastCreatedElems.Clear();
4645 myLastCreatedNodes.Clear();
4648 std::list<double> aPrms;
4649 TIDSortedElemSet::iterator itElem;
4652 TopoDS_Edge aTrackEdge;
4653 TopoDS_Vertex aV1, aV2;
4655 SMDS_ElemIteratorPtr aItE;
4656 SMDS_NodeIteratorPtr aItN;
4657 SMDSAbs_ElementType aTypeE;
4659 TNodeOfNodeListMap mapNewNodes;
4662 aNbE = theElements.size();
4665 return EXTR_NO_ELEMENTS;
4667 // 1.1 Track Pattern
4670 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4672 aItE = pSubMeshDS->GetElements();
4673 while ( aItE->more() ) {
4674 const SMDS_MeshElement* pE = aItE->next();
4675 aTypeE = pE->GetType();
4676 // Pattern must contain links only
4677 if ( aTypeE != SMDSAbs_Edge )
4678 return EXTR_PATH_NOT_EDGE;
4681 list<SMESH_MeshEditor_PathPoint> fullList;
4683 const TopoDS_Shape& aS = theTrack->GetSubShape();
4684 // Sub-shape for the Pattern must be an Edge or Wire
4685 if( aS.ShapeType() == TopAbs_EDGE ) {
4686 aTrackEdge = TopoDS::Edge( aS );
4687 // the Edge must not be degenerated
4688 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4689 return EXTR_BAD_PATH_SHAPE;
4690 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4691 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4692 const SMDS_MeshNode* aN1 = aItN->next();
4693 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4694 const SMDS_MeshNode* aN2 = aItN->next();
4695 // starting node must be aN1 or aN2
4696 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4697 return EXTR_BAD_STARTING_NODE;
4698 aItN = pSubMeshDS->GetNodes();
4699 while ( aItN->more() ) {
4700 const SMDS_MeshNode* pNode = aItN->next();
4701 const SMDS_EdgePosition* pEPos =
4702 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4703 double aT = pEPos->GetUParameter();
4704 aPrms.push_back( aT );
4706 //Extrusion_Error err =
4707 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4708 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4709 list< SMESH_subMesh* > LSM;
4710 TopTools_SequenceOfShape Edges;
4711 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4712 while(itSM->more()) {
4713 SMESH_subMesh* SM = itSM->next();
4715 const TopoDS_Shape& aS = SM->GetSubShape();
4718 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4719 int startNid = theN1->GetID();
4720 TColStd_MapOfInteger UsedNums;
4722 int NbEdges = Edges.Length();
4724 for(; i<=NbEdges; i++) {
4726 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4727 for(; itLSM!=LSM.end(); itLSM++) {
4729 if(UsedNums.Contains(k)) continue;
4730 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4731 SMESH_subMesh* locTrack = *itLSM;
4732 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4733 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4734 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4735 const SMDS_MeshNode* aN1 = aItN->next();
4736 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4737 const SMDS_MeshNode* aN2 = aItN->next();
4738 // starting node must be aN1 or aN2
4739 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4740 // 2. Collect parameters on the track edge
4742 aItN = locMeshDS->GetNodes();
4743 while ( aItN->more() ) {
4744 const SMDS_MeshNode* pNode = aItN->next();
4745 const SMDS_EdgePosition* pEPos =
4746 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4747 double aT = pEPos->GetUParameter();
4748 aPrms.push_back( aT );
4750 list<SMESH_MeshEditor_PathPoint> LPP;
4751 //Extrusion_Error err =
4752 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4753 LLPPs.push_back(LPP);
4755 // update startN for search following egde
4756 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4757 else startNid = aN1->GetID();
4761 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4762 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4763 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4764 for(; itPP!=firstList.end(); itPP++) {
4765 fullList.push_back( *itPP );
4767 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4768 fullList.pop_back();
4770 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4771 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4772 itPP = currList.begin();
4773 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4774 gp_Dir D1 = PP1.Tangent();
4775 gp_Dir D2 = PP2.Tangent();
4776 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4777 (D1.Z()+D2.Z())/2 ) );
4778 PP1.SetTangent(Dnew);
4779 fullList.push_back(PP1);
4781 for(; itPP!=firstList.end(); itPP++) {
4782 fullList.push_back( *itPP );
4784 PP1 = fullList.back();
4785 fullList.pop_back();
4787 // if wire not closed
4788 fullList.push_back(PP1);
4792 return EXTR_BAD_PATH_SHAPE;
4795 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4796 theHasRefPoint, theRefPoint, theMakeGroups);
4800 //=======================================================================
4801 //function : ExtrusionAlongTrack
4803 //=======================================================================
4804 SMESH_MeshEditor::Extrusion_Error
4805 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4806 SMESH_Mesh* theTrack,
4807 const SMDS_MeshNode* theN1,
4808 const bool theHasAngles,
4809 list<double>& theAngles,
4810 const bool theLinearVariation,
4811 const bool theHasRefPoint,
4812 const gp_Pnt& theRefPoint,
4813 const bool theMakeGroups)
4815 myLastCreatedElems.Clear();
4816 myLastCreatedNodes.Clear();
4819 std::list<double> aPrms;
4820 TIDSortedElemSet::iterator itElem;
4823 TopoDS_Edge aTrackEdge;
4824 TopoDS_Vertex aV1, aV2;
4826 SMDS_ElemIteratorPtr aItE;
4827 SMDS_NodeIteratorPtr aItN;
4828 SMDSAbs_ElementType aTypeE;
4830 TNodeOfNodeListMap mapNewNodes;
4833 aNbE = theElements.size();
4836 return EXTR_NO_ELEMENTS;
4838 // 1.1 Track Pattern
4841 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4843 aItE = pMeshDS->elementsIterator();
4844 while ( aItE->more() ) {
4845 const SMDS_MeshElement* pE = aItE->next();
4846 aTypeE = pE->GetType();
4847 // Pattern must contain links only
4848 if ( aTypeE != SMDSAbs_Edge )
4849 return EXTR_PATH_NOT_EDGE;
4852 list<SMESH_MeshEditor_PathPoint> fullList;
4854 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4856 if( aS == SMESH_Mesh::PseudoShape() ) {
4857 //Mesh without shape
4858 const SMDS_MeshNode* currentNode = NULL;
4859 const SMDS_MeshNode* prevNode = theN1;
4860 std::vector<const SMDS_MeshNode*> aNodesList;
4861 aNodesList.push_back(theN1);
4862 int nbEdges = 0, conn=0;
4863 const SMDS_MeshElement* prevElem = NULL;
4864 const SMDS_MeshElement* currentElem = NULL;
4865 int totalNbEdges = theTrack->NbEdges();
4866 SMDS_ElemIteratorPtr nIt;
4867 bool isClosed = false;
4870 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4871 return EXTR_BAD_STARTING_NODE;
4874 conn = nbEdgeConnectivity(theN1);
4876 return EXTR_PATH_NOT_EDGE;
4878 aItE = theN1->GetInverseElementIterator();
4879 prevElem = aItE->next();
4880 currentElem = prevElem;
4882 if(totalNbEdges == 1 ) {
4883 nIt = currentElem->nodesIterator();
4884 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4885 if(currentNode == prevNode)
4886 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4887 aNodesList.push_back(currentNode);
4889 nIt = currentElem->nodesIterator();
4890 while( nIt->more() ) {
4891 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4892 if(currentNode == prevNode)
4893 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4894 aNodesList.push_back(currentNode);
4896 //case of the closed mesh
4897 if(currentNode == theN1) {
4903 conn = nbEdgeConnectivity(currentNode);
4905 return EXTR_PATH_NOT_EDGE;
4906 }else if( conn == 1 && nbEdges > 0 ) {
4911 prevNode = currentNode;
4912 aItE = currentNode->GetInverseElementIterator();
4913 currentElem = aItE->next();
4914 if( currentElem == prevElem)
4915 currentElem = aItE->next();
4916 nIt = currentElem->nodesIterator();
4917 prevElem = currentElem;
4923 if(nbEdges != totalNbEdges)
4924 return EXTR_PATH_NOT_EDGE;
4926 TopTools_SequenceOfShape Edges;
4927 double x1,x2,y1,y2,z1,z2;
4928 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4929 int startNid = theN1->GetID();
4930 for(int i = 1; i < aNodesList.size(); i++) {
4931 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
4932 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
4933 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
4934 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
4935 list<SMESH_MeshEditor_PathPoint> LPP;
4937 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
4938 LLPPs.push_back(LPP);
4939 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
4940 else startNid = aNodesList[i-1]->GetID();
4944 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4945 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4946 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4947 for(; itPP!=firstList.end(); itPP++) {
4948 fullList.push_back( *itPP );
4951 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4952 SMESH_MeshEditor_PathPoint PP2;
4953 fullList.pop_back();
4955 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4956 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4957 itPP = currList.begin();
4958 PP2 = currList.front();
4959 gp_Dir D1 = PP1.Tangent();
4960 gp_Dir D2 = PP2.Tangent();
4961 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4962 (D1.Z()+D2.Z())/2 ) );
4963 PP1.SetTangent(Dnew);
4964 fullList.push_back(PP1);
4966 for(; itPP!=currList.end(); itPP++) {
4967 fullList.push_back( *itPP );
4969 PP1 = fullList.back();
4970 fullList.pop_back();
4972 fullList.push_back(PP1);
4974 } // Sub-shape for the Pattern must be an Edge or Wire
4975 else if( aS.ShapeType() == TopAbs_EDGE ) {
4976 aTrackEdge = TopoDS::Edge( aS );
4977 // the Edge must not be degenerated
4978 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4979 return EXTR_BAD_PATH_SHAPE;
4980 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4981 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4982 const SMDS_MeshNode* aN1 = aItN->next();
4983 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4984 const SMDS_MeshNode* aN2 = aItN->next();
4985 // starting node must be aN1 or aN2
4986 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4987 return EXTR_BAD_STARTING_NODE;
4988 aItN = pMeshDS->nodesIterator();
4989 while ( aItN->more() ) {
4990 const SMDS_MeshNode* pNode = aItN->next();
4991 if( pNode==aN1 || pNode==aN2 ) continue;
4992 const SMDS_EdgePosition* pEPos =
4993 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4994 double aT = pEPos->GetUParameter();
4995 aPrms.push_back( aT );
4997 //Extrusion_Error err =
4998 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5000 else if( aS.ShapeType() == TopAbs_WIRE ) {
5001 list< SMESH_subMesh* > LSM;
5002 TopTools_SequenceOfShape Edges;
5003 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5004 for(; eExp.More(); eExp.Next()) {
5005 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5006 if( BRep_Tool::Degenerated(E) ) continue;
5007 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5013 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5014 int startNid = theN1->GetID();
5015 TColStd_MapOfInteger UsedNums;
5016 int NbEdges = Edges.Length();
5018 for(; i<=NbEdges; i++) {
5020 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5021 for(; itLSM!=LSM.end(); itLSM++) {
5023 if(UsedNums.Contains(k)) continue;
5024 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5025 SMESH_subMesh* locTrack = *itLSM;
5026 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5027 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5028 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5029 const SMDS_MeshNode* aN1 = aItN->next();
5030 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5031 const SMDS_MeshNode* aN2 = aItN->next();
5032 // starting node must be aN1 or aN2
5033 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5034 // 2. Collect parameters on the track edge
5036 aItN = locMeshDS->GetNodes();
5037 while ( aItN->more() ) {
5038 const SMDS_MeshNode* pNode = aItN->next();
5039 const SMDS_EdgePosition* pEPos =
5040 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5041 double aT = pEPos->GetUParameter();
5042 aPrms.push_back( aT );
5044 list<SMESH_MeshEditor_PathPoint> LPP;
5045 //Extrusion_Error err =
5046 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5047 LLPPs.push_back(LPP);
5049 // update startN for search following egde
5050 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5051 else startNid = aN1->GetID();
5055 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5056 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5057 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5058 for(; itPP!=firstList.end(); itPP++) {
5059 fullList.push_back( *itPP );
5061 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5062 fullList.pop_back();
5064 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5065 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5066 itPP = currList.begin();
5067 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5068 gp_Dir D1 = PP1.Tangent();
5069 gp_Dir D2 = PP2.Tangent();
5070 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5071 (D1.Z()+D2.Z())/2 ) );
5072 PP1.SetTangent(Dnew);
5073 fullList.push_back(PP1);
5075 for(; itPP!=currList.end(); itPP++) {
5076 fullList.push_back( *itPP );
5078 PP1 = fullList.back();
5079 fullList.pop_back();
5081 // if wire not closed
5082 fullList.push_back(PP1);
5086 return EXTR_BAD_PATH_SHAPE;
5089 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5090 theHasRefPoint, theRefPoint, theMakeGroups);
5094 //=======================================================================
5095 //function : MakeEdgePathPoints
5096 //purpose : auxilary for ExtrusionAlongTrack
5097 //=======================================================================
5098 SMESH_MeshEditor::Extrusion_Error
5099 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5100 const TopoDS_Edge& aTrackEdge,
5102 list<SMESH_MeshEditor_PathPoint>& LPP)
5104 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5106 aTolVec2=aTolVec*aTolVec;
5108 TopoDS_Vertex aV1, aV2;
5109 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5110 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5111 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5112 // 2. Collect parameters on the track edge
5113 aPrms.push_front( aT1 );
5114 aPrms.push_back( aT2 );
5117 if( FirstIsStart ) {
5128 SMESH_MeshEditor_PathPoint aPP;
5129 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5130 std::list<double>::iterator aItD = aPrms.begin();
5131 for(; aItD != aPrms.end(); ++aItD) {
5135 aC3D->D1( aT, aP3D, aVec );
5136 aL2 = aVec.SquareMagnitude();
5137 if ( aL2 < aTolVec2 )
5138 return EXTR_CANT_GET_TANGENT;
5139 gp_Dir aTgt( aVec );
5141 aPP.SetTangent( aTgt );
5142 aPP.SetParameter( aT );
5149 //=======================================================================
5150 //function : MakeExtrElements
5151 //purpose : auxilary for ExtrusionAlongTrack
5152 //=======================================================================
5153 SMESH_MeshEditor::Extrusion_Error
5154 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5155 list<SMESH_MeshEditor_PathPoint>& fullList,
5156 const bool theHasAngles,
5157 list<double>& theAngles,
5158 const bool theLinearVariation,
5159 const bool theHasRefPoint,
5160 const gp_Pnt& theRefPoint,
5161 const bool theMakeGroups)
5163 MESSAGE("MakeExtrElements");
5164 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5165 int aNbTP = fullList.size();
5166 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5168 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5169 LinearAngleVariation(aNbTP-1, theAngles);
5171 vector<double> aAngles( aNbTP );
5173 for(; j<aNbTP; ++j) {
5176 if ( theHasAngles ) {
5178 std::list<double>::iterator aItD = theAngles.begin();
5179 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5181 aAngles[j] = anAngle;
5184 // fill vector of path points with angles
5185 //aPPs.resize(fullList.size());
5187 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5188 for(; itPP!=fullList.end(); itPP++) {
5190 SMESH_MeshEditor_PathPoint PP = *itPP;
5191 PP.SetAngle(aAngles[j]);
5195 TNodeOfNodeListMap mapNewNodes;
5196 TElemOfVecOfNnlmiMap mapElemNewNodes;
5197 TElemOfElemListMap newElemsMap;
5198 TIDSortedElemSet::iterator itElem;
5201 SMDSAbs_ElementType aTypeE;
5202 // source elements for each generated one
5203 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5205 // 3. Center of rotation aV0
5206 gp_Pnt aV0 = theRefPoint;
5208 if ( !theHasRefPoint ) {
5210 aGC.SetCoord( 0.,0.,0. );
5212 itElem = theElements.begin();
5213 for ( ; itElem != theElements.end(); itElem++ ) {
5214 const SMDS_MeshElement* elem = *itElem;
5216 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5217 while ( itN->more() ) {
5218 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5223 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5224 list<const SMDS_MeshNode*> aLNx;
5225 mapNewNodes[node] = aLNx;
5227 gp_XYZ aXYZ( aX, aY, aZ );
5235 } // if (!theHasRefPoint) {
5236 mapNewNodes.clear();
5238 // 4. Processing the elements
5239 SMESHDS_Mesh* aMesh = GetMeshDS();
5241 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5242 // check element type
5243 const SMDS_MeshElement* elem = *itElem;
5244 aTypeE = elem->GetType();
5245 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5248 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5249 newNodesItVec.reserve( elem->NbNodes() );
5251 // loop on elem nodes
5253 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5254 while ( itN->more() )
5257 // check if a node has been already processed
5258 const SMDS_MeshNode* node =
5259 static_cast<const SMDS_MeshNode*>( itN->next() );
5260 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5261 if ( nIt == mapNewNodes.end() ) {
5262 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5263 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5266 aX = node->X(); aY = node->Y(); aZ = node->Z();
5268 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5269 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5270 gp_Ax1 anAx1, anAxT1T0;
5271 gp_Dir aDT1x, aDT0x, aDT1T0;
5276 aPN0.SetCoord(aX, aY, aZ);
5278 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5280 aDT0x= aPP0.Tangent();
5281 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5283 for ( j = 1; j < aNbTP; ++j ) {
5284 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5286 aDT1x = aPP1.Tangent();
5287 aAngle1x = aPP1.Angle();
5289 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5291 gp_Vec aV01x( aP0x, aP1x );
5292 aTrsf.SetTranslation( aV01x );
5295 aV1x = aV0x.Transformed( aTrsf );
5296 aPN1 = aPN0.Transformed( aTrsf );
5298 // rotation 1 [ T1,T0 ]
5299 aAngleT1T0=-aDT1x.Angle( aDT0x );
5300 if (fabs(aAngleT1T0) > aTolAng) {
5302 anAxT1T0.SetLocation( aV1x );
5303 anAxT1T0.SetDirection( aDT1T0 );
5304 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5306 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5310 if ( theHasAngles ) {
5311 anAx1.SetLocation( aV1x );
5312 anAx1.SetDirection( aDT1x );
5313 aTrsfRot.SetRotation( anAx1, aAngle1x );
5315 aPN1 = aPN1.Transformed( aTrsfRot );
5319 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5320 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5321 // create additional node
5322 double x = ( aPN1.X() + aPN0.X() )/2.;
5323 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5324 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5325 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5326 myLastCreatedNodes.Append(newNode);
5327 srcNodes.Append( node );
5328 listNewNodes.push_back( newNode );
5333 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5334 myLastCreatedNodes.Append(newNode);
5335 srcNodes.Append( node );
5336 listNewNodes.push_back( newNode );
5346 // if current elem is quadratic and current node is not medium
5347 // we have to check - may be it is needed to insert additional nodes
5348 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5349 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5350 if(listNewNodes.size()==aNbTP-1) {
5351 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5352 gp_XYZ P(node->X(), node->Y(), node->Z());
5353 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5355 for(i=0; i<aNbTP-1; i++) {
5356 const SMDS_MeshNode* N = *it;
5357 double x = ( N->X() + P.X() )/2.;
5358 double y = ( N->Y() + P.Y() )/2.;
5359 double z = ( N->Z() + P.Z() )/2.;
5360 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5361 srcNodes.Append( node );
5362 myLastCreatedNodes.Append(newN);
5365 P = gp_XYZ(N->X(),N->Y(),N->Z());
5367 listNewNodes.clear();
5368 for(i=0; i<2*(aNbTP-1); i++) {
5369 listNewNodes.push_back(aNodes[i]);
5375 newNodesItVec.push_back( nIt );
5377 // make new elements
5378 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5379 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5380 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5383 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5385 if ( theMakeGroups )
5386 generateGroups( srcNodes, srcElems, "extruded");
5392 //=======================================================================
5393 //function : LinearAngleVariation
5394 //purpose : auxilary for ExtrusionAlongTrack
5395 //=======================================================================
5396 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5397 list<double>& Angles)
5399 int nbAngles = Angles.size();
5400 if( nbSteps > nbAngles ) {
5401 vector<double> theAngles(nbAngles);
5402 list<double>::iterator it = Angles.begin();
5404 for(; it!=Angles.end(); it++) {
5406 theAngles[i] = (*it);
5409 double rAn2St = double( nbAngles ) / double( nbSteps );
5410 double angPrev = 0, angle;
5411 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5412 double angCur = rAn2St * ( iSt+1 );
5413 double angCurFloor = floor( angCur );
5414 double angPrevFloor = floor( angPrev );
5415 if ( angPrevFloor == angCurFloor )
5416 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5418 int iP = int( angPrevFloor );
5419 double angPrevCeil = ceil(angPrev);
5420 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5422 int iC = int( angCurFloor );
5423 if ( iC < nbAngles )
5424 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5426 iP = int( angPrevCeil );
5428 angle += theAngles[ iC ];
5430 res.push_back(angle);
5435 for(; it!=res.end(); it++)
5436 Angles.push_back( *it );
5441 //================================================================================
5443 * \brief Move or copy theElements applying theTrsf to their nodes
5444 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5445 * \param theTrsf - transformation to apply
5446 * \param theCopy - if true, create translated copies of theElems
5447 * \param theMakeGroups - if true and theCopy, create translated groups
5448 * \param theTargetMesh - mesh to copy translated elements into
5449 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5451 //================================================================================
5453 SMESH_MeshEditor::PGroupIDs
5454 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5455 const gp_Trsf& theTrsf,
5457 const bool theMakeGroups,
5458 SMESH_Mesh* theTargetMesh)
5460 myLastCreatedElems.Clear();
5461 myLastCreatedNodes.Clear();
5463 bool needReverse = false;
5464 string groupPostfix;
5465 switch ( theTrsf.Form() ) {
5467 MESSAGE("gp_PntMirror");
5469 groupPostfix = "mirrored";
5472 MESSAGE("gp_Ax1Mirror");
5473 groupPostfix = "mirrored";
5476 MESSAGE("gp_Ax2Mirror");
5478 groupPostfix = "mirrored";
5481 MESSAGE("gp_Rotation");
5482 groupPostfix = "rotated";
5484 case gp_Translation:
5485 MESSAGE("gp_Translation");
5486 groupPostfix = "translated";
5489 MESSAGE("gp_Scale");
5490 groupPostfix = "scaled";
5492 case gp_CompoundTrsf: // different scale by axis
5493 MESSAGE("gp_CompoundTrsf");
5494 groupPostfix = "scaled";
5498 needReverse = false;
5499 groupPostfix = "transformed";
5502 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5503 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5504 SMESHDS_Mesh* aMesh = GetMeshDS();
5507 // map old node to new one
5508 TNodeNodeMap nodeMap;
5510 // elements sharing moved nodes; those of them which have all
5511 // nodes mirrored but are not in theElems are to be reversed
5512 TIDSortedElemSet inverseElemSet;
5514 // source elements for each generated one
5515 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5517 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5518 TIDSortedElemSet orphanNode;
5520 if ( theElems.empty() ) // transform the whole mesh
5523 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5524 while ( eIt->more() ) theElems.insert( eIt->next() );
5526 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5527 while ( nIt->more() )
5529 const SMDS_MeshNode* node = nIt->next();
5530 if ( node->NbInverseElements() == 0)
5531 orphanNode.insert( node );
5535 // loop on elements to transform nodes : first orphan nodes then elems
5536 TIDSortedElemSet::iterator itElem;
5537 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5538 for (int i=0; i<2; i++)
5539 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5540 const SMDS_MeshElement* elem = *itElem;
5544 // loop on elem nodes
5545 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5546 while ( itN->more() ) {
5548 const SMDS_MeshNode* node = cast2Node( itN->next() );
5549 // check if a node has been already transformed
5550 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5551 nodeMap.insert( make_pair ( node, node ));
5552 if ( !n2n_isnew.second )
5556 coord[0] = node->X();
5557 coord[1] = node->Y();
5558 coord[2] = node->Z();
5559 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5560 if ( theTargetMesh ) {
5561 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5562 n2n_isnew.first->second = newNode;
5563 myLastCreatedNodes.Append(newNode);
5564 srcNodes.Append( node );
5566 else if ( theCopy ) {
5567 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5568 n2n_isnew.first->second = newNode;
5569 myLastCreatedNodes.Append(newNode);
5570 srcNodes.Append( node );
5573 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5574 // node position on shape becomes invalid
5575 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5576 ( SMDS_SpacePosition::originSpacePosition() );
5579 // keep inverse elements
5580 if ( !theCopy && !theTargetMesh && needReverse ) {
5581 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5582 while ( invElemIt->more() ) {
5583 const SMDS_MeshElement* iel = invElemIt->next();
5584 inverseElemSet.insert( iel );
5590 // either create new elements or reverse mirrored ones
5591 if ( !theCopy && !needReverse && !theTargetMesh )
5594 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5595 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5596 theElems.insert( *invElemIt );
5598 // Replicate or reverse elements
5600 std::vector<int> iForw;
5601 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5603 const SMDS_MeshElement* elem = *itElem;
5604 if ( !elem || elem->GetType() == SMDSAbs_Node )
5607 int nbNodes = elem->NbNodes();
5608 int elemType = elem->GetType();
5610 if (elem->IsPoly()) {
5612 // polygon or polyhedral volume
5613 switch ( elemType ) {
5616 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5618 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5619 while (itN->more()) {
5620 const SMDS_MeshNode* node =
5621 static_cast<const SMDS_MeshNode*>(itN->next());
5622 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5623 if (nodeMapIt == nodeMap.end())
5624 break; // not all nodes transformed
5626 // reverse mirrored faces and volumes
5627 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5629 poly_nodes[iNode] = (*nodeMapIt).second;
5633 if ( iNode != nbNodes )
5634 continue; // not all nodes transformed
5636 if ( theTargetMesh ) {
5637 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5638 srcElems.Append( elem );
5640 else if ( theCopy ) {
5641 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5642 srcElems.Append( elem );
5645 aMesh->ChangePolygonNodes(elem, poly_nodes);
5649 case SMDSAbs_Volume:
5651 const SMDS_VtkVolume* aPolyedre =
5652 dynamic_cast<const SMDS_VtkVolume*>( elem );
5654 MESSAGE("Warning: bad volumic element");
5658 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5659 vector<int> quantities;
5661 bool allTransformed = true;
5662 int nbFaces = aPolyedre->NbFaces();
5663 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5664 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5665 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5666 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5667 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5668 if (nodeMapIt == nodeMap.end()) {
5669 allTransformed = false; // not all nodes transformed
5671 poly_nodes.push_back((*nodeMapIt).second);
5673 if ( needReverse && allTransformed )
5674 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5676 quantities.push_back(nbFaceNodes);
5678 if ( !allTransformed )
5679 continue; // not all nodes transformed
5681 if ( theTargetMesh ) {
5682 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5683 srcElems.Append( elem );
5685 else if ( theCopy ) {
5686 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5687 srcElems.Append( elem );
5690 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5702 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5703 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5704 const std::vector<int>& i = needReverse ? iRev : iForw;
5706 // find transformed nodes
5707 vector<const SMDS_MeshNode*> nodes(nbNodes);
5709 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5710 while ( itN->more() ) {
5711 const SMDS_MeshNode* node =
5712 static_cast<const SMDS_MeshNode*>( itN->next() );
5713 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5714 if ( nodeMapIt == nodeMap.end() )
5715 break; // not all nodes transformed
5716 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5718 if ( iNode != nbNodes )
5719 continue; // not all nodes transformed
5721 if ( theTargetMesh ) {
5722 if ( SMDS_MeshElement* copy =
5723 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5724 myLastCreatedElems.Append( copy );
5725 srcElems.Append( elem );
5728 else if ( theCopy ) {
5729 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5730 srcElems.Append( elem );
5733 // reverse element as it was reversed by transformation
5735 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5738 } // loop on elements
5740 PGroupIDs newGroupIDs;
5742 if ( ( theMakeGroups && theCopy ) ||
5743 ( theMakeGroups && theTargetMesh ) )
5744 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5749 //=======================================================================
5751 * \brief Create groups of elements made during transformation
5752 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5753 * \param elemGens - elements making corresponding myLastCreatedElems
5754 * \param postfix - to append to names of new groups
5756 //=======================================================================
5758 SMESH_MeshEditor::PGroupIDs
5759 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5760 const SMESH_SequenceOfElemPtr& elemGens,
5761 const std::string& postfix,
5762 SMESH_Mesh* targetMesh)
5764 PGroupIDs newGroupIDs( new list<int> );
5765 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5767 // Sort existing groups by types and collect their names
5769 // to store an old group and a generated new one
5770 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5771 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5773 set< string > groupNames;
5775 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5776 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5777 while ( groupIt->more() ) {
5778 SMESH_Group * group = groupIt->next();
5779 if ( !group ) continue;
5780 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5781 if ( !groupDS || groupDS->IsEmpty() ) continue;
5782 groupNames.insert( group->GetName() );
5783 groupDS->SetStoreName( group->GetName() );
5784 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5789 // loop on nodes and elements
5790 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5792 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5793 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5794 if ( gens.Length() != elems.Length() )
5795 throw SALOME_Exception(LOCALIZED("invalid args"));
5797 // loop on created elements
5798 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5800 const SMDS_MeshElement* sourceElem = gens( iElem );
5801 if ( !sourceElem ) {
5802 MESSAGE("generateGroups(): NULL source element");
5805 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5806 if ( groupsOldNew.empty() ) {
5807 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5808 ++iElem; // skip all elements made by sourceElem
5811 // collect all elements made by sourceElem
5812 list< const SMDS_MeshElement* > resultElems;
5813 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5814 if ( resElem != sourceElem )
5815 resultElems.push_back( resElem );
5816 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5817 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5818 if ( resElem != sourceElem )
5819 resultElems.push_back( resElem );
5820 // do not generate element groups from node ones
5821 // if ( sourceElem->GetType() == SMDSAbs_Node &&
5822 // elems( iElem )->GetType() != SMDSAbs_Node )
5825 // add resultElems to groups made by ones the sourceElem belongs to
5826 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5827 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5829 SMESHDS_GroupBase* oldGroup = gOldNew->first;
5830 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5832 SMDS_MeshGroup* & newGroup = gOldNew->second;
5833 if ( !newGroup )// create a new group
5836 string name = oldGroup->GetStoreName();
5837 if ( !targetMesh ) {
5841 while ( !groupNames.insert( name ).second ) // name exists
5847 TCollection_AsciiString nbStr(nb+1);
5848 name.resize( name.rfind('_')+1 );
5849 name += nbStr.ToCString();
5856 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5858 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5859 newGroup = & groupDS->SMDSGroup();
5860 newGroupIDs->push_back( id );
5863 // fill in a new group
5864 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5865 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5866 newGroup->Add( *resElemIt );
5869 } // loop on created elements
5870 }// loop on nodes and elements
5875 //================================================================================
5877 * \brief Return list of group of nodes close to each other within theTolerance
5878 * Search among theNodes or in the whole mesh if theNodes is empty using
5879 * an Octree algorithm
5881 //================================================================================
5883 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
5884 const double theTolerance,
5885 TListOfListOfNodes & theGroupsOfNodes)
5887 myLastCreatedElems.Clear();
5888 myLastCreatedNodes.Clear();
5890 if ( theNodes.empty() )
5891 { // get all nodes in the mesh
5892 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
5893 while ( nIt->more() )
5894 theNodes.insert( theNodes.end(),nIt->next());
5897 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
5901 //=======================================================================
5903 * \brief Implementation of search for the node closest to point
5905 //=======================================================================
5907 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
5909 //---------------------------------------------------------------------
5911 * \brief Constructor
5913 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
5915 myMesh = ( SMESHDS_Mesh* ) theMesh;
5917 TIDSortedNodeSet nodes;
5919 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
5920 while ( nIt->more() )
5921 nodes.insert( nodes.end(), nIt->next() );
5923 myOctreeNode = new SMESH_OctreeNode(nodes) ;
5925 // get max size of a leaf box
5926 SMESH_OctreeNode* tree = myOctreeNode;
5927 while ( !tree->isLeaf() )
5929 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5933 myHalfLeafSize = tree->maxSize() / 2.;
5936 //---------------------------------------------------------------------
5938 * \brief Move node and update myOctreeNode accordingly
5940 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
5942 myOctreeNode->UpdateByMoveNode( node, toPnt );
5943 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
5946 //---------------------------------------------------------------------
5948 * \brief Do it's job
5950 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
5952 map<double, const SMDS_MeshNode*> dist2Nodes;
5953 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
5954 if ( !dist2Nodes.empty() )
5955 return dist2Nodes.begin()->second;
5956 list<const SMDS_MeshNode*> nodes;
5957 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
5959 double minSqDist = DBL_MAX;
5960 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
5962 // sort leafs by their distance from thePnt
5963 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
5964 TDistTreeMap treeMap;
5965 list< SMESH_OctreeNode* > treeList;
5966 list< SMESH_OctreeNode* >::iterator trIt;
5967 treeList.push_back( myOctreeNode );
5969 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
5970 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
5971 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
5973 SMESH_OctreeNode* tree = *trIt;
5974 if ( !tree->isLeaf() ) // put children to the queue
5976 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
5977 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
5978 while ( cIt->more() )
5979 treeList.push_back( cIt->next() );
5981 else if ( tree->NbNodes() ) // put a tree to the treeMap
5983 const Bnd_B3d& box = tree->getBox();
5984 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
5985 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
5986 if ( !it_in.second ) // not unique distance to box center
5987 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
5990 // find distance after which there is no sense to check tree's
5991 double sqLimit = DBL_MAX;
5992 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
5993 if ( treeMap.size() > 5 ) {
5994 SMESH_OctreeNode* closestTree = sqDist_tree->second;
5995 const Bnd_B3d& box = closestTree->getBox();
5996 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
5997 sqLimit = limit * limit;
5999 // get all nodes from trees
6000 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6001 if ( sqDist_tree->first > sqLimit )
6003 SMESH_OctreeNode* tree = sqDist_tree->second;
6004 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6007 // find closest among nodes
6008 minSqDist = DBL_MAX;
6009 const SMDS_MeshNode* closestNode = 0;
6010 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6011 for ( ; nIt != nodes.end(); ++nIt ) {
6012 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6013 if ( minSqDist > sqDist ) {
6021 //---------------------------------------------------------------------
6025 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6027 //---------------------------------------------------------------------
6029 * \brief Return the node tree
6031 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6034 SMESH_OctreeNode* myOctreeNode;
6035 SMESHDS_Mesh* myMesh;
6036 double myHalfLeafSize; // max size of a leaf box
6039 //=======================================================================
6041 * \brief Return SMESH_NodeSearcher
6043 //=======================================================================
6045 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6047 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6050 // ========================================================================
6051 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6053 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6054 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6055 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6057 //=======================================================================
6059 * \brief Octal tree of bounding boxes of elements
6061 //=======================================================================
6063 class ElementBndBoxTree : public SMESH_Octree
6067 ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(), double tolerance = NodeRadius );
6068 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems);
6069 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6070 ~ElementBndBoxTree();
6073 ElementBndBoxTree() {}
6074 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6075 void buildChildrenData();
6076 Bnd_B3d* buildRootBox();
6078 //!< Bounding box of element
6079 struct ElementBox : public Bnd_B3d
6081 const SMDS_MeshElement* _element;
6082 int _refCount; // an ElementBox can be included in several tree branches
6083 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6085 vector< ElementBox* > _elements;
6088 //================================================================================
6090 * \brief ElementBndBoxTree creation
6092 //================================================================================
6094 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6095 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6097 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6098 _elements.reserve( nbElems );
6100 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6101 while ( elemIt->more() )
6102 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6104 if ( _elements.size() > MaxNbElemsInLeaf )
6110 //================================================================================
6114 //================================================================================
6116 ElementBndBoxTree::~ElementBndBoxTree()
6118 for ( int i = 0; i < _elements.size(); ++i )
6119 if ( --_elements[i]->_refCount <= 0 )
6120 delete _elements[i];
6123 //================================================================================
6125 * \brief Return the maximal box
6127 //================================================================================
6129 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6131 Bnd_B3d* box = new Bnd_B3d;
6132 for ( int i = 0; i < _elements.size(); ++i )
6133 box->Add( *_elements[i] );
6137 //================================================================================
6139 * \brief Redistrubute element boxes among children
6141 //================================================================================
6143 void ElementBndBoxTree::buildChildrenData()
6145 for ( int i = 0; i < _elements.size(); ++i )
6147 for (int j = 0; j < 8; j++)
6149 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6151 _elements[i]->_refCount++;
6152 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6155 _elements[i]->_refCount--;
6159 for (int j = 0; j < 8; j++)
6161 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6162 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6163 child->myIsLeaf = true;
6165 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6166 child->_elements.resize( child->_elements.size() ); // compact
6170 //================================================================================
6172 * \brief Return elements which can include the point
6174 //================================================================================
6176 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6177 TIDSortedElemSet& foundElems)
6179 if ( level() && getBox().IsOut( point.XYZ() ))
6184 for ( int i = 0; i < _elements.size(); ++i )
6185 if ( !_elements[i]->IsOut( point.XYZ() ))
6186 foundElems.insert( _elements[i]->_element );
6190 for (int i = 0; i < 8; i++)
6191 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6195 //================================================================================
6197 * \brief Return elements which can be intersected by the line
6199 //================================================================================
6201 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6202 TIDSortedElemSet& foundElems)
6204 if ( level() && getBox().IsOut( line ))
6209 for ( int i = 0; i < _elements.size(); ++i )
6210 if ( !_elements[i]->IsOut( line ))
6211 foundElems.insert( _elements[i]->_element );
6215 for (int i = 0; i < 8; i++)
6216 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6220 //================================================================================
6222 * \brief Construct the element box
6224 //================================================================================
6226 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6230 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6231 while ( nIt->more() )
6232 Add( SMESH_TNodeXYZ( cast2Node( nIt->next() )));
6233 Enlarge( tolerance );
6238 //=======================================================================
6240 * \brief Implementation of search for the elements by point and
6241 * of classification of point in 2D mesh
6243 //=======================================================================
6245 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6247 SMESHDS_Mesh* _mesh;
6248 SMDS_ElemIteratorPtr _meshPartIt;
6249 ElementBndBoxTree* _ebbTree;
6250 SMESH_NodeSearcherImpl* _nodeSearcher;
6251 SMDSAbs_ElementType _elementType;
6253 bool _outerFacesFound;
6254 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6256 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6257 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6258 ~SMESH_ElementSearcherImpl()
6260 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6261 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6263 virtual int FindElementsByPoint(const gp_Pnt& point,
6264 SMDSAbs_ElementType type,
6265 vector< const SMDS_MeshElement* >& foundElements);
6266 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6268 void GetElementsNearLine( const gp_Ax1& line,
6269 SMDSAbs_ElementType type,
6270 vector< const SMDS_MeshElement* >& foundElems);
6271 double getTolerance();
6272 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6273 const double tolerance, double & param);
6274 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6275 bool isOuterBoundary(const SMDS_MeshElement* face) const
6277 return _outerFaces.empty() || _outerFaces.count(face);
6279 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6281 const SMDS_MeshElement* _face;
6283 bool _coincides; //!< the line lays in face plane
6284 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6285 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6287 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6290 TIDSortedElemSet _faces;
6291 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6292 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6296 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6298 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6299 << ", _coincides="<<i._coincides << ")";
6302 //=======================================================================
6304 * \brief define tolerance for search
6306 //=======================================================================
6308 double SMESH_ElementSearcherImpl::getTolerance()
6310 if ( _tolerance < 0 )
6312 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6315 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6317 double boxSize = _nodeSearcher->getTree()->maxSize();
6318 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6320 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6322 double boxSize = _ebbTree->maxSize();
6323 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6325 if ( _tolerance == 0 )
6327 // define tolerance by size of a most complex element
6328 int complexType = SMDSAbs_Volume;
6329 while ( complexType > SMDSAbs_All &&
6330 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6332 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6334 if ( complexType == int( SMDSAbs_Node ))
6336 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6338 if ( meshInfo.NbNodes() > 2 )
6339 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6343 SMDS_ElemIteratorPtr elemIt =
6344 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6345 const SMDS_MeshElement* elem = elemIt->next();
6346 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6347 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6349 while ( nodeIt->more() )
6351 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6352 elemSize = max( dist, elemSize );
6355 _tolerance = 1e-4 * elemSize;
6361 //================================================================================
6363 * \brief Find intersection of the line and an edge of face and return parameter on line
6365 //================================================================================
6367 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6368 const SMDS_MeshElement* face,
6375 GeomAPI_ExtremaCurveCurve anExtCC;
6376 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6378 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6379 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6381 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6382 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6383 anExtCC.Init( lineCurve, edge);
6384 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6386 Quantity_Parameter pl, pe;
6387 anExtCC.LowerDistanceParameters( pl, pe );
6389 if ( ++nbInts == 2 )
6393 if ( nbInts > 0 ) param /= nbInts;
6396 //================================================================================
6398 * \brief Find all faces belonging to the outer boundary of mesh
6400 //================================================================================
6402 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6404 if ( _outerFacesFound ) return;
6406 // Collect all outer faces by passing from one outer face to another via their links
6407 // and BTW find out if there are internal faces at all.
6409 // checked links and links where outer boundary meets internal one
6410 set< SMESH_TLink > visitedLinks, seamLinks;
6412 // links to treat with already visited faces sharing them
6413 list < TFaceLink > startLinks;
6415 // load startLinks with the first outerFace
6416 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6417 _outerFaces.insert( outerFace );
6419 TIDSortedElemSet emptySet;
6420 while ( !startLinks.empty() )
6422 const SMESH_TLink& link = startLinks.front()._link;
6423 TIDSortedElemSet& faces = startLinks.front()._faces;
6425 outerFace = *faces.begin();
6426 // find other faces sharing the link
6427 const SMDS_MeshElement* f;
6428 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6431 // select another outer face among the found
6432 const SMDS_MeshElement* outerFace2 = 0;
6433 if ( faces.size() == 2 )
6435 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6437 else if ( faces.size() > 2 )
6439 seamLinks.insert( link );
6441 // link direction within the outerFace
6442 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6443 SMESH_TNodeXYZ( link.node2()));
6444 int i1 = outerFace->GetNodeIndex( link.node1() );
6445 int i2 = outerFace->GetNodeIndex( link.node2() );
6446 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6447 if ( rev ) n1n2.Reverse();
6449 gp_XYZ ofNorm, fNorm;
6450 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6452 // direction from the link inside outerFace
6453 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6454 // sort all other faces by angle with the dirInOF
6455 map< double, const SMDS_MeshElement* > angle2Face;
6456 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6457 for ( ; face != faces.end(); ++face )
6459 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6461 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6462 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6463 if ( angle < 0 ) angle += 2. * M_PI;
6464 angle2Face.insert( make_pair( angle, *face ));
6466 if ( !angle2Face.empty() )
6467 outerFace2 = angle2Face.begin()->second;
6470 // store the found outer face and add its links to continue seaching from
6473 _outerFaces.insert( outerFace );
6474 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6475 for ( int i = 0; i < nbNodes; ++i )
6477 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6478 if ( visitedLinks.insert( link2 ).second )
6479 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6482 startLinks.pop_front();
6484 _outerFacesFound = true;
6486 if ( !seamLinks.empty() )
6488 // There are internal boundaries touching the outher one,
6489 // find all faces of internal boundaries in order to find
6490 // faces of boundaries of holes, if any.
6495 _outerFaces.clear();
6499 //=======================================================================
6501 * \brief Find elements of given type where the given point is IN or ON.
6502 * Returns nb of found elements and elements them-selves.
6504 * 'ALL' type means elements of any type excluding nodes and 0D elements
6506 //=======================================================================
6508 int SMESH_ElementSearcherImpl::
6509 FindElementsByPoint(const gp_Pnt& point,
6510 SMDSAbs_ElementType type,
6511 vector< const SMDS_MeshElement* >& foundElements)
6513 foundElements.clear();
6515 double tolerance = getTolerance();
6517 // =================================================================================
6518 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement )
6520 if ( !_nodeSearcher )
6521 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6523 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6524 if ( !closeNode ) return foundElements.size();
6526 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6527 return foundElements.size(); // to far from any node
6529 if ( type == SMDSAbs_Node )
6531 foundElements.push_back( closeNode );
6535 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( SMDSAbs_0DElement );
6536 while ( elemIt->more() )
6537 foundElements.push_back( elemIt->next() );
6540 // =================================================================================
6541 else // elements more complex than 0D
6543 if ( !_ebbTree || _elementType != type )
6545 if ( _ebbTree ) delete _ebbTree;
6546 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6548 TIDSortedElemSet suspectElems;
6549 _ebbTree->getElementsNearPoint( point, suspectElems );
6550 TIDSortedElemSet::iterator elem = suspectElems.begin();
6551 for ( ; elem != suspectElems.end(); ++elem )
6552 if ( !SMESH_MeshEditor::isOut( *elem, point, tolerance ))
6553 foundElements.push_back( *elem );
6555 return foundElements.size();
6558 //================================================================================
6560 * \brief Classify the given point in the closed 2D mesh
6562 //================================================================================
6564 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6566 double tolerance = getTolerance();
6567 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6569 if ( _ebbTree ) delete _ebbTree;
6570 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6572 // Algo: analyse transition of a line starting at the point through mesh boundary;
6573 // try three lines parallel to axis of the coordinate system and perform rough
6574 // analysis. If solution is not clear perform thorough analysis.
6576 const int nbAxes = 3;
6577 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6578 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6579 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6580 multimap< int, int > nbInt2Axis; // to find the simplest case
6581 for ( int axis = 0; axis < nbAxes; ++axis )
6583 gp_Ax1 lineAxis( point, axisDir[axis]);
6584 gp_Lin line ( lineAxis );
6586 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6587 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6589 // Intersect faces with the line
6591 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6592 TIDSortedElemSet::iterator face = suspectFaces.begin();
6593 for ( ; face != suspectFaces.end(); ++face )
6597 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6598 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6600 // perform intersection
6601 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6602 if ( !intersection.IsDone() )
6604 if ( intersection.IsInQuadric() )
6606 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6608 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6610 gp_Pnt intersectionPoint = intersection.Point(1);
6611 if ( !SMESH_MeshEditor::isOut( *face, intersectionPoint, tolerance ))
6612 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6615 // Analyse intersections roughly
6617 int nbInter = u2inters.size();
6621 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6622 if ( nbInter == 1 ) // not closed mesh
6623 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6625 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6628 if ( (f<0) == (l<0) )
6631 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6632 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6633 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6636 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6638 if ( _outerFacesFound ) break; // pass to thorough analysis
6640 } // three attempts - loop on CS axes
6642 // Analyse intersections thoroughly.
6643 // We make two loops maximum, on the first one we only exclude touching intersections,
6644 // on the second, if situation is still unclear, we gather and use information on
6645 // position of faces (internal or outer). If faces position is already gathered,
6646 // we make the second loop right away.
6648 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6650 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6651 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6653 int axis = nb_axis->second;
6654 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6656 gp_Ax1 lineAxis( point, axisDir[axis]);
6657 gp_Lin line ( lineAxis );
6659 // add tangent intersections to u2inters
6661 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6662 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6663 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6664 u2inters.insert(make_pair( param, *tgtInt ));
6665 tangentInters[ axis ].clear();
6667 // Count intersections before and after the point excluding touching ones.
6668 // If hasPositionInfo we count intersections of outer boundary only
6670 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6671 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6672 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6673 bool ok = ! u_int1->second._coincides;
6674 while ( ok && u_int1 != u2inters.end() )
6676 double u = u_int1->first;
6677 bool touchingInt = false;
6678 if ( ++u_int2 != u2inters.end() )
6680 // skip intersections at the same point (if the line passes through edge or node)
6682 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6688 // skip tangent intersections
6690 const SMDS_MeshElement* prevFace = u_int1->second._face;
6691 while ( ok && u_int2->second._coincides )
6693 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6699 ok = ( u_int2 != u2inters.end() );
6704 // skip intersections at the same point after tangent intersections
6707 double u2 = u_int2->first;
6709 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6715 // decide if we skipped a touching intersection
6716 if ( nbSamePnt + nbTgt > 0 )
6718 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6719 map< double, TInters >::iterator u_int = u_int1;
6720 for ( ; u_int != u_int2; ++u_int )
6722 if ( u_int->second._coincides ) continue;
6723 double dot = u_int->second._faceNorm * line.Direction();
6724 if ( dot > maxDot ) maxDot = dot;
6725 if ( dot < minDot ) minDot = dot;
6727 touchingInt = ( minDot*maxDot < 0 );
6732 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6743 u_int1 = u_int2; // to next intersection
6745 } // loop on intersections with one line
6749 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6752 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6755 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6756 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6758 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6761 if ( (f<0) == (l<0) )
6764 if ( hasPositionInfo )
6765 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
6767 } // loop on intersections of the tree lines - thorough analysis
6769 if ( !hasPositionInfo )
6771 // gather info on faces position - is face in the outer boundary or not
6772 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
6773 findOuterBoundary( u2inters.begin()->second._face );
6776 } // two attempts - with and w/o faces position info in the mesh
6778 return TopAbs_UNKNOWN;
6781 //=======================================================================
6783 * \brief Return elements possibly intersecting the line
6785 //=======================================================================
6787 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
6788 SMDSAbs_ElementType type,
6789 vector< const SMDS_MeshElement* >& foundElems)
6791 if ( !_ebbTree || _elementType != type )
6793 if ( _ebbTree ) delete _ebbTree;
6794 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6796 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
6797 _ebbTree->getElementsNearLine( line, suspectFaces );
6798 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
6801 //=======================================================================
6803 * \brief Return SMESH_ElementSearcher
6805 //=======================================================================
6807 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
6809 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
6812 //=======================================================================
6814 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
6816 //=======================================================================
6818 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
6820 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
6823 //=======================================================================
6825 * \brief Return true if the point is IN or ON of the element
6827 //=======================================================================
6829 bool SMESH_MeshEditor::isOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
6831 if ( element->GetType() == SMDSAbs_Volume)
6833 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
6836 // get ordered nodes
6838 vector< gp_XYZ > xyz;
6839 vector<const SMDS_MeshNode*> nodeList;
6841 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
6842 if ( element->IsQuadratic() ) {
6843 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
6844 nodeIt = f->interlacedNodesElemIterator();
6845 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
6846 nodeIt = e->interlacedNodesElemIterator();
6848 while ( nodeIt->more() )
6850 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
6851 xyz.push_back( SMESH_TNodeXYZ(node) );
6852 nodeList.push_back(node);
6855 int i, nbNodes = element->NbNodes();
6857 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
6859 // compute face normal
6860 gp_Vec faceNorm(0,0,0);
6861 xyz.push_back( xyz.front() );
6862 nodeList.push_back( nodeList.front() );
6863 for ( i = 0; i < nbNodes; ++i )
6865 gp_Vec edge1( xyz[i+1], xyz[i]);
6866 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
6867 faceNorm += edge1 ^ edge2;
6869 double normSize = faceNorm.Magnitude();
6870 if ( normSize <= tol )
6872 // degenerated face: point is out if it is out of all face edges
6873 for ( i = 0; i < nbNodes; ++i )
6875 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
6876 if ( !isOut( &edge, point, tol ))
6881 faceNorm /= normSize;
6883 // check if the point lays on face plane
6884 gp_Vec n2p( xyz[0], point );
6885 if ( fabs( n2p * faceNorm ) > tol )
6886 return true; // not on face plane
6888 // check if point is out of face boundary:
6889 // define it by closest transition of a ray point->infinity through face boundary
6890 // on the face plane.
6891 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
6892 // to find intersections of the ray with the boundary.
6894 gp_Vec plnNorm = ray ^ faceNorm;
6895 normSize = plnNorm.Magnitude();
6896 if ( normSize <= tol ) return false; // point coincides with the first node
6897 plnNorm /= normSize;
6898 // for each node of the face, compute its signed distance to the plane
6899 vector<double> dist( nbNodes + 1);
6900 for ( i = 0; i < nbNodes; ++i )
6902 gp_Vec n2p( xyz[i], point );
6903 dist[i] = n2p * plnNorm;
6905 dist.back() = dist.front();
6906 // find the closest intersection
6908 double rClosest, distClosest = 1e100;;
6910 for ( i = 0; i < nbNodes; ++i )
6913 if ( fabs( dist[i]) < tol )
6915 else if ( fabs( dist[i+1]) < tol )
6917 else if ( dist[i] * dist[i+1] < 0 )
6918 r = dist[i] / ( dist[i] - dist[i+1] );
6920 continue; // no intersection
6921 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
6922 gp_Vec p2int ( point, pInt);
6923 if ( p2int * ray > -tol ) // right half-space
6925 double intDist = p2int.SquareMagnitude();
6926 if ( intDist < distClosest )
6931 distClosest = intDist;
6936 return true; // no intesections - out
6938 // analyse transition
6939 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
6940 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
6941 gp_Vec p2int ( point, pClosest );
6942 bool out = (edgeNorm * p2int) < -tol;
6943 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
6946 // ray pass through a face node; analyze transition through an adjacent edge
6947 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
6948 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
6949 gp_Vec edgeAdjacent( p1, p2 );
6950 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
6951 bool out2 = (edgeNorm2 * p2int) < -tol;
6953 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
6954 return covexCorner ? (out || out2) : (out && out2);
6956 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
6958 // point is out of edge if it is NOT ON any straight part of edge
6959 // (we consider quadratic edge as being composed of two straight parts)
6960 for ( i = 1; i < nbNodes; ++i )
6962 gp_Vec edge( xyz[i-1], xyz[i]);
6963 gp_Vec n1p ( xyz[i-1], point);
6964 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
6967 gp_Vec n2p( xyz[i], point );
6968 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
6970 return false; // point is ON this part
6974 // Node or 0D element -------------------------------------------------------------------------
6976 gp_Vec n2p ( xyz[0], point );
6977 return n2p.Magnitude() <= tol;
6982 //=======================================================================
6983 //function : SimplifyFace
6985 //=======================================================================
6986 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
6987 vector<const SMDS_MeshNode *>& poly_nodes,
6988 vector<int>& quantities) const
6990 int nbNodes = faceNodes.size();
6995 set<const SMDS_MeshNode*> nodeSet;
6997 // get simple seq of nodes
6998 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6999 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7000 int iSimple = 0, nbUnique = 0;
7002 simpleNodes[iSimple++] = faceNodes[0];
7004 for (int iCur = 1; iCur < nbNodes; iCur++) {
7005 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7006 simpleNodes[iSimple++] = faceNodes[iCur];
7007 if (nodeSet.insert( faceNodes[iCur] ).second)
7011 int nbSimple = iSimple;
7012 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7022 bool foundLoop = (nbSimple > nbUnique);
7025 set<const SMDS_MeshNode*> loopSet;
7026 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7027 const SMDS_MeshNode* n = simpleNodes[iSimple];
7028 if (!loopSet.insert( n ).second) {
7032 int iC = 0, curLast = iSimple;
7033 for (; iC < curLast; iC++) {
7034 if (simpleNodes[iC] == n) break;
7036 int loopLen = curLast - iC;
7038 // create sub-element
7040 quantities.push_back(loopLen);
7041 for (; iC < curLast; iC++) {
7042 poly_nodes.push_back(simpleNodes[iC]);
7045 // shift the rest nodes (place from the first loop position)
7046 for (iC = curLast + 1; iC < nbSimple; iC++) {
7047 simpleNodes[iC - loopLen] = simpleNodes[iC];
7049 nbSimple -= loopLen;
7052 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7053 } // while (foundLoop)
7057 quantities.push_back(iSimple);
7058 for (int i = 0; i < iSimple; i++)
7059 poly_nodes.push_back(simpleNodes[i]);
7065 //=======================================================================
7066 //function : MergeNodes
7067 //purpose : In each group, the cdr of nodes are substituted by the first one
7069 //=======================================================================
7071 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7073 MESSAGE("MergeNodes");
7074 myLastCreatedElems.Clear();
7075 myLastCreatedNodes.Clear();
7077 SMESHDS_Mesh* aMesh = GetMeshDS();
7079 TNodeNodeMap nodeNodeMap; // node to replace - new node
7080 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7081 list< int > rmElemIds, rmNodeIds;
7083 // Fill nodeNodeMap and elems
7085 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7086 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7087 list<const SMDS_MeshNode*>& nodes = *grIt;
7088 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7089 const SMDS_MeshNode* nToKeep = *nIt;
7090 //MESSAGE("node to keep " << nToKeep->GetID());
7091 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7092 const SMDS_MeshNode* nToRemove = *nIt;
7093 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7094 if ( nToRemove != nToKeep ) {
7095 //MESSAGE(" node to remove " << nToRemove->GetID());
7096 rmNodeIds.push_back( nToRemove->GetID() );
7097 AddToSameGroups( nToKeep, nToRemove, aMesh );
7100 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7101 while ( invElemIt->more() ) {
7102 const SMDS_MeshElement* elem = invElemIt->next();
7107 // Change element nodes or remove an element
7109 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7110 for ( ; eIt != elems.end(); eIt++ ) {
7111 const SMDS_MeshElement* elem = *eIt;
7112 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7113 int nbNodes = elem->NbNodes();
7114 int aShapeId = FindShape( elem );
7116 set<const SMDS_MeshNode*> nodeSet;
7117 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7118 int iUnique = 0, iCur = 0, nbRepl = 0;
7119 vector<int> iRepl( nbNodes );
7121 // get new seq of nodes
7122 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7123 while ( itN->more() ) {
7124 const SMDS_MeshNode* n =
7125 static_cast<const SMDS_MeshNode*>( itN->next() );
7127 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7128 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7130 // BUG 0020185: begin
7132 bool stopRecur = false;
7133 set<const SMDS_MeshNode*> nodesRecur;
7134 nodesRecur.insert(n);
7135 while (!stopRecur) {
7136 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7137 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7138 n = (*nnIt_i).second;
7139 if (!nodesRecur.insert(n).second) {
7140 // error: recursive dependancy
7150 curNodes[ iCur ] = n;
7151 bool isUnique = nodeSet.insert( n ).second;
7153 uniqueNodes[ iUnique++ ] = n;
7155 iRepl[ nbRepl++ ] = iCur;
7159 // Analyse element topology after replacement
7162 int nbUniqueNodes = nodeSet.size();
7163 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7164 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7165 // Polygons and Polyhedral volumes
7166 if (elem->IsPoly()) {
7168 if (elem->GetType() == SMDSAbs_Face) {
7170 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7172 for (; inode < nbNodes; inode++) {
7173 face_nodes[inode] = curNodes[inode];
7176 vector<const SMDS_MeshNode *> polygons_nodes;
7177 vector<int> quantities;
7178 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7181 for (int iface = 0; iface < nbNew; iface++) {
7182 int nbNodes = quantities[iface];
7183 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7184 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7185 poly_nodes[ii] = polygons_nodes[inode];
7187 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7188 myLastCreatedElems.Append(newElem);
7190 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7193 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7194 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7195 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7197 if (nbNew > 0) quid = nbNew - 1;
7198 vector<int> newquant(quantities.begin()+quid, quantities.end());
7199 const SMDS_MeshElement* newElem = 0;
7200 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7201 myLastCreatedElems.Append(newElem);
7202 if ( aShapeId && newElem )
7203 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7204 rmElemIds.push_back(elem->GetID());
7207 rmElemIds.push_back(elem->GetID());
7211 else if (elem->GetType() == SMDSAbs_Volume) {
7212 // Polyhedral volume
7213 if (nbUniqueNodes < 4) {
7214 rmElemIds.push_back(elem->GetID());
7217 // each face has to be analyzed in order to check volume validity
7218 const SMDS_VtkVolume* aPolyedre =
7219 dynamic_cast<const SMDS_VtkVolume*>( elem );
7221 int nbFaces = aPolyedre->NbFaces();
7223 vector<const SMDS_MeshNode *> poly_nodes;
7224 vector<int> quantities;
7226 for (int iface = 1; iface <= nbFaces; iface++) {
7227 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7228 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7230 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7231 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7232 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7233 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7234 faceNode = (*nnIt).second;
7236 faceNodes[inode - 1] = faceNode;
7239 SimplifyFace(faceNodes, poly_nodes, quantities);
7242 if (quantities.size() > 3) {
7243 // to be done: remove coincident faces
7246 if (quantities.size() > 3)
7248 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7249 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7250 const SMDS_MeshElement* newElem = 0;
7251 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7252 myLastCreatedElems.Append(newElem);
7253 if ( aShapeId && newElem )
7254 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7255 rmElemIds.push_back(elem->GetID());
7259 rmElemIds.push_back(elem->GetID());
7270 // TODO not all the possible cases are solved. Find something more generic?
7271 switch ( nbNodes ) {
7272 case 2: ///////////////////////////////////// EDGE
7273 isOk = false; break;
7274 case 3: ///////////////////////////////////// TRIANGLE
7275 isOk = false; break;
7277 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7279 else { //////////////////////////////////// QUADRANGLE
7280 if ( nbUniqueNodes < 3 )
7282 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7283 isOk = false; // opposite nodes stick
7284 //MESSAGE("isOk " << isOk);
7287 case 6: ///////////////////////////////////// PENTAHEDRON
7288 if ( nbUniqueNodes == 4 ) {
7289 // ---------------------------------> tetrahedron
7291 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7292 // all top nodes stick: reverse a bottom
7293 uniqueNodes[ 0 ] = curNodes [ 1 ];
7294 uniqueNodes[ 1 ] = curNodes [ 0 ];
7296 else if (nbRepl == 3 &&
7297 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7298 // all bottom nodes stick: set a top before
7299 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7300 uniqueNodes[ 0 ] = curNodes [ 3 ];
7301 uniqueNodes[ 1 ] = curNodes [ 4 ];
7302 uniqueNodes[ 2 ] = curNodes [ 5 ];
7304 else if (nbRepl == 4 &&
7305 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7306 // a lateral face turns into a line: reverse a bottom
7307 uniqueNodes[ 0 ] = curNodes [ 1 ];
7308 uniqueNodes[ 1 ] = curNodes [ 0 ];
7313 else if ( nbUniqueNodes == 5 ) {
7314 // PENTAHEDRON --------------------> 2 tetrahedrons
7315 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7316 // a bottom node sticks with a linked top one
7318 SMDS_MeshElement* newElem =
7319 aMesh->AddVolume(curNodes[ 3 ],
7322 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7323 myLastCreatedElems.Append(newElem);
7325 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7326 // 2. : reverse a bottom
7327 uniqueNodes[ 0 ] = curNodes [ 1 ];
7328 uniqueNodes[ 1 ] = curNodes [ 0 ];
7338 if(elem->IsQuadratic()) { // Quadratic quadrangle
7350 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7353 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7355 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7356 uniqueNodes[0] = curNodes[0];
7357 uniqueNodes[1] = curNodes[2];
7358 uniqueNodes[2] = curNodes[3];
7359 uniqueNodes[3] = curNodes[5];
7360 uniqueNodes[4] = curNodes[6];
7361 uniqueNodes[5] = curNodes[7];
7364 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7365 uniqueNodes[0] = curNodes[0];
7366 uniqueNodes[1] = curNodes[1];
7367 uniqueNodes[2] = curNodes[2];
7368 uniqueNodes[3] = curNodes[4];
7369 uniqueNodes[4] = curNodes[5];
7370 uniqueNodes[5] = curNodes[6];
7373 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7374 uniqueNodes[0] = curNodes[1];
7375 uniqueNodes[1] = curNodes[2];
7376 uniqueNodes[2] = curNodes[3];
7377 uniqueNodes[3] = curNodes[5];
7378 uniqueNodes[4] = curNodes[6];
7379 uniqueNodes[5] = curNodes[0];
7382 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7383 uniqueNodes[0] = curNodes[0];
7384 uniqueNodes[1] = curNodes[1];
7385 uniqueNodes[2] = curNodes[3];
7386 uniqueNodes[3] = curNodes[4];
7387 uniqueNodes[4] = curNodes[6];
7388 uniqueNodes[5] = curNodes[7];
7391 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7392 uniqueNodes[0] = curNodes[0];
7393 uniqueNodes[1] = curNodes[2];
7394 uniqueNodes[2] = curNodes[3];
7395 uniqueNodes[3] = curNodes[1];
7396 uniqueNodes[4] = curNodes[6];
7397 uniqueNodes[5] = curNodes[7];
7400 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7401 uniqueNodes[0] = curNodes[0];
7402 uniqueNodes[1] = curNodes[1];
7403 uniqueNodes[2] = curNodes[2];
7404 uniqueNodes[3] = curNodes[4];
7405 uniqueNodes[4] = curNodes[5];
7406 uniqueNodes[5] = curNodes[7];
7409 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7410 uniqueNodes[0] = curNodes[0];
7411 uniqueNodes[1] = curNodes[1];
7412 uniqueNodes[2] = curNodes[3];
7413 uniqueNodes[3] = curNodes[4];
7414 uniqueNodes[4] = curNodes[2];
7415 uniqueNodes[5] = curNodes[7];
7418 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7419 uniqueNodes[0] = curNodes[0];
7420 uniqueNodes[1] = curNodes[1];
7421 uniqueNodes[2] = curNodes[2];
7422 uniqueNodes[3] = curNodes[4];
7423 uniqueNodes[4] = curNodes[5];
7424 uniqueNodes[5] = curNodes[3];
7429 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7432 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7436 //////////////////////////////////// HEXAHEDRON
7438 SMDS_VolumeTool hexa (elem);
7439 hexa.SetExternalNormal();
7440 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7441 //////////////////////// HEX ---> 1 tetrahedron
7442 for ( int iFace = 0; iFace < 6; iFace++ ) {
7443 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7444 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7445 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7446 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7447 // one face turns into a point ...
7448 int iOppFace = hexa.GetOppFaceIndex( iFace );
7449 ind = hexa.GetFaceNodesIndices( iOppFace );
7451 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7452 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7455 if ( nbStick == 1 ) {
7456 // ... and the opposite one - into a triangle.
7458 ind = hexa.GetFaceNodesIndices( iFace );
7459 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7466 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7467 //////////////////////// HEX ---> 1 prism
7468 int nbTria = 0, iTria[3];
7469 const int *ind; // indices of face nodes
7470 // look for triangular faces
7471 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7472 ind = hexa.GetFaceNodesIndices( iFace );
7473 TIDSortedNodeSet faceNodes;
7474 for ( iCur = 0; iCur < 4; iCur++ )
7475 faceNodes.insert( curNodes[ind[iCur]] );
7476 if ( faceNodes.size() == 3 )
7477 iTria[ nbTria++ ] = iFace;
7479 // check if triangles are opposite
7480 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7483 // set nodes of the bottom triangle
7484 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7486 for ( iCur = 0; iCur < 4; iCur++ )
7487 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7488 indB.push_back( ind[iCur] );
7489 if ( !hexa.IsForward() )
7490 std::swap( indB[0], indB[2] );
7491 for ( iCur = 0; iCur < 3; iCur++ )
7492 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7493 // set nodes of the top triangle
7494 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7495 for ( iCur = 0; iCur < 3; ++iCur )
7496 for ( int j = 0; j < 4; ++j )
7497 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7499 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7505 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7506 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7507 for ( int iFace = 0; iFace < 6; iFace++ ) {
7508 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7509 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7510 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7511 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7512 // one face turns into a point ...
7513 int iOppFace = hexa.GetOppFaceIndex( iFace );
7514 ind = hexa.GetFaceNodesIndices( iOppFace );
7516 iUnique = 2; // reverse a tetrahedron 1 bottom
7517 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7518 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7520 else if ( iUnique >= 0 )
7521 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7523 if ( nbStick == 0 ) {
7524 // ... and the opposite one is a quadrangle
7526 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7527 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7530 SMDS_MeshElement* newElem =
7531 aMesh->AddVolume(curNodes[ind[ 0 ]],
7534 curNodes[indTop[ 0 ]]);
7535 myLastCreatedElems.Append(newElem);
7537 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7544 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7545 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7546 // find indices of quad and tri faces
7547 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7548 for ( iFace = 0; iFace < 6; iFace++ ) {
7549 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7551 for ( iCur = 0; iCur < 4; iCur++ )
7552 nodeSet.insert( curNodes[ind[ iCur ]] );
7553 nbUniqueNodes = nodeSet.size();
7554 if ( nbUniqueNodes == 3 )
7555 iTriFace[ nbTri++ ] = iFace;
7556 else if ( nbUniqueNodes == 4 )
7557 iQuadFace[ nbQuad++ ] = iFace;
7559 if (nbQuad == 2 && nbTri == 4 &&
7560 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7561 // 2 opposite quadrangles stuck with a diagonal;
7562 // sample groups of merged indices: (0-4)(2-6)
7563 // --------------------------------------------> 2 tetrahedrons
7564 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7565 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7566 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7567 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7568 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7569 // stuck with 0-2 diagonal
7577 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7578 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7579 // stuck with 1-3 diagonal
7591 uniqueNodes[ 0 ] = curNodes [ i0 ];
7592 uniqueNodes[ 1 ] = curNodes [ i1d ];
7593 uniqueNodes[ 2 ] = curNodes [ i3d ];
7594 uniqueNodes[ 3 ] = curNodes [ i0t ];
7597 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7601 myLastCreatedElems.Append(newElem);
7603 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7606 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7607 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7608 // --------------------------------------------> prism
7609 // find 2 opposite triangles
7611 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7612 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7613 // find indices of kept and replaced nodes
7614 // and fill unique nodes of 2 opposite triangles
7615 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7616 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7617 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7618 // fill unique nodes
7621 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7622 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7623 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7625 // iCur of a linked node of the opposite face (make normals co-directed):
7626 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7627 // check that correspondent corners of triangles are linked
7628 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7631 uniqueNodes[ iUnique ] = n;
7632 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7641 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7644 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7651 } // switch ( nbNodes )
7653 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7655 if ( isOk ) { // the elem remains valid after sticking nodes
7656 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7658 // Change nodes of polyedre
7659 const SMDS_VtkVolume* aPolyedre =
7660 dynamic_cast<const SMDS_VtkVolume*>( elem );
7662 int nbFaces = aPolyedre->NbFaces();
7664 vector<const SMDS_MeshNode *> poly_nodes;
7665 vector<int> quantities (nbFaces);
7667 for (int iface = 1; iface <= nbFaces; iface++) {
7668 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7669 quantities[iface - 1] = nbFaceNodes;
7671 for (inode = 1; inode <= nbFaceNodes; inode++) {
7672 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7674 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7675 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7676 curNode = (*nnIt).second;
7678 poly_nodes.push_back(curNode);
7681 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7684 else // replace non-polyhedron elements
7686 const SMDSAbs_ElementType etyp = elem->GetType();
7687 const int elemId = elem->GetID();
7688 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7689 uniqueNodes.resize(nbUniqueNodes);
7691 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7693 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7694 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7695 if ( sm && newElem )
7696 sm->AddElement( newElem );
7697 if ( elem != newElem )
7698 ReplaceElemInGroups( elem, newElem, aMesh );
7702 // Remove invalid regular element or invalid polygon
7703 rmElemIds.push_back( elem->GetID() );
7706 } // loop on elements
7708 // Remove bad elements, then equal nodes (order important)
7710 Remove( rmElemIds, false );
7711 Remove( rmNodeIds, true );
7716 // ========================================================
7717 // class : SortableElement
7718 // purpose : allow sorting elements basing on their nodes
7719 // ========================================================
7720 class SortableElement : public set <const SMDS_MeshElement*>
7724 SortableElement( const SMDS_MeshElement* theElem )
7727 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7728 while ( nodeIt->more() )
7729 this->insert( nodeIt->next() );
7732 const SMDS_MeshElement* Get() const
7735 void Set(const SMDS_MeshElement* e) const
7740 mutable const SMDS_MeshElement* myElem;
7743 //=======================================================================
7744 //function : FindEqualElements
7745 //purpose : Return list of group of elements built on the same nodes.
7746 // Search among theElements or in the whole mesh if theElements is empty
7747 //=======================================================================
7748 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
7749 TListOfListOfElementsID & theGroupsOfElementsID)
7751 myLastCreatedElems.Clear();
7752 myLastCreatedNodes.Clear();
7754 typedef set<const SMDS_MeshElement*> TElemsSet;
7755 typedef map< SortableElement, int > TMapOfNodeSet;
7756 typedef list<int> TGroupOfElems;
7759 if ( theElements.empty() )
7760 { // get all elements in the mesh
7761 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7762 while ( eIt->more() )
7763 elems.insert( elems.end(), eIt->next());
7766 elems = theElements;
7768 vector< TGroupOfElems > arrayOfGroups;
7769 TGroupOfElems groupOfElems;
7770 TMapOfNodeSet mapOfNodeSet;
7772 TElemsSet::iterator elemIt = elems.begin();
7773 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
7774 const SMDS_MeshElement* curElem = *elemIt;
7775 SortableElement SE(curElem);
7778 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7779 if( !(pp.second) ) {
7780 TMapOfNodeSet::iterator& itSE = pp.first;
7781 ind = (*itSE).second;
7782 arrayOfGroups[ind].push_back(curElem->GetID());
7785 groupOfElems.clear();
7786 groupOfElems.push_back(curElem->GetID());
7787 arrayOfGroups.push_back(groupOfElems);
7792 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7793 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7794 groupOfElems = *groupIt;
7795 if ( groupOfElems.size() > 1 ) {
7796 groupOfElems.sort();
7797 theGroupsOfElementsID.push_back(groupOfElems);
7802 //=======================================================================
7803 //function : MergeElements
7804 //purpose : In each given group, substitute all elements by the first one.
7805 //=======================================================================
7807 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7809 myLastCreatedElems.Clear();
7810 myLastCreatedNodes.Clear();
7812 typedef list<int> TListOfIDs;
7813 TListOfIDs rmElemIds; // IDs of elems to remove
7815 SMESHDS_Mesh* aMesh = GetMeshDS();
7817 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7818 while ( groupsIt != theGroupsOfElementsID.end() ) {
7819 TListOfIDs& aGroupOfElemID = *groupsIt;
7820 aGroupOfElemID.sort();
7821 int elemIDToKeep = aGroupOfElemID.front();
7822 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7823 aGroupOfElemID.pop_front();
7824 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7825 while ( idIt != aGroupOfElemID.end() ) {
7826 int elemIDToRemove = *idIt;
7827 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7828 // add the kept element in groups of removed one (PAL15188)
7829 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7830 rmElemIds.push_back( elemIDToRemove );
7836 Remove( rmElemIds, false );
7839 //=======================================================================
7840 //function : MergeEqualElements
7841 //purpose : Remove all but one of elements built on the same nodes.
7842 //=======================================================================
7844 void SMESH_MeshEditor::MergeEqualElements()
7846 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
7847 to merge equal elements in the whole mesh */
7848 TListOfListOfElementsID aGroupsOfElementsID;
7849 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7850 MergeElements(aGroupsOfElementsID);
7853 //=======================================================================
7854 //function : FindFaceInSet
7855 //purpose : Return a face having linked nodes n1 and n2 and which is
7856 // - not in avoidSet,
7857 // - in elemSet provided that !elemSet.empty()
7858 // i1 and i2 optionally returns indices of n1 and n2
7859 //=======================================================================
7861 const SMDS_MeshElement*
7862 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
7863 const SMDS_MeshNode* n2,
7864 const TIDSortedElemSet& elemSet,
7865 const TIDSortedElemSet& avoidSet,
7871 const SMDS_MeshElement* face = 0;
7873 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
7874 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
7875 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
7877 //MESSAGE("in while ( invElemIt->more() && !face )");
7878 const SMDS_MeshElement* elem = invElemIt->next();
7879 if (avoidSet.count( elem ))
7881 if ( !elemSet.empty() && !elemSet.count( elem ))
7884 i1 = elem->GetNodeIndex( n1 );
7885 // find a n2 linked to n1
7886 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
7887 for ( int di = -1; di < 2 && !face; di += 2 )
7889 i2 = (i1+di+nbN) % nbN;
7890 if ( elem->GetNode( i2 ) == n2 )
7893 if ( !face && elem->IsQuadratic())
7895 // analysis for quadratic elements using all nodes
7896 const SMDS_VtkFace* F =
7897 dynamic_cast<const SMDS_VtkFace*>(elem);
7898 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7899 // use special nodes iterator
7900 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7901 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
7902 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
7904 const SMDS_MeshNode* n = cast2Node( anIter->next() );
7905 if ( n1 == prevN && n2 == n )
7909 else if ( n2 == prevN && n1 == n )
7911 face = elem; swap( i1, i2 );
7917 if ( n1ind ) *n1ind = i1;
7918 if ( n2ind ) *n2ind = i2;
7922 //=======================================================================
7923 //function : findAdjacentFace
7925 //=======================================================================
7927 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7928 const SMDS_MeshNode* n2,
7929 const SMDS_MeshElement* elem)
7931 TIDSortedElemSet elemSet, avoidSet;
7933 avoidSet.insert ( elem );
7934 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
7937 //=======================================================================
7938 //function : FindFreeBorder
7940 //=======================================================================
7942 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7944 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7945 const SMDS_MeshNode* theSecondNode,
7946 const SMDS_MeshNode* theLastNode,
7947 list< const SMDS_MeshNode* > & theNodes,
7948 list< const SMDS_MeshElement* >& theFaces)
7950 if ( !theFirstNode || !theSecondNode )
7952 // find border face between theFirstNode and theSecondNode
7953 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7957 theFaces.push_back( curElem );
7958 theNodes.push_back( theFirstNode );
7959 theNodes.push_back( theSecondNode );
7961 //vector<const SMDS_MeshNode*> nodes;
7962 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7963 TIDSortedElemSet foundElems;
7964 bool needTheLast = ( theLastNode != 0 );
7966 while ( nStart != theLastNode ) {
7967 if ( nStart == theFirstNode )
7968 return !needTheLast;
7970 // find all free border faces sharing form nStart
7972 list< const SMDS_MeshElement* > curElemList;
7973 list< const SMDS_MeshNode* > nStartList;
7974 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7975 while ( invElemIt->more() ) {
7976 const SMDS_MeshElement* e = invElemIt->next();
7977 if ( e == curElem || foundElems.insert( e ).second ) {
7979 int iNode = 0, nbNodes = e->NbNodes();
7980 //const SMDS_MeshNode* nodes[nbNodes+1];
7981 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7983 if(e->IsQuadratic()) {
7984 const SMDS_VtkFace* F =
7985 dynamic_cast<const SMDS_VtkFace*>(e);
7986 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7987 // use special nodes iterator
7988 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7989 while( anIter->more() ) {
7990 nodes[ iNode++ ] = cast2Node(anIter->next());
7994 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7995 while ( nIt->more() )
7996 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7998 nodes[ iNode ] = nodes[ 0 ];
8000 for ( iNode = 0; iNode < nbNodes; iNode++ )
8001 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8002 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8003 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8005 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8006 curElemList.push_back( e );
8010 // analyse the found
8012 int nbNewBorders = curElemList.size();
8013 if ( nbNewBorders == 0 ) {
8014 // no free border furthermore
8015 return !needTheLast;
8017 else if ( nbNewBorders == 1 ) {
8018 // one more element found
8020 nStart = nStartList.front();
8021 curElem = curElemList.front();
8022 theFaces.push_back( curElem );
8023 theNodes.push_back( nStart );
8026 // several continuations found
8027 list< const SMDS_MeshElement* >::iterator curElemIt;
8028 list< const SMDS_MeshNode* >::iterator nStartIt;
8029 // check if one of them reached the last node
8030 if ( needTheLast ) {
8031 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8032 curElemIt!= curElemList.end();
8033 curElemIt++, nStartIt++ )
8034 if ( *nStartIt == theLastNode ) {
8035 theFaces.push_back( *curElemIt );
8036 theNodes.push_back( *nStartIt );
8040 // find the best free border by the continuations
8041 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8042 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8043 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8044 curElemIt!= curElemList.end();
8045 curElemIt++, nStartIt++ )
8047 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8048 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8049 // find one more free border
8050 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8054 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8055 // choice: clear a worse one
8056 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8057 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8058 contNodes[ iWorse ].clear();
8059 contFaces[ iWorse ].clear();
8062 if ( contNodes[0].empty() && contNodes[1].empty() )
8065 // append the best free border
8066 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8067 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8068 theNodes.pop_back(); // remove nIgnore
8069 theNodes.pop_back(); // remove nStart
8070 theFaces.pop_back(); // remove curElem
8071 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8072 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8073 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8074 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8077 } // several continuations found
8078 } // while ( nStart != theLastNode )
8083 //=======================================================================
8084 //function : CheckFreeBorderNodes
8085 //purpose : Return true if the tree nodes are on a free border
8086 //=======================================================================
8088 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8089 const SMDS_MeshNode* theNode2,
8090 const SMDS_MeshNode* theNode3)
8092 list< const SMDS_MeshNode* > nodes;
8093 list< const SMDS_MeshElement* > faces;
8094 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8097 //=======================================================================
8098 //function : SewFreeBorder
8100 //=======================================================================
8102 SMESH_MeshEditor::Sew_Error
8103 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8104 const SMDS_MeshNode* theBordSecondNode,
8105 const SMDS_MeshNode* theBordLastNode,
8106 const SMDS_MeshNode* theSideFirstNode,
8107 const SMDS_MeshNode* theSideSecondNode,
8108 const SMDS_MeshNode* theSideThirdNode,
8109 const bool theSideIsFreeBorder,
8110 const bool toCreatePolygons,
8111 const bool toCreatePolyedrs)
8113 myLastCreatedElems.Clear();
8114 myLastCreatedNodes.Clear();
8116 MESSAGE("::SewFreeBorder()");
8117 Sew_Error aResult = SEW_OK;
8119 // ====================================
8120 // find side nodes and elements
8121 // ====================================
8123 list< const SMDS_MeshNode* > nSide[ 2 ];
8124 list< const SMDS_MeshElement* > eSide[ 2 ];
8125 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8126 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8130 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8131 nSide[0], eSide[0])) {
8132 MESSAGE(" Free Border 1 not found " );
8133 aResult = SEW_BORDER1_NOT_FOUND;
8135 if (theSideIsFreeBorder) {
8138 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8139 nSide[1], eSide[1])) {
8140 MESSAGE(" Free Border 2 not found " );
8141 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8144 if ( aResult != SEW_OK )
8147 if (!theSideIsFreeBorder) {
8151 // -------------------------------------------------------------------------
8153 // 1. If nodes to merge are not coincident, move nodes of the free border
8154 // from the coord sys defined by the direction from the first to last
8155 // nodes of the border to the correspondent sys of the side 2
8156 // 2. On the side 2, find the links most co-directed with the correspondent
8157 // links of the free border
8158 // -------------------------------------------------------------------------
8160 // 1. Since sewing may break if there are volumes to split on the side 2,
8161 // we wont move nodes but just compute new coordinates for them
8162 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8163 TNodeXYZMap nBordXYZ;
8164 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8165 list< const SMDS_MeshNode* >::iterator nBordIt;
8167 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8168 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8169 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8170 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8171 double tol2 = 1.e-8;
8172 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8173 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8174 // Need node movement.
8176 // find X and Z axes to create trsf
8177 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8179 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8181 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8184 gp_Ax3 toBordAx( Pb1, Zb, X );
8185 gp_Ax3 fromSideAx( Ps1, Zs, X );
8186 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8188 gp_Trsf toBordSys, fromSide2Sys;
8189 toBordSys.SetTransformation( toBordAx );
8190 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8191 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8194 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8195 const SMDS_MeshNode* n = *nBordIt;
8196 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8197 toBordSys.Transforms( xyz );
8198 fromSide2Sys.Transforms( xyz );
8199 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8203 // just insert nodes XYZ in the nBordXYZ map
8204 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8205 const SMDS_MeshNode* n = *nBordIt;
8206 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8210 // 2. On the side 2, find the links most co-directed with the correspondent
8211 // links of the free border
8213 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8214 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8215 sideNodes.push_back( theSideFirstNode );
8217 bool hasVolumes = false;
8218 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8219 set<long> foundSideLinkIDs, checkedLinkIDs;
8220 SMDS_VolumeTool volume;
8221 //const SMDS_MeshNode* faceNodes[ 4 ];
8223 const SMDS_MeshNode* sideNode;
8224 const SMDS_MeshElement* sideElem;
8225 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8226 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8227 nBordIt = bordNodes.begin();
8229 // border node position and border link direction to compare with
8230 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8231 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8232 // choose next side node by link direction or by closeness to
8233 // the current border node:
8234 bool searchByDir = ( *nBordIt != theBordLastNode );
8236 // find the next node on the Side 2
8238 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8240 checkedLinkIDs.clear();
8241 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8243 // loop on inverse elements of current node (prevSideNode) on the Side 2
8244 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8245 while ( invElemIt->more() )
8247 const SMDS_MeshElement* elem = invElemIt->next();
8248 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8249 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8250 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8251 bool isVolume = volume.Set( elem );
8252 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8253 if ( isVolume ) // --volume
8255 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8256 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8257 if(elem->IsQuadratic()) {
8258 const SMDS_VtkFace* F =
8259 dynamic_cast<const SMDS_VtkFace*>(elem);
8260 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8261 // use special nodes iterator
8262 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8263 while( anIter->more() ) {
8264 nodes[ iNode ] = cast2Node(anIter->next());
8265 if ( nodes[ iNode++ ] == prevSideNode )
8266 iPrevNode = iNode - 1;
8270 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8271 while ( nIt->more() ) {
8272 nodes[ iNode ] = cast2Node( nIt->next() );
8273 if ( nodes[ iNode++ ] == prevSideNode )
8274 iPrevNode = iNode - 1;
8277 // there are 2 links to check
8282 // loop on links, to be precise, on the second node of links
8283 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8284 const SMDS_MeshNode* n = nodes[ iNode ];
8286 if ( !volume.IsLinked( n, prevSideNode ))
8290 if ( iNode ) // a node before prevSideNode
8291 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8292 else // a node after prevSideNode
8293 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8295 // check if this link was already used
8296 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8297 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8298 if (!isJustChecked &&
8299 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8301 // test a link geometrically
8302 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8303 bool linkIsBetter = false;
8304 double dot = 0.0, dist = 0.0;
8305 if ( searchByDir ) { // choose most co-directed link
8306 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8307 linkIsBetter = ( dot > maxDot );
8309 else { // choose link with the node closest to bordPos
8310 dist = ( nextXYZ - bordPos ).SquareModulus();
8311 linkIsBetter = ( dist < minDist );
8313 if ( linkIsBetter ) {
8322 } // loop on inverse elements of prevSideNode
8325 MESSAGE(" Cant find path by links of the Side 2 ");
8326 return SEW_BAD_SIDE_NODES;
8328 sideNodes.push_back( sideNode );
8329 sideElems.push_back( sideElem );
8330 foundSideLinkIDs.insert ( linkID );
8331 prevSideNode = sideNode;
8333 if ( *nBordIt == theBordLastNode )
8334 searchByDir = false;
8336 // find the next border link to compare with
8337 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8338 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8339 // move to next border node if sideNode is before forward border node (bordPos)
8340 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8341 prevBordNode = *nBordIt;
8343 bordPos = nBordXYZ[ *nBordIt ];
8344 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8345 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8349 while ( sideNode != theSideSecondNode );
8351 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8352 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8353 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8355 } // end nodes search on the side 2
8357 // ============================
8358 // sew the border to the side 2
8359 // ============================
8361 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8362 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8364 TListOfListOfNodes nodeGroupsToMerge;
8365 if ( nbNodes[0] == nbNodes[1] ||
8366 ( theSideIsFreeBorder && !theSideThirdNode)) {
8368 // all nodes are to be merged
8370 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8371 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8372 nIt[0]++, nIt[1]++ )
8374 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8375 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8376 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8381 // insert new nodes into the border and the side to get equal nb of segments
8383 // get normalized parameters of nodes on the borders
8384 //double param[ 2 ][ maxNbNodes ];
8386 param[0] = new double [ maxNbNodes ];
8387 param[1] = new double [ maxNbNodes ];
8389 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8390 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8391 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8392 const SMDS_MeshNode* nPrev = *nIt;
8393 double bordLength = 0;
8394 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8395 const SMDS_MeshNode* nCur = *nIt;
8396 gp_XYZ segment (nCur->X() - nPrev->X(),
8397 nCur->Y() - nPrev->Y(),
8398 nCur->Z() - nPrev->Z());
8399 double segmentLen = segment.Modulus();
8400 bordLength += segmentLen;
8401 param[ iBord ][ iNode ] = bordLength;
8404 // normalize within [0,1]
8405 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8406 param[ iBord ][ iNode ] /= bordLength;
8410 // loop on border segments
8411 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8412 int i[ 2 ] = { 0, 0 };
8413 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8414 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8416 TElemOfNodeListMap insertMap;
8417 TElemOfNodeListMap::iterator insertMapIt;
8419 // key: elem to insert nodes into
8420 // value: 2 nodes to insert between + nodes to be inserted
8422 bool next[ 2 ] = { false, false };
8424 // find min adjacent segment length after sewing
8425 double nextParam = 10., prevParam = 0;
8426 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8427 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8428 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8429 if ( i[ iBord ] > 0 )
8430 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8432 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8433 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8434 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8436 // choose to insert or to merge nodes
8437 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8438 if ( Abs( du ) <= minSegLen * 0.2 ) {
8441 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8442 const SMDS_MeshNode* n0 = *nIt[0];
8443 const SMDS_MeshNode* n1 = *nIt[1];
8444 nodeGroupsToMerge.back().push_back( n1 );
8445 nodeGroupsToMerge.back().push_back( n0 );
8446 // position of node of the border changes due to merge
8447 param[ 0 ][ i[0] ] += du;
8448 // move n1 for the sake of elem shape evaluation during insertion.
8449 // n1 will be removed by MergeNodes() anyway
8450 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8451 next[0] = next[1] = true;
8456 int intoBord = ( du < 0 ) ? 0 : 1;
8457 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8458 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8459 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8460 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8461 if ( intoBord == 1 ) {
8462 // move node of the border to be on a link of elem of the side
8463 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8464 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8465 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8466 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8467 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8469 insertMapIt = insertMap.find( elem );
8470 bool notFound = ( insertMapIt == insertMap.end() );
8471 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8473 // insert into another link of the same element:
8474 // 1. perform insertion into the other link of the elem
8475 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8476 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8477 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8478 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8479 // 2. perform insertion into the link of adjacent faces
8481 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8483 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8487 if (toCreatePolyedrs) {
8488 // perform insertion into the links of adjacent volumes
8489 UpdateVolumes(n12, n22, nodeList);
8491 // 3. find an element appeared on n1 and n2 after the insertion
8492 insertMap.erase( elem );
8493 elem = findAdjacentFace( n1, n2, 0 );
8495 if ( notFound || otherLink ) {
8496 // add element and nodes of the side into the insertMap
8497 insertMapIt = insertMap.insert
8498 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8499 (*insertMapIt).second.push_back( n1 );
8500 (*insertMapIt).second.push_back( n2 );
8502 // add node to be inserted into elem
8503 (*insertMapIt).second.push_back( nIns );
8504 next[ 1 - intoBord ] = true;
8507 // go to the next segment
8508 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8509 if ( next[ iBord ] ) {
8510 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8512 nPrev[ iBord ] = *nIt[ iBord ];
8513 nIt[ iBord ]++; i[ iBord ]++;
8517 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8519 // perform insertion of nodes into elements
8521 for (insertMapIt = insertMap.begin();
8522 insertMapIt != insertMap.end();
8525 const SMDS_MeshElement* elem = (*insertMapIt).first;
8526 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8527 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8528 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8530 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8532 if ( !theSideIsFreeBorder ) {
8533 // look for and insert nodes into the faces adjacent to elem
8535 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8537 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8542 if (toCreatePolyedrs) {
8543 // perform insertion into the links of adjacent volumes
8544 UpdateVolumes(n1, n2, nodeList);
8550 } // end: insert new nodes
8552 MergeNodes ( nodeGroupsToMerge );
8557 //=======================================================================
8558 //function : InsertNodesIntoLink
8559 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8560 // and theBetweenNode2 and split theElement
8561 //=======================================================================
8563 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8564 const SMDS_MeshNode* theBetweenNode1,
8565 const SMDS_MeshNode* theBetweenNode2,
8566 list<const SMDS_MeshNode*>& theNodesToInsert,
8567 const bool toCreatePoly)
8569 if ( theFace->GetType() != SMDSAbs_Face ) return;
8571 // find indices of 2 link nodes and of the rest nodes
8572 int iNode = 0, il1, il2, i3, i4;
8573 il1 = il2 = i3 = i4 = -1;
8574 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8575 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8577 if(theFace->IsQuadratic()) {
8578 const SMDS_VtkFace* F =
8579 dynamic_cast<const SMDS_VtkFace*>(theFace);
8580 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8581 // use special nodes iterator
8582 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8583 while( anIter->more() ) {
8584 const SMDS_MeshNode* n = cast2Node(anIter->next());
8585 if ( n == theBetweenNode1 )
8587 else if ( n == theBetweenNode2 )
8593 nodes[ iNode++ ] = n;
8597 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8598 while ( nodeIt->more() ) {
8599 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8600 if ( n == theBetweenNode1 )
8602 else if ( n == theBetweenNode2 )
8608 nodes[ iNode++ ] = n;
8611 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8614 // arrange link nodes to go one after another regarding the face orientation
8615 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8616 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8621 aNodesToInsert.reverse();
8623 // check that not link nodes of a quadrangles are in good order
8624 int nbFaceNodes = theFace->NbNodes();
8625 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8631 if (toCreatePoly || theFace->IsPoly()) {
8634 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8636 // add nodes of face up to first node of link
8639 if(theFace->IsQuadratic()) {
8640 const SMDS_VtkFace* F =
8641 dynamic_cast<const SMDS_VtkFace*>(theFace);
8642 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8643 // use special nodes iterator
8644 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8645 while( anIter->more() && !isFLN ) {
8646 const SMDS_MeshNode* n = cast2Node(anIter->next());
8647 poly_nodes[iNode++] = n;
8648 if (n == nodes[il1]) {
8652 // add nodes to insert
8653 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8654 for (; nIt != aNodesToInsert.end(); nIt++) {
8655 poly_nodes[iNode++] = *nIt;
8657 // add nodes of face starting from last node of link
8658 while ( anIter->more() ) {
8659 poly_nodes[iNode++] = cast2Node(anIter->next());
8663 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8664 while ( nodeIt->more() && !isFLN ) {
8665 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8666 poly_nodes[iNode++] = n;
8667 if (n == nodes[il1]) {
8671 // add nodes to insert
8672 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8673 for (; nIt != aNodesToInsert.end(); nIt++) {
8674 poly_nodes[iNode++] = *nIt;
8676 // add nodes of face starting from last node of link
8677 while ( nodeIt->more() ) {
8678 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8679 poly_nodes[iNode++] = n;
8683 // edit or replace the face
8684 SMESHDS_Mesh *aMesh = GetMeshDS();
8686 if (theFace->IsPoly()) {
8687 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8690 int aShapeId = FindShape( theFace );
8692 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8693 myLastCreatedElems.Append(newElem);
8694 if ( aShapeId && newElem )
8695 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8697 aMesh->RemoveElement(theFace);
8702 SMESHDS_Mesh *aMesh = GetMeshDS();
8703 if( !theFace->IsQuadratic() ) {
8705 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8706 int nbLinkNodes = 2 + aNodesToInsert.size();
8707 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8708 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8709 linkNodes[ 0 ] = nodes[ il1 ];
8710 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8711 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8712 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8713 linkNodes[ iNode++ ] = *nIt;
8715 // decide how to split a quadrangle: compare possible variants
8716 // and choose which of splits to be a quadrangle
8717 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8718 if ( nbFaceNodes == 3 ) {
8719 iBestQuad = nbSplits;
8722 else if ( nbFaceNodes == 4 ) {
8723 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8724 double aBestRate = DBL_MAX;
8725 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8727 double aBadRate = 0;
8728 // evaluate elements quality
8729 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8730 if ( iSplit == iQuad ) {
8731 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8735 aBadRate += getBadRate( &quad, aCrit );
8738 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8740 nodes[ iSplit < iQuad ? i4 : i3 ]);
8741 aBadRate += getBadRate( &tria, aCrit );
8745 if ( aBadRate < aBestRate ) {
8747 aBestRate = aBadRate;
8752 // create new elements
8753 int aShapeId = FindShape( theFace );
8756 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8757 SMDS_MeshElement* newElem = 0;
8758 if ( iSplit == iBestQuad )
8759 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8764 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8766 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8767 myLastCreatedElems.Append(newElem);
8768 if ( aShapeId && newElem )
8769 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8772 // change nodes of theFace
8773 const SMDS_MeshNode* newNodes[ 4 ];
8774 newNodes[ 0 ] = linkNodes[ i1 ];
8775 newNodes[ 1 ] = linkNodes[ i2 ];
8776 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8777 newNodes[ 3 ] = nodes[ i4 ];
8778 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8779 const SMDS_MeshElement* newElem = 0;
8780 if (iSplit == iBestQuad)
8781 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8783 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8784 myLastCreatedElems.Append(newElem);
8785 if ( aShapeId && newElem )
8786 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8787 } // end if(!theFace->IsQuadratic())
8788 else { // theFace is quadratic
8789 // we have to split theFace on simple triangles and one simple quadrangle
8791 int nbshift = tmp*2;
8792 // shift nodes in nodes[] by nbshift
8794 for(i=0; i<nbshift; i++) {
8795 const SMDS_MeshNode* n = nodes[0];
8796 for(j=0; j<nbFaceNodes-1; j++) {
8797 nodes[j] = nodes[j+1];
8799 nodes[nbFaceNodes-1] = n;
8801 il1 = il1 - nbshift;
8802 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8803 // n0 n1 n2 n0 n1 n2
8804 // +-----+-----+ +-----+-----+
8813 // create new elements
8814 int aShapeId = FindShape( theFace );
8817 if(nbFaceNodes==6) { // quadratic triangle
8818 SMDS_MeshElement* newElem =
8819 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8820 myLastCreatedElems.Append(newElem);
8821 if ( aShapeId && newElem )
8822 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8823 if(theFace->IsMediumNode(nodes[il1])) {
8824 // create quadrangle
8825 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8826 myLastCreatedElems.Append(newElem);
8827 if ( aShapeId && newElem )
8828 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8834 // create quadrangle
8835 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8836 myLastCreatedElems.Append(newElem);
8837 if ( aShapeId && newElem )
8838 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8844 else { // nbFaceNodes==8 - quadratic quadrangle
8845 SMDS_MeshElement* newElem =
8846 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8847 myLastCreatedElems.Append(newElem);
8848 if ( aShapeId && newElem )
8849 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8850 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8851 myLastCreatedElems.Append(newElem);
8852 if ( aShapeId && newElem )
8853 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8854 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8855 myLastCreatedElems.Append(newElem);
8856 if ( aShapeId && newElem )
8857 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8858 if(theFace->IsMediumNode(nodes[il1])) {
8859 // create quadrangle
8860 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8861 myLastCreatedElems.Append(newElem);
8862 if ( aShapeId && newElem )
8863 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8869 // create quadrangle
8870 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8871 myLastCreatedElems.Append(newElem);
8872 if ( aShapeId && newElem )
8873 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8879 // create needed triangles using n1,n2,n3 and inserted nodes
8880 int nbn = 2 + aNodesToInsert.size();
8881 //const SMDS_MeshNode* aNodes[nbn];
8882 vector<const SMDS_MeshNode*> aNodes(nbn);
8883 aNodes[0] = nodes[n1];
8884 aNodes[nbn-1] = nodes[n2];
8885 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8886 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8887 aNodes[iNode++] = *nIt;
8889 for(i=1; i<nbn; i++) {
8890 SMDS_MeshElement* newElem =
8891 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8892 myLastCreatedElems.Append(newElem);
8893 if ( aShapeId && newElem )
8894 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8898 aMesh->RemoveElement(theFace);
8901 //=======================================================================
8902 //function : UpdateVolumes
8904 //=======================================================================
8905 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8906 const SMDS_MeshNode* theBetweenNode2,
8907 list<const SMDS_MeshNode*>& theNodesToInsert)
8909 myLastCreatedElems.Clear();
8910 myLastCreatedNodes.Clear();
8912 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8913 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8914 const SMDS_MeshElement* elem = invElemIt->next();
8916 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8917 SMDS_VolumeTool aVolume (elem);
8918 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8921 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8922 int iface, nbFaces = aVolume.NbFaces();
8923 vector<const SMDS_MeshNode *> poly_nodes;
8924 vector<int> quantities (nbFaces);
8926 for (iface = 0; iface < nbFaces; iface++) {
8927 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8928 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8929 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8931 for (int inode = 0; inode < nbFaceNodes; inode++) {
8932 poly_nodes.push_back(faceNodes[inode]);
8934 if (nbInserted == 0) {
8935 if (faceNodes[inode] == theBetweenNode1) {
8936 if (faceNodes[inode + 1] == theBetweenNode2) {
8937 nbInserted = theNodesToInsert.size();
8939 // add nodes to insert
8940 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8941 for (; nIt != theNodesToInsert.end(); nIt++) {
8942 poly_nodes.push_back(*nIt);
8946 else if (faceNodes[inode] == theBetweenNode2) {
8947 if (faceNodes[inode + 1] == theBetweenNode1) {
8948 nbInserted = theNodesToInsert.size();
8950 // add nodes to insert in reversed order
8951 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8953 for (; nIt != theNodesToInsert.begin(); nIt--) {
8954 poly_nodes.push_back(*nIt);
8956 poly_nodes.push_back(*nIt);
8963 quantities[iface] = nbFaceNodes + nbInserted;
8966 // Replace or update the volume
8967 SMESHDS_Mesh *aMesh = GetMeshDS();
8969 if (elem->IsPoly()) {
8970 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8974 int aShapeId = FindShape( elem );
8976 SMDS_MeshElement* newElem =
8977 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8978 myLastCreatedElems.Append(newElem);
8979 if (aShapeId && newElem)
8980 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8982 aMesh->RemoveElement(elem);
8989 //================================================================================
8991 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8993 //================================================================================
8995 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8996 vector<const SMDS_MeshNode *> & nodes,
8997 vector<int> & nbNodeInFaces )
9000 nbNodeInFaces.clear();
9001 SMDS_VolumeTool vTool ( elem );
9002 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9004 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9005 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9006 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9011 //=======================================================================
9013 * \brief Convert elements contained in a submesh to quadratic
9014 * \return int - nb of checked elements
9016 //=======================================================================
9018 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9019 SMESH_MesherHelper& theHelper,
9020 const bool theForce3d)
9023 if( !theSm ) return nbElem;
9025 vector<int> nbNodeInFaces;
9026 vector<const SMDS_MeshNode *> nodes;
9027 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9028 while(ElemItr->more())
9031 const SMDS_MeshElement* elem = ElemItr->next();
9032 if( !elem || elem->IsQuadratic() ) continue;
9034 // get elem data needed to re-create it
9036 const int id = elem->GetID();
9037 const int nbNodes = elem->NbNodes();
9038 const SMDSAbs_ElementType aType = elem->GetType();
9039 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9040 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9041 if ( aGeomType == SMDSEntity_Polyhedra )
9042 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9043 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9044 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9046 // remove a linear element
9047 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9049 const SMDS_MeshElement* NewElem = 0;
9055 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9063 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9066 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9069 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9074 case SMDSAbs_Volume :
9078 case SMDSEntity_Tetra:
9079 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9081 case SMDSEntity_Pyramid:
9082 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9084 case SMDSEntity_Penta:
9085 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9087 case SMDSEntity_Hexa:
9088 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9089 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9091 case SMDSEntity_Hexagonal_Prism:
9093 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9100 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9102 theSm->AddElement( NewElem );
9107 //=======================================================================
9108 //function : ConvertToQuadratic
9110 //=======================================================================
9112 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9114 SMESHDS_Mesh* meshDS = GetMeshDS();
9116 SMESH_MesherHelper aHelper(*myMesh);
9117 aHelper.SetIsQuadratic( true );
9119 int nbCheckedElems = 0;
9120 if ( myMesh->HasShapeToMesh() )
9122 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9124 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9125 while ( smIt->more() ) {
9126 SMESH_subMesh* sm = smIt->next();
9127 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9128 aHelper.SetSubShape( sm->GetSubShape() );
9129 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9134 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9135 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9137 SMESHDS_SubMesh *smDS = 0;
9138 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9139 while(aEdgeItr->more())
9141 const SMDS_MeshEdge* edge = aEdgeItr->next();
9142 if(edge && !edge->IsQuadratic())
9144 int id = edge->GetID();
9145 //MESSAGE("edge->GetID() " << id);
9146 const SMDS_MeshNode* n1 = edge->GetNode(0);
9147 const SMDS_MeshNode* n2 = edge->GetNode(1);
9149 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9151 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9152 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9155 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9156 while(aFaceItr->more())
9158 const SMDS_MeshFace* face = aFaceItr->next();
9159 if(!face || face->IsQuadratic() ) continue;
9161 const int id = face->GetID();
9162 const SMDSAbs_EntityType type = face->GetEntityType();
9163 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9165 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9167 SMDS_MeshFace * NewFace = 0;
9170 case SMDSEntity_Triangle:
9171 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9173 case SMDSEntity_Quadrangle:
9174 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9177 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9179 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9181 vector<int> nbNodeInFaces;
9182 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9183 while(aVolumeItr->more())
9185 const SMDS_MeshVolume* volume = aVolumeItr->next();
9186 if(!volume || volume->IsQuadratic() ) continue;
9188 const int id = volume->GetID();
9189 const SMDSAbs_EntityType type = volume->GetEntityType();
9190 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9191 if ( type == SMDSEntity_Polyhedra )
9192 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9193 else if ( type == SMDSEntity_Hexagonal_Prism )
9194 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9196 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9198 SMDS_MeshVolume * NewVolume = 0;
9201 case SMDSEntity_Tetra:
9202 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9204 case SMDSEntity_Hexa:
9205 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9206 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9208 case SMDSEntity_Pyramid:
9209 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9210 nodes[3], nodes[4], id, theForce3d);
9212 case SMDSEntity_Penta:
9213 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9214 nodes[3], nodes[4], nodes[5], id, theForce3d);
9216 case SMDSEntity_Hexagonal_Prism:
9218 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9220 ReplaceElemInGroups(volume, NewVolume, meshDS);
9225 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9226 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9227 aHelper.FixQuadraticElements();
9231 //================================================================================
9233 * \brief Makes given elements quadratic
9234 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9235 * \param theElements - elements to make quadratic
9237 //================================================================================
9239 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9240 TIDSortedElemSet& theElements)
9242 if ( theElements.empty() ) return;
9244 // we believe that all theElements are of the same type
9245 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9247 // get all nodes shared by theElements
9248 TIDSortedNodeSet allNodes;
9249 TIDSortedElemSet::iterator eIt = theElements.begin();
9250 for ( ; eIt != theElements.end(); ++eIt )
9251 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9253 // complete theElements with elements of lower dim whose all nodes are in allNodes
9255 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9256 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9257 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9258 for ( ; nIt != allNodes.end(); ++nIt )
9260 const SMDS_MeshNode* n = *nIt;
9261 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9262 while ( invIt->more() )
9264 const SMDS_MeshElement* e = invIt->next();
9265 if ( e->IsQuadratic() )
9267 quadAdjacentElems[ e->GetType() ].insert( e );
9270 if ( e->GetType() >= elemType )
9272 continue; // same type of more complex linear element
9275 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9276 continue; // e is already checked
9280 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9281 while ( nodeIt->more() && allIn )
9282 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9284 theElements.insert(e );
9288 SMESH_MesherHelper helper(*myMesh);
9289 helper.SetIsQuadratic( true );
9291 // add links of quadratic adjacent elements to the helper
9293 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9294 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9295 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9297 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9299 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9300 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9301 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9303 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9305 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9306 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9307 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9309 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9312 // make quadratic elements instead of linear ones
9314 SMESHDS_Mesh* meshDS = GetMeshDS();
9315 SMESHDS_SubMesh* smDS = 0;
9316 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9318 const SMDS_MeshElement* elem = *eIt;
9319 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9322 const int id = elem->GetID();
9323 const SMDSAbs_ElementType type = elem->GetType();
9324 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9326 if ( !smDS || !smDS->Contains( elem ))
9327 smDS = meshDS->MeshElements( elem->getshapeId() );
9328 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9330 SMDS_MeshElement * newElem = 0;
9331 switch( nodes.size() )
9333 case 4: // cases for most frequently used element types go first (for optimization)
9334 if ( type == SMDSAbs_Volume )
9335 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9337 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9340 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9341 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9344 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9347 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9350 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9351 nodes[4], id, theForce3d);
9354 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9355 nodes[4], nodes[5], id, theForce3d);
9359 ReplaceElemInGroups( elem, newElem, meshDS);
9360 if( newElem && smDS )
9361 smDS->AddElement( newElem );
9364 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9365 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9366 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9367 helper.FixQuadraticElements();
9371 //=======================================================================
9373 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9374 * \return int - nb of checked elements
9376 //=======================================================================
9378 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9379 SMDS_ElemIteratorPtr theItr,
9380 const int theShapeID)
9383 SMESHDS_Mesh* meshDS = GetMeshDS();
9385 while( theItr->more() )
9387 const SMDS_MeshElement* elem = theItr->next();
9389 if( elem && elem->IsQuadratic())
9391 int id = elem->GetID();
9392 int nbCornerNodes = elem->NbCornerNodes();
9393 SMDSAbs_ElementType aType = elem->GetType();
9395 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9397 //remove a quadratic element
9398 if ( !theSm || !theSm->Contains( elem ))
9399 theSm = meshDS->MeshElements( elem->getshapeId() );
9400 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9402 // remove medium nodes
9403 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9404 if ( nodes[i]->NbInverseElements() == 0 )
9405 meshDS->RemoveFreeNode( nodes[i], theSm );
9407 // add a linear element
9408 nodes.resize( nbCornerNodes );
9409 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9410 ReplaceElemInGroups(elem, newElem, meshDS);
9411 if( theSm && newElem )
9412 theSm->AddElement( newElem );
9418 //=======================================================================
9419 //function : ConvertFromQuadratic
9421 //=======================================================================
9423 bool SMESH_MeshEditor::ConvertFromQuadratic()
9425 int nbCheckedElems = 0;
9426 if ( myMesh->HasShapeToMesh() )
9428 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9430 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9431 while ( smIt->more() ) {
9432 SMESH_subMesh* sm = smIt->next();
9433 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9434 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9440 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9441 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9443 SMESHDS_SubMesh *aSM = 0;
9444 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9452 //================================================================================
9454 * \brief Return true if all medium nodes of the element are in the node set
9456 //================================================================================
9458 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9460 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9461 if ( !nodeSet.count( elem->GetNode(i) ))
9467 //================================================================================
9469 * \brief Makes given elements linear
9471 //================================================================================
9473 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9475 if ( theElements.empty() ) return;
9477 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9478 set<int> mediumNodeIDs;
9479 TIDSortedElemSet::iterator eIt = theElements.begin();
9480 for ( ; eIt != theElements.end(); ++eIt )
9482 const SMDS_MeshElement* e = *eIt;
9483 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9484 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9487 // replace given elements by linear ones
9488 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9489 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9490 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9492 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9493 // except those elements sharing medium nodes of quadratic element whose medium nodes
9494 // are not all in mediumNodeIDs
9496 // get remaining medium nodes
9497 TIDSortedNodeSet mediumNodes;
9498 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9499 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9500 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9501 mediumNodes.insert( mediumNodes.end(), n );
9503 // find more quadratic elements to convert
9504 TIDSortedElemSet moreElemsToConvert;
9505 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9506 for ( ; nIt != mediumNodes.end(); ++nIt )
9508 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9509 while ( invIt->more() )
9511 const SMDS_MeshElement* e = invIt->next();
9512 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9514 // find a more complex element including e and
9515 // whose medium nodes are not in mediumNodes
9516 bool complexFound = false;
9517 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9519 SMDS_ElemIteratorPtr invIt2 =
9520 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9521 while ( invIt2->more() )
9523 const SMDS_MeshElement* eComplex = invIt2->next();
9524 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9526 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9527 if ( nbCommonNodes == e->NbNodes())
9529 complexFound = true;
9530 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9536 if ( !complexFound )
9537 moreElemsToConvert.insert( e );
9541 elemIt = SMDS_ElemIteratorPtr
9542 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9543 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9546 //=======================================================================
9547 //function : SewSideElements
9549 //=======================================================================
9551 SMESH_MeshEditor::Sew_Error
9552 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9553 TIDSortedElemSet& theSide2,
9554 const SMDS_MeshNode* theFirstNode1,
9555 const SMDS_MeshNode* theFirstNode2,
9556 const SMDS_MeshNode* theSecondNode1,
9557 const SMDS_MeshNode* theSecondNode2)
9559 myLastCreatedElems.Clear();
9560 myLastCreatedNodes.Clear();
9562 MESSAGE ("::::SewSideElements()");
9563 if ( theSide1.size() != theSide2.size() )
9564 return SEW_DIFF_NB_OF_ELEMENTS;
9566 Sew_Error aResult = SEW_OK;
9568 // 1. Build set of faces representing each side
9569 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9570 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9572 // =======================================================================
9573 // 1. Build set of faces representing each side:
9574 // =======================================================================
9575 // a. build set of nodes belonging to faces
9576 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9577 // c. create temporary faces representing side of volumes if correspondent
9578 // face does not exist
9580 SMESHDS_Mesh* aMesh = GetMeshDS();
9581 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9582 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9583 set<const SMDS_MeshElement*> faceSet1, faceSet2;
9584 set<const SMDS_MeshElement*> volSet1, volSet2;
9585 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9586 set<const SMDS_MeshElement*> * faceSetPtr[] = { &faceSet1, &faceSet2 };
9587 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9588 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9589 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9590 int iSide, iFace, iNode;
9592 list<const SMDS_MeshElement* > tempFaceList;
9593 for ( iSide = 0; iSide < 2; iSide++ ) {
9594 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9595 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9596 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9597 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9598 set<const SMDS_MeshElement*>::iterator vIt;
9599 TIDSortedElemSet::iterator eIt;
9600 set<const SMDS_MeshNode*>::iterator nIt;
9602 // check that given nodes belong to given elements
9603 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9604 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9605 int firstIndex = -1, secondIndex = -1;
9606 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9607 const SMDS_MeshElement* elem = *eIt;
9608 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9609 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9610 if ( firstIndex > -1 && secondIndex > -1 ) break;
9612 if ( firstIndex < 0 || secondIndex < 0 ) {
9613 // we can simply return until temporary faces created
9614 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9617 // -----------------------------------------------------------
9618 // 1a. Collect nodes of existing faces
9619 // and build set of face nodes in order to detect missing
9620 // faces corresponding to sides of volumes
9621 // -----------------------------------------------------------
9623 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9625 // loop on the given element of a side
9626 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9627 //const SMDS_MeshElement* elem = *eIt;
9628 const SMDS_MeshElement* elem = *eIt;
9629 if ( elem->GetType() == SMDSAbs_Face ) {
9630 faceSet->insert( elem );
9631 set <const SMDS_MeshNode*> faceNodeSet;
9632 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9633 while ( nodeIt->more() ) {
9634 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9635 nodeSet->insert( n );
9636 faceNodeSet.insert( n );
9638 setOfFaceNodeSet.insert( faceNodeSet );
9640 else if ( elem->GetType() == SMDSAbs_Volume )
9641 volSet->insert( elem );
9643 // ------------------------------------------------------------------------------
9644 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9645 // ------------------------------------------------------------------------------
9647 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9648 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9649 while ( fIt->more() ) { // loop on faces sharing a node
9650 const SMDS_MeshElement* f = fIt->next();
9651 if ( faceSet->find( f ) == faceSet->end() ) {
9652 // check if all nodes are in nodeSet and
9653 // complete setOfFaceNodeSet if they are
9654 set <const SMDS_MeshNode*> faceNodeSet;
9655 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9656 bool allInSet = true;
9657 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9658 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9659 if ( nodeSet->find( n ) == nodeSet->end() )
9662 faceNodeSet.insert( n );
9665 faceSet->insert( f );
9666 setOfFaceNodeSet.insert( faceNodeSet );
9672 // -------------------------------------------------------------------------
9673 // 1c. Create temporary faces representing sides of volumes if correspondent
9674 // face does not exist
9675 // -------------------------------------------------------------------------
9677 if ( !volSet->empty() ) {
9678 //int nodeSetSize = nodeSet->size();
9680 // loop on given volumes
9681 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9682 SMDS_VolumeTool vol (*vIt);
9683 // loop on volume faces: find free faces
9684 // --------------------------------------
9685 list<const SMDS_MeshElement* > freeFaceList;
9686 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9687 if ( !vol.IsFreeFace( iFace ))
9689 // check if there is already a face with same nodes in a face set
9690 const SMDS_MeshElement* aFreeFace = 0;
9691 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9692 int nbNodes = vol.NbFaceNodes( iFace );
9693 set <const SMDS_MeshNode*> faceNodeSet;
9694 vol.GetFaceNodes( iFace, faceNodeSet );
9695 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9697 // no such a face is given but it still can exist, check it
9698 if ( nbNodes == 3 ) {
9699 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] );
9701 else if ( nbNodes == 4 ) {
9702 aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9705 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9706 aFreeFace = aMesh->FindFace(poly_nodes);
9710 // create a temporary face
9711 if ( nbNodes == 3 ) {
9712 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9713 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9715 else if ( nbNodes == 4 ) {
9716 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9717 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9720 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9721 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9722 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9726 freeFaceList.push_back( aFreeFace );
9727 tempFaceList.push_back( aFreeFace );
9730 } // loop on faces of a volume
9732 // choose one of several free faces
9733 // --------------------------------------
9734 if ( freeFaceList.size() > 1 ) {
9735 // choose a face having max nb of nodes shared by other elems of a side
9736 int maxNbNodes = -1/*, nbExcludedFaces = 0*/;
9737 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9738 while ( fIt != freeFaceList.end() ) { // loop on free faces
9739 int nbSharedNodes = 0;
9740 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9741 while ( nodeIt->more() ) { // loop on free face nodes
9742 const SMDS_MeshNode* n =
9743 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9744 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9745 while ( invElemIt->more() ) {
9746 const SMDS_MeshElement* e = invElemIt->next();
9747 if ( faceSet->find( e ) != faceSet->end() )
9749 if ( elemSet->find( e ) != elemSet->end() )
9753 if ( nbSharedNodes >= maxNbNodes ) {
9754 maxNbNodes = nbSharedNodes;
9758 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9760 if ( freeFaceList.size() > 1 )
9762 // could not choose one face, use another way
9763 // choose a face most close to the bary center of the opposite side
9764 gp_XYZ aBC( 0., 0., 0. );
9765 set <const SMDS_MeshNode*> addedNodes;
9766 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9767 eIt = elemSet2->begin();
9768 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9769 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9770 while ( nodeIt->more() ) { // loop on free face nodes
9771 const SMDS_MeshNode* n =
9772 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9773 if ( addedNodes.insert( n ).second )
9774 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9777 aBC /= addedNodes.size();
9778 double minDist = DBL_MAX;
9779 fIt = freeFaceList.begin();
9780 while ( fIt != freeFaceList.end() ) { // loop on free faces
9782 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9783 while ( nodeIt->more() ) { // loop on free face nodes
9784 const SMDS_MeshNode* n =
9785 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9786 gp_XYZ p( n->X(),n->Y(),n->Z() );
9787 dist += ( aBC - p ).SquareModulus();
9789 if ( dist < minDist ) {
9791 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9794 fIt = freeFaceList.erase( fIt++ );
9797 } // choose one of several free faces of a volume
9799 if ( freeFaceList.size() == 1 ) {
9800 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9801 faceSet->insert( aFreeFace );
9802 // complete a node set with nodes of a found free face
9803 // for ( iNode = 0; iNode < ; iNode++ )
9804 // nodeSet->insert( fNodes[ iNode ] );
9807 } // loop on volumes of a side
9809 // // complete a set of faces if new nodes in a nodeSet appeared
9810 // // ----------------------------------------------------------
9811 // if ( nodeSetSize != nodeSet->size() ) {
9812 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9813 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9814 // while ( fIt->more() ) { // loop on faces sharing a node
9815 // const SMDS_MeshElement* f = fIt->next();
9816 // if ( faceSet->find( f ) == faceSet->end() ) {
9817 // // check if all nodes are in nodeSet and
9818 // // complete setOfFaceNodeSet if they are
9819 // set <const SMDS_MeshNode*> faceNodeSet;
9820 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9821 // bool allInSet = true;
9822 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9823 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9824 // if ( nodeSet->find( n ) == nodeSet->end() )
9825 // allInSet = false;
9827 // faceNodeSet.insert( n );
9829 // if ( allInSet ) {
9830 // faceSet->insert( f );
9831 // setOfFaceNodeSet.insert( faceNodeSet );
9837 } // Create temporary faces, if there are volumes given
9840 if ( faceSet1.size() != faceSet2.size() ) {
9841 // delete temporary faces: they are in reverseElements of actual nodes
9842 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9843 // while ( tmpFaceIt->more() )
9844 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9845 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9846 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9847 // aMesh->RemoveElement(*tmpFaceIt);
9848 MESSAGE("Diff nb of faces");
9849 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9852 // ============================================================
9853 // 2. Find nodes to merge:
9854 // bind a node to remove to a node to put instead
9855 // ============================================================
9857 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9858 if ( theFirstNode1 != theFirstNode2 )
9859 nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 ));
9860 if ( theSecondNode1 != theSecondNode2 )
9861 nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 ));
9863 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9864 set< long > linkIdSet; // links to process
9865 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9867 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9868 list< NLink > linkList[2];
9869 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9870 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9871 // loop on links in linkList; find faces by links and append links
9872 // of the found faces to linkList
9873 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9874 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9875 NLink link[] = { *linkIt[0], *linkIt[1] };
9876 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9877 if ( linkIdSet.find( linkID ) == linkIdSet.end() )
9880 // by links, find faces in the face sets,
9881 // and find indices of link nodes in the found faces;
9882 // in a face set, there is only one or no face sharing a link
9883 // ---------------------------------------------------------------
9885 const SMDS_MeshElement* face[] = { 0, 0 };
9886 //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ];
9887 vector<const SMDS_MeshNode*> fnodes1(9);
9888 vector<const SMDS_MeshNode*> fnodes2(9);
9889 //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ;
9890 vector<const SMDS_MeshNode*> notLinkNodes1(6);
9891 vector<const SMDS_MeshNode*> notLinkNodes2(6);
9892 int iLinkNode[2][2];
9893 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9894 const SMDS_MeshNode* n1 = link[iSide].first;
9895 const SMDS_MeshNode* n2 = link[iSide].second;
9896 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9897 set< const SMDS_MeshElement* > fMap;
9898 for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link
9899 const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link
9900 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9901 while ( fIt->more() ) { // loop on faces sharing a node
9902 const SMDS_MeshElement* f = fIt->next();
9903 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9904 ! fMap.insert( f ).second ) // f encounters twice
9906 if ( face[ iSide ] ) {
9907 MESSAGE( "2 faces per link " );
9908 aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES;
9912 faceSet->erase( f );
9913 // get face nodes and find ones of a link
9918 fnodes1.resize(f->NbNodes()+1);
9919 notLinkNodes1.resize(f->NbNodes()-2);
9922 fnodes2.resize(f->NbNodes()+1);
9923 notLinkNodes2.resize(f->NbNodes()-2);
9926 if(!f->IsQuadratic()) {
9927 SMDS_ElemIteratorPtr nIt = f->nodesIterator();
9928 while ( nIt->more() ) {
9929 const SMDS_MeshNode* n =
9930 static_cast<const SMDS_MeshNode*>( nIt->next() );
9932 iLinkNode[ iSide ][ 0 ] = iNode;
9934 else if ( n == n2 ) {
9935 iLinkNode[ iSide ][ 1 ] = iNode;
9937 //else if ( notLinkNodes[ iSide ][ 0 ] )
9938 // notLinkNodes[ iSide ][ 1 ] = n;
9940 // notLinkNodes[ iSide ][ 0 ] = n;
9944 notLinkNodes1[nbl] = n;
9945 //notLinkNodes1.push_back(n);
9947 notLinkNodes2[nbl] = n;
9948 //notLinkNodes2.push_back(n);
9950 //faceNodes[ iSide ][ iNode++ ] = n;
9952 fnodes1[iNode++] = n;
9955 fnodes2[iNode++] = n;
9959 else { // f->IsQuadratic()
9960 const SMDS_VtkFace* F =
9961 dynamic_cast<const SMDS_VtkFace*>(f);
9962 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9963 // use special nodes iterator
9964 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9965 while ( anIter->more() ) {
9966 const SMDS_MeshNode* n =
9967 static_cast<const SMDS_MeshNode*>( anIter->next() );
9969 iLinkNode[ iSide ][ 0 ] = iNode;
9971 else if ( n == n2 ) {
9972 iLinkNode[ iSide ][ 1 ] = iNode;
9977 notLinkNodes1[nbl] = n;
9980 notLinkNodes2[nbl] = n;
9984 fnodes1[iNode++] = n;
9987 fnodes2[iNode++] = n;
9991 //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ];
9993 fnodes1[iNode] = fnodes1[0];
9996 fnodes2[iNode] = fnodes1[0];
10003 // check similarity of elements of the sides
10004 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10005 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10006 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10007 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10010 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10012 break; // do not return because it s necessary to remove tmp faces
10015 // set nodes to merge
10016 // -------------------
10018 if ( face[0] && face[1] ) {
10019 int nbNodes = face[0]->NbNodes();
10020 if ( nbNodes != face[1]->NbNodes() ) {
10021 MESSAGE("Diff nb of face nodes");
10022 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10023 break; // do not return because it s necessary to remove tmp faces
10025 bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle
10026 if ( nbNodes == 3 ) {
10027 //nReplaceMap.insert( TNodeNodeMap::value_type
10028 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10029 nReplaceMap.insert( TNodeNodeMap::value_type
10030 ( notLinkNodes1[0], notLinkNodes2[0] ));
10033 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10034 // analyse link orientation in faces
10035 int i1 = iLinkNode[ iSide ][ 0 ];
10036 int i2 = iLinkNode[ iSide ][ 1 ];
10037 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10038 // if notLinkNodes are the first and the last ones, then
10039 // their order does not correspond to the link orientation
10040 if (( i1 == 1 && i2 == 2 ) ||
10041 ( i1 == 2 && i2 == 1 ))
10042 reverse[ iSide ] = !reverse[ iSide ];
10044 if ( reverse[0] == reverse[1] ) {
10045 //nReplaceMap.insert( TNodeNodeMap::value_type
10046 // ( notLinkNodes[0][0], notLinkNodes[1][0] ));
10047 //nReplaceMap.insert( TNodeNodeMap::value_type
10048 // ( notLinkNodes[0][1], notLinkNodes[1][1] ));
10049 for(int nn=0; nn<nbNodes-2; nn++) {
10050 nReplaceMap.insert( TNodeNodeMap::value_type
10051 ( notLinkNodes1[nn], notLinkNodes2[nn] ));
10055 //nReplaceMap.insert( TNodeNodeMap::value_type
10056 // ( notLinkNodes[0][0], notLinkNodes[1][1] ));
10057 //nReplaceMap.insert( TNodeNodeMap::value_type
10058 // ( notLinkNodes[0][1], notLinkNodes[1][0] ));
10059 for(int nn=0; nn<nbNodes-2; nn++) {
10060 nReplaceMap.insert( TNodeNodeMap::value_type
10061 ( notLinkNodes1[nn], notLinkNodes2[nbNodes-3-nn] ));
10066 // add other links of the faces to linkList
10067 // -----------------------------------------
10069 //const SMDS_MeshNode** nodes = faceNodes[ 0 ];
10070 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10071 //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] );
10072 linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] );
10073 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10074 if ( !iter_isnew.second ) { // already in a set: no need to process
10075 linkIdSet.erase( iter_isnew.first );
10077 else // new in set == encountered for the first time: add
10079 //const SMDS_MeshNode* n1 = nodes[ iNode ];
10080 //const SMDS_MeshNode* n2 = nodes[ iNode + 1];
10081 const SMDS_MeshNode* n1 = fnodes1[ iNode ];
10082 const SMDS_MeshNode* n2 = fnodes1[ iNode + 1];
10083 linkList[0].push_back ( NLink( n1, n2 ));
10084 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10088 } // loop on link lists
10090 if ( aResult == SEW_OK &&
10091 ( linkIt[0] != linkList[0].end() ||
10092 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10093 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10094 " " << (faceSetPtr[1]->empty()));
10095 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10098 // ====================================================================
10099 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10100 // ====================================================================
10102 // delete temporary faces: they are in reverseElements of actual nodes
10103 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10104 // while ( tmpFaceIt->more() )
10105 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10106 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10107 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10108 // aMesh->RemoveElement(*tmpFaceIt);
10110 if ( aResult != SEW_OK)
10113 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10114 // loop on nodes replacement map
10115 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10116 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10117 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10118 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10119 nodeIDsToRemove.push_back( nToRemove->GetID() );
10120 // loop on elements sharing nToRemove
10121 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10122 while ( invElemIt->more() ) {
10123 const SMDS_MeshElement* e = invElemIt->next();
10124 // get a new suite of nodes: make replacement
10125 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10126 vector< const SMDS_MeshNode*> nodes( nbNodes );
10127 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10128 while ( nIt->more() ) {
10129 const SMDS_MeshNode* n =
10130 static_cast<const SMDS_MeshNode*>( nIt->next() );
10131 nnIt = nReplaceMap.find( n );
10132 if ( nnIt != nReplaceMap.end() ) {
10134 n = (*nnIt).second;
10138 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10139 // elemIDsToRemove.push_back( e->GetID() );
10143 SMDSAbs_ElementType etyp = e->GetType();
10144 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10147 myLastCreatedElems.Append(newElem);
10148 AddToSameGroups(newElem, e, aMesh);
10149 int aShapeId = e->getshapeId();
10152 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10155 aMesh->RemoveElement(e);
10160 Remove( nodeIDsToRemove, true );
10165 //================================================================================
10167 * \brief Find corresponding nodes in two sets of faces
10168 * \param theSide1 - first face set
10169 * \param theSide2 - second first face
10170 * \param theFirstNode1 - a boundary node of set 1
10171 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10172 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10173 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10174 * \param nReplaceMap - output map of corresponding nodes
10175 * \return bool - is a success or not
10177 //================================================================================
10180 //#define DEBUG_MATCHING_NODES
10183 SMESH_MeshEditor::Sew_Error
10184 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10185 set<const SMDS_MeshElement*>& theSide2,
10186 const SMDS_MeshNode* theFirstNode1,
10187 const SMDS_MeshNode* theFirstNode2,
10188 const SMDS_MeshNode* theSecondNode1,
10189 const SMDS_MeshNode* theSecondNode2,
10190 TNodeNodeMap & nReplaceMap)
10192 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10194 nReplaceMap.clear();
10195 if ( theFirstNode1 != theFirstNode2 )
10196 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10197 if ( theSecondNode1 != theSecondNode2 )
10198 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10200 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10201 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10203 list< NLink > linkList[2];
10204 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10205 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10207 // loop on links in linkList; find faces by links and append links
10208 // of the found faces to linkList
10209 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10210 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10211 NLink link[] = { *linkIt[0], *linkIt[1] };
10212 if ( linkSet.find( link[0] ) == linkSet.end() )
10215 // by links, find faces in the face sets,
10216 // and find indices of link nodes in the found faces;
10217 // in a face set, there is only one or no face sharing a link
10218 // ---------------------------------------------------------------
10220 const SMDS_MeshElement* face[] = { 0, 0 };
10221 list<const SMDS_MeshNode*> notLinkNodes[2];
10222 //bool reverse[] = { false, false }; // order of notLinkNodes
10224 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10226 const SMDS_MeshNode* n1 = link[iSide].first;
10227 const SMDS_MeshNode* n2 = link[iSide].second;
10228 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10229 set< const SMDS_MeshElement* > facesOfNode1;
10230 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10232 // during a loop of the first node, we find all faces around n1,
10233 // during a loop of the second node, we find one face sharing both n1 and n2
10234 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10235 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10236 while ( fIt->more() ) { // loop on faces sharing a node
10237 const SMDS_MeshElement* f = fIt->next();
10238 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10239 ! facesOfNode1.insert( f ).second ) // f encounters twice
10241 if ( face[ iSide ] ) {
10242 MESSAGE( "2 faces per link " );
10243 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10246 faceSet->erase( f );
10248 // get not link nodes
10249 int nbN = f->NbNodes();
10250 if ( f->IsQuadratic() )
10252 nbNodes[ iSide ] = nbN;
10253 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10254 int i1 = f->GetNodeIndex( n1 );
10255 int i2 = f->GetNodeIndex( n2 );
10256 int iEnd = nbN, iBeg = -1, iDelta = 1;
10257 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10259 std::swap( iEnd, iBeg ); iDelta = -1;
10264 if ( i == iEnd ) i = iBeg + iDelta;
10265 if ( i == i1 ) break;
10266 nodes.push_back ( f->GetNode( i ) );
10272 // check similarity of elements of the sides
10273 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10274 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10275 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10276 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10279 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10283 // set nodes to merge
10284 // -------------------
10286 if ( face[0] && face[1] ) {
10287 if ( nbNodes[0] != nbNodes[1] ) {
10288 MESSAGE("Diff nb of face nodes");
10289 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10291 #ifdef DEBUG_MATCHING_NODES
10292 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10293 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10294 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10296 int nbN = nbNodes[0];
10298 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10299 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10300 for ( int i = 0 ; i < nbN - 2; ++i ) {
10301 #ifdef DEBUG_MATCHING_NODES
10302 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10304 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10308 // add other links of the face 1 to linkList
10309 // -----------------------------------------
10311 const SMDS_MeshElement* f0 = face[0];
10312 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10313 for ( int i = 0; i < nbN; i++ )
10315 const SMDS_MeshNode* n2 = f0->GetNode( i );
10316 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10317 linkSet.insert( SMESH_TLink( n1, n2 ));
10318 if ( !iter_isnew.second ) { // already in a set: no need to process
10319 linkSet.erase( iter_isnew.first );
10321 else // new in set == encountered for the first time: add
10323 #ifdef DEBUG_MATCHING_NODES
10324 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10325 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10327 linkList[0].push_back ( NLink( n1, n2 ));
10328 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10333 } // loop on link lists
10338 //================================================================================
10340 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10341 \param theElems - the list of elements (edges or faces) to be replicated
10342 The nodes for duplication could be found from these elements
10343 \param theNodesNot - list of nodes to NOT replicate
10344 \param theAffectedElems - the list of elements (cells and edges) to which the
10345 replicated nodes should be associated to.
10346 \return TRUE if operation has been completed successfully, FALSE otherwise
10348 //================================================================================
10350 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10351 const TIDSortedElemSet& theNodesNot,
10352 const TIDSortedElemSet& theAffectedElems )
10354 myLastCreatedElems.Clear();
10355 myLastCreatedNodes.Clear();
10357 if ( theElems.size() == 0 )
10360 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10365 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10366 // duplicate elements and nodes
10367 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10368 // replce nodes by duplications
10369 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10373 //================================================================================
10375 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10376 \param theMeshDS - mesh instance
10377 \param theElems - the elements replicated or modified (nodes should be changed)
10378 \param theNodesNot - nodes to NOT replicate
10379 \param theNodeNodeMap - relation of old node to new created node
10380 \param theIsDoubleElem - flag os to replicate element or modify
10381 \return TRUE if operation has been completed successfully, FALSE otherwise
10383 //================================================================================
10385 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10386 const TIDSortedElemSet& theElems,
10387 const TIDSortedElemSet& theNodesNot,
10388 std::map< const SMDS_MeshNode*,
10389 const SMDS_MeshNode* >& theNodeNodeMap,
10390 const bool theIsDoubleElem )
10392 MESSAGE("doubleNodes");
10393 // iterate on through element and duplicate them (by nodes duplication)
10395 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10396 for ( ; elemItr != theElems.end(); ++elemItr )
10398 const SMDS_MeshElement* anElem = *elemItr;
10402 bool isDuplicate = false;
10403 // duplicate nodes to duplicate element
10404 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10405 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10407 while ( anIter->more() )
10410 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10411 SMDS_MeshNode* aNewNode = aCurrNode;
10412 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10413 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10414 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10417 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10418 theNodeNodeMap[ aCurrNode ] = aNewNode;
10419 myLastCreatedNodes.Append( aNewNode );
10421 isDuplicate |= (aCurrNode != aNewNode);
10422 newNodes[ ind++ ] = aNewNode;
10424 if ( !isDuplicate )
10427 if ( theIsDoubleElem )
10428 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10431 MESSAGE("ChangeElementNodes");
10432 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10439 //================================================================================
10441 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10442 \param theNodes - identifiers of nodes to be doubled
10443 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10444 nodes. If list of element identifiers is empty then nodes are doubled but
10445 they not assigned to elements
10446 \return TRUE if operation has been completed successfully, FALSE otherwise
10448 //================================================================================
10450 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10451 const std::list< int >& theListOfModifiedElems )
10453 MESSAGE("DoubleNodes");
10454 myLastCreatedElems.Clear();
10455 myLastCreatedNodes.Clear();
10457 if ( theListOfNodes.size() == 0 )
10460 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10464 // iterate through nodes and duplicate them
10466 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10468 std::list< int >::const_iterator aNodeIter;
10469 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10471 int aCurr = *aNodeIter;
10472 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10478 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10481 anOldNodeToNewNode[ aNode ] = aNewNode;
10482 myLastCreatedNodes.Append( aNewNode );
10486 // Create map of new nodes for modified elements
10488 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10490 std::list< int >::const_iterator anElemIter;
10491 for ( anElemIter = theListOfModifiedElems.begin();
10492 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10494 int aCurr = *anElemIter;
10495 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10499 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10501 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10503 while ( anIter->more() )
10505 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10506 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10508 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10509 aNodeArr[ ind++ ] = aNewNode;
10512 aNodeArr[ ind++ ] = aCurrNode;
10514 anElemToNodes[ anElem ] = aNodeArr;
10517 // Change nodes of elements
10519 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10520 anElemToNodesIter = anElemToNodes.begin();
10521 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10523 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10524 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10527 MESSAGE("ChangeElementNodes");
10528 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10537 //================================================================================
10539 \brief Check if element located inside shape
10540 \return TRUE if IN or ON shape, FALSE otherwise
10542 //================================================================================
10544 template<class Classifier>
10545 bool isInside(const SMDS_MeshElement* theElem,
10546 Classifier& theClassifier,
10547 const double theTol)
10549 gp_XYZ centerXYZ (0, 0, 0);
10550 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10551 while (aNodeItr->more())
10552 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10554 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10555 theClassifier.Perform(aPnt, theTol);
10556 TopAbs_State aState = theClassifier.State();
10557 return (aState == TopAbs_IN || aState == TopAbs_ON );
10560 //================================================================================
10562 * \brief Classifier of the 3D point on the TopoDS_Face
10563 * with interaface suitable for isInside()
10565 //================================================================================
10567 struct _FaceClassifier
10569 Extrema_ExtPS _extremum;
10570 BRepAdaptor_Surface _surface;
10571 TopAbs_State _state;
10573 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10575 _extremum.Initialize( _surface,
10576 _surface.FirstUParameter(), _surface.LastUParameter(),
10577 _surface.FirstVParameter(), _surface.LastVParameter(),
10578 _surface.Tolerance(), _surface.Tolerance() );
10580 void Perform(const gp_Pnt& aPnt, double theTol)
10582 _state = TopAbs_OUT;
10583 _extremum.Perform(aPnt);
10584 if ( _extremum.IsDone() )
10585 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10586 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10587 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10589 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10592 TopAbs_State State() const
10599 //================================================================================
10601 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10602 \param theElems - group of of elements (edges or faces) to be replicated
10603 \param theNodesNot - group of nodes not to replicate
10604 \param theShape - shape to detect affected elements (element which geometric center
10605 located on or inside shape).
10606 The replicated nodes should be associated to affected elements.
10607 \return TRUE if operation has been completed successfully, FALSE otherwise
10609 //================================================================================
10611 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10612 const TIDSortedElemSet& theNodesNot,
10613 const TopoDS_Shape& theShape )
10615 if ( theShape.IsNull() )
10618 const double aTol = Precision::Confusion();
10619 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10620 auto_ptr<_FaceClassifier> aFaceClassifier;
10621 if ( theShape.ShapeType() == TopAbs_SOLID )
10623 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10624 bsc3d->PerformInfinitePoint(aTol);
10626 else if (theShape.ShapeType() == TopAbs_FACE )
10628 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10631 // iterates on indicated elements and get elements by back references from their nodes
10632 TIDSortedElemSet anAffected;
10633 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10634 for ( ; elemItr != theElems.end(); ++elemItr )
10636 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10640 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10641 while ( nodeItr->more() )
10643 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10644 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10646 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10647 while ( backElemItr->more() )
10649 const SMDS_MeshElement* curElem = backElemItr->next();
10650 if ( curElem && theElems.find(curElem) == theElems.end() &&
10652 isInside( curElem, *bsc3d, aTol ) :
10653 isInside( curElem, *aFaceClassifier, aTol )))
10654 anAffected.insert( curElem );
10658 return DoubleNodes( theElems, theNodesNot, anAffected );
10662 * \brief compute an oriented angle between two planes defined by four points.
10663 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10664 * @param p0 base of the rotation axe
10665 * @param p1 extremity of the rotation axe
10666 * @param g1 belongs to the first plane
10667 * @param g2 belongs to the second plane
10669 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10671 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10672 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10673 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10674 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10675 gp_Vec vref(p0, p1);
10678 gp_Vec n1 = vref.Crossed(v1);
10679 gp_Vec n2 = vref.Crossed(v2);
10680 return n2.AngleWithRef(n1, vref);
10684 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10685 * The list of groups must describe a partition of the mesh volumes.
10686 * The nodes of the internal faces at the boundaries of the groups are doubled.
10687 * In option, the internal faces are replaced by flat elements.
10688 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10689 * The flat elements are stored in groups of volumes.
10690 * @param theElems - list of groups of volumes, where a group of volume is a set of
10691 * SMDS_MeshElements sorted by Id.
10692 * @param createJointElems - if TRUE, create the elements
10693 * @return TRUE if operation has been completed successfully, FALSE otherwise
10695 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10696 bool createJointElems)
10698 MESSAGE("----------------------------------------------");
10699 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10700 MESSAGE("----------------------------------------------");
10702 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10703 meshDS->BuildDownWardConnectivity(true);
10705 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10707 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10708 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10709 // build the list of nodes shared by 2 or more domains, with their domain indexes
10711 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10712 std::map<int,int>celldom; // cell vtkId --> domain
10713 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10714 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10715 faceDomains.clear();
10717 cellDomains.clear();
10718 nodeDomains.clear();
10719 std::map<int,int> emptyMap;
10720 std::set<int> emptySet;
10723 for (int idom = 0; idom < theElems.size(); idom++)
10726 // --- build a map (face to duplicate --> volume to modify)
10727 // with all the faces shared by 2 domains (group of elements)
10728 // and corresponding volume of this domain, for each shared face.
10729 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
10731 //MESSAGE("Domain " << idom);
10732 const TIDSortedElemSet& domain = theElems[idom];
10733 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10734 for (; elemItr != domain.end(); ++elemItr)
10736 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10739 int vtkId = anElem->getVtkId();
10740 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10741 int neighborsVtkIds[NBMAXNEIGHBORS];
10742 int downIds[NBMAXNEIGHBORS];
10743 unsigned char downTypes[NBMAXNEIGHBORS];
10744 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10745 for (int n = 0; n < nbNeighbors; n++)
10747 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10748 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10749 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10751 DownIdType face(downIds[n], downTypes[n]);
10752 if (!faceDomains.count(face))
10753 faceDomains[face] = emptyMap; // create an empty entry for face
10754 if (!faceDomains[face].count(idom))
10756 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10757 celldom[vtkId] = idom;
10758 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10765 //MESSAGE("Number of shared faces " << faceDomains.size());
10766 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10768 // --- explore the shared faces domain by domain,
10769 // explore the nodes of the face and see if they belong to a cell in the domain,
10770 // which has only a node or an edge on the border (not a shared face)
10772 for (int idomain = 0; idomain < theElems.size(); idomain++)
10774 //MESSAGE("Domain " << idomain);
10775 const TIDSortedElemSet& domain = theElems[idomain];
10776 itface = faceDomains.begin();
10777 for (; itface != faceDomains.end(); ++itface)
10779 std::map<int, int> domvol = itface->second;
10780 if (!domvol.count(idomain))
10782 DownIdType face = itface->first;
10783 //MESSAGE(" --- face " << face.cellId);
10784 std::set<int> oldNodes;
10786 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10787 std::set<int>::iterator itn = oldNodes.begin();
10788 for (; itn != oldNodes.end(); ++itn)
10791 //MESSAGE(" node " << oldId);
10792 std::set<int> cells;
10794 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10795 for (int i=0; i<l.ncells; i++)
10797 int vtkId = l.cells[i];
10798 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10799 if (!domain.count(anElem))
10801 int vtkType = grid->GetCellType(vtkId);
10802 int downId = grid->CellIdToDownId(vtkId);
10805 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10806 continue; // not OK at this stage of the algorithm:
10807 //no cells created after BuildDownWardConnectivity
10809 DownIdType aCell(downId, vtkType);
10810 if (celldom.count(vtkId))
10812 cellDomains[aCell][idomain] = vtkId;
10813 celldom[vtkId] = idomain;
10814 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10820 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10821 // for each shared face, get the nodes
10822 // for each node, for each domain of the face, create a clone of the node
10824 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10825 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10826 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10828 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10829 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10830 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10832 for (int idomain = 0; idomain < theElems.size(); idomain++)
10834 itface = faceDomains.begin();
10835 for (; itface != faceDomains.end(); ++itface)
10837 std::map<int, int> domvol = itface->second;
10838 if (!domvol.count(idomain))
10840 DownIdType face = itface->first;
10841 //MESSAGE(" --- face " << face.cellId);
10842 std::set<int> oldNodes;
10844 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10845 bool isMultipleDetected = false;
10846 std::set<int>::iterator itn = oldNodes.begin();
10847 for (; itn != oldNodes.end(); ++itn)
10850 //MESSAGE(" node " << oldId);
10851 if (!nodeDomains.count(oldId))
10852 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10853 if (nodeDomains[oldId].empty())
10854 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10855 std::map<int, int>::iterator itdom = domvol.begin();
10856 for (; itdom != domvol.end(); ++itdom)
10858 int idom = itdom->first;
10859 //MESSAGE(" domain " << idom);
10860 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10862 if (nodeDomains[oldId].size() >= 2) // a multiple node
10864 vector<int> orderedDoms;
10865 //MESSAGE("multiple node " << oldId);
10866 isMultipleDetected =true;
10867 if (mutipleNodes.count(oldId))
10868 orderedDoms = mutipleNodes[oldId];
10871 map<int,int>::iterator it = nodeDomains[oldId].begin();
10872 for (; it != nodeDomains[oldId].end(); ++it)
10873 orderedDoms.push_back(it->first);
10875 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10876 //stringstream txt;
10877 //for (int i=0; i<orderedDoms.size(); i++)
10878 // txt << orderedDoms[i] << " ";
10879 //MESSAGE("orderedDoms " << txt.str());
10880 mutipleNodes[oldId] = orderedDoms;
10882 double *coords = grid->GetPoint(oldId);
10883 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10884 int newId = newNode->getVtkId();
10885 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10886 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
10888 if (nodeDomains[oldId].size() >= 3)
10890 //MESSAGE("confirm multiple node " << oldId);
10891 isMultipleDetected =true;
10895 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
10897 //MESSAGE("multiple Nodes detected on a shared face");
10898 int downId = itface->first.cellId;
10899 unsigned char cellType = itface->first.cellType;
10900 // --- shared edge or shared face ?
10901 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10904 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10905 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10906 if (mutipleNodes.count(nodes[i]))
10907 if (!mutipleNodesToFace.count(nodes[i]))
10908 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10910 else // shared face (between two volumes)
10912 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10913 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10914 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10915 for (int ie =0; ie < nbEdges; ie++)
10918 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10919 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10921 vector<int> vn0 = mutipleNodes[nodes[0]];
10922 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10923 sort( vn0.begin(), vn0.end() );
10924 sort( vn1.begin(), vn1.end() );
10927 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10928 double *coords = grid->GetPoint(nodes[0]);
10929 gp_Pnt p0(coords[0], coords[1], coords[2]);
10930 coords = grid->GetPoint(nodes[nbNodes - 1]);
10931 gp_Pnt p1(coords[0], coords[1], coords[2]);
10933 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10934 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10935 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10936 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10937 for (int id=0; id < vn0.size(); id++)
10939 int idom = vn0[id];
10940 for (int ivol=0; ivol<nbvol; ivol++)
10942 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10943 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10944 if (theElems[idom].count(elem))
10946 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10947 domvol[idom] = svol;
10948 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10950 vtkIdType npts = 0;
10951 vtkIdType* pts = 0;
10952 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10953 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10956 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10957 angleDom[idom] = 0;
10961 gp_Pnt g(values[0], values[1], values[2]);
10962 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10963 //MESSAGE(" angle=" << angleDom[idom]);
10969 map<double, int> sortedDom; // sort domains by angle
10970 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10971 sortedDom[ia->second] = ia->first;
10972 vector<int> vnodes;
10974 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10976 vdom.push_back(ib->second);
10977 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10979 for (int ino = 0; ino < nbNodes; ino++)
10980 vnodes.push_back(nodes[ino]);
10981 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10990 // --- iterate on shared faces (volumes to modify, face to extrude)
10991 // get node id's of the face (id SMDS = id VTK)
10992 // create flat element with old and new nodes if requested
10994 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10995 // (domain1 X domain2) = domain1 + MAXINT*domain2
10997 std::map<int, std::map<long,int> > nodeQuadDomains;
10998 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11000 if (createJointElems)
11002 itface = faceDomains.begin();
11003 for (; itface != faceDomains.end(); ++itface)
11005 DownIdType face = itface->first;
11006 std::set<int> oldNodes;
11007 std::set<int>::iterator itn;
11009 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11011 std::map<int, int> domvol = itface->second;
11012 std::map<int, int>::iterator itdom = domvol.begin();
11013 int dom1 = itdom->first;
11014 int vtkVolId = itdom->second;
11016 int dom2 = itdom->first;
11017 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11019 stringstream grpname;
11022 grpname << dom1 << "_" << dom2;
11024 grpname << dom2 << "_" << dom1;
11026 string namegrp = grpname.str();
11027 if (!mapOfJunctionGroups.count(namegrp))
11028 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11029 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11031 sgrp->Add(vol->GetID());
11035 // --- create volumes on multiple domain intersection if requested
11036 // iterate on mutipleNodesToFace
11037 // iterate on edgesMultiDomains
11039 if (createJointElems)
11041 // --- iterate on mutipleNodesToFace
11043 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11044 for (; itn != mutipleNodesToFace.end(); ++itn)
11046 int node = itn->first;
11047 vector<int> orderDom = itn->second;
11048 vector<vtkIdType> orderedNodes;
11049 for (int idom = 0; idom <orderDom.size(); idom++)
11050 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11051 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11053 stringstream grpname;
11055 grpname << 0 << "_" << 0;
11057 string namegrp = grpname.str();
11058 if (!mapOfJunctionGroups.count(namegrp))
11059 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11060 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11062 sgrp->Add(face->GetID());
11065 // --- iterate on edgesMultiDomains
11067 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11068 for (; ite != edgesMultiDomains.end(); ++ite)
11070 vector<int> nodes = ite->first;
11071 vector<int> orderDom = ite->second;
11072 vector<vtkIdType> orderedNodes;
11073 if (nodes.size() == 2)
11075 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11076 for (int ino=0; ino < nodes.size(); ino++)
11077 if (orderDom.size() == 3)
11078 for (int idom = 0; idom <orderDom.size(); idom++)
11079 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11081 for (int idom = orderDom.size()-1; idom >=0; idom--)
11082 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11083 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11085 stringstream grpname;
11087 grpname << 0 << "_" << 0;
11089 string namegrp = grpname.str();
11090 if (!mapOfJunctionGroups.count(namegrp))
11091 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11092 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11094 sgrp->Add(vol->GetID());
11098 MESSAGE("Quadratic multiple joints not implemented");
11099 // TODO quadratic nodes
11104 // --- list the explicit faces and edges of the mesh that need to be modified,
11105 // i.e. faces and edges built with one or more duplicated nodes.
11106 // associate these faces or edges to their corresponding domain.
11107 // only the first domain found is kept when a face or edge is shared
11109 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11110 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11111 faceOrEdgeDom.clear();
11114 for (int idomain = 0; idomain < theElems.size(); idomain++)
11116 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11117 for (; itnod != nodeDomains.end(); ++itnod)
11119 int oldId = itnod->first;
11120 //MESSAGE(" node " << oldId);
11121 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11122 for (int i = 0; i < l.ncells; i++)
11124 int vtkId = l.cells[i];
11125 int vtkType = grid->GetCellType(vtkId);
11126 int downId = grid->CellIdToDownId(vtkId);
11128 continue; // new cells: not to be modified
11129 DownIdType aCell(downId, vtkType);
11130 int volParents[1000];
11131 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11132 for (int j = 0; j < nbvol; j++)
11133 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11134 if (!feDom.count(vtkId))
11136 feDom[vtkId] = idomain;
11137 faceOrEdgeDom[aCell] = emptyMap;
11138 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11139 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11140 // << " type " << vtkType << " downId " << downId);
11146 // --- iterate on shared faces (volumes to modify, face to extrude)
11147 // get node id's of the face
11148 // replace old nodes by new nodes in volumes, and update inverse connectivity
11150 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11151 for (int m=0; m<3; m++)
11153 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11154 itface = (*amap).begin();
11155 for (; itface != (*amap).end(); ++itface)
11157 DownIdType face = itface->first;
11158 std::set<int> oldNodes;
11159 std::set<int>::iterator itn;
11161 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11162 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11163 std::map<int, int> localClonedNodeIds;
11165 std::map<int, int> domvol = itface->second;
11166 std::map<int, int>::iterator itdom = domvol.begin();
11167 for (; itdom != domvol.end(); ++itdom)
11169 int idom = itdom->first;
11170 int vtkVolId = itdom->second;
11171 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11172 localClonedNodeIds.clear();
11173 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11176 if (nodeDomains[oldId].count(idom))
11178 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11179 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11182 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11187 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11188 grid->BuildLinks();
11196 * \brief Double nodes on some external faces and create flat elements.
11197 * Flat elements are mainly used by some types of mechanic calculations.
11199 * Each group of the list must be constituted of faces.
11200 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11201 * @param theElems - list of groups of faces, where a group of faces is a set of
11202 * SMDS_MeshElements sorted by Id.
11203 * @return TRUE if operation has been completed successfully, FALSE otherwise
11205 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11207 MESSAGE("-------------------------------------------------");
11208 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11209 MESSAGE("-------------------------------------------------");
11211 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11213 // --- For each group of faces
11214 // duplicate the nodes, create a flat element based on the face
11215 // replace the nodes of the faces by their clones
11217 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11218 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11219 clonedNodes.clear();
11220 intermediateNodes.clear();
11221 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11222 mapOfJunctionGroups.clear();
11224 for (int idom = 0; idom < theElems.size(); idom++)
11226 const TIDSortedElemSet& domain = theElems[idom];
11227 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11228 for (; elemItr != domain.end(); ++elemItr)
11230 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11231 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11234 // MESSAGE("aFace=" << aFace->GetID());
11235 bool isQuad = aFace->IsQuadratic();
11236 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11238 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11240 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11241 while (nodeIt->more())
11243 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11244 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11246 ln2.push_back(node);
11248 ln0.push_back(node);
11250 const SMDS_MeshNode* clone = 0;
11251 if (!clonedNodes.count(node))
11253 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11254 clonedNodes[node] = clone;
11257 clone = clonedNodes[node];
11260 ln3.push_back(clone);
11262 ln1.push_back(clone);
11264 const SMDS_MeshNode* inter = 0;
11265 if (isQuad && (!isMedium))
11267 if (!intermediateNodes.count(node))
11269 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11270 intermediateNodes[node] = inter;
11273 inter = intermediateNodes[node];
11274 ln4.push_back(inter);
11278 // --- extrude the face
11280 vector<const SMDS_MeshNode*> ln;
11281 SMDS_MeshVolume* vol = 0;
11282 vtkIdType aType = aFace->GetVtkType();
11286 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11287 // MESSAGE("vol prism " << vol->GetID());
11288 ln.push_back(ln1[0]);
11289 ln.push_back(ln1[1]);
11290 ln.push_back(ln1[2]);
11293 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11294 // MESSAGE("vol hexa " << vol->GetID());
11295 ln.push_back(ln1[0]);
11296 ln.push_back(ln1[1]);
11297 ln.push_back(ln1[2]);
11298 ln.push_back(ln1[3]);
11300 case VTK_QUADRATIC_TRIANGLE:
11301 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11302 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11303 // MESSAGE("vol quad prism " << vol->GetID());
11304 ln.push_back(ln1[0]);
11305 ln.push_back(ln1[1]);
11306 ln.push_back(ln1[2]);
11307 ln.push_back(ln3[0]);
11308 ln.push_back(ln3[1]);
11309 ln.push_back(ln3[2]);
11311 case VTK_QUADRATIC_QUAD:
11312 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11313 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11314 // ln4[0], ln4[1], ln4[2], ln4[3]);
11315 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11316 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11317 ln4[0], ln4[1], ln4[2], ln4[3]);
11318 // MESSAGE("vol quad hexa " << vol->GetID());
11319 ln.push_back(ln1[0]);
11320 ln.push_back(ln1[1]);
11321 ln.push_back(ln1[2]);
11322 ln.push_back(ln1[3]);
11323 ln.push_back(ln3[0]);
11324 ln.push_back(ln3[1]);
11325 ln.push_back(ln3[2]);
11326 ln.push_back(ln3[3]);
11336 stringstream grpname;
11340 string namegrp = grpname.str();
11341 if (!mapOfJunctionGroups.count(namegrp))
11342 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11343 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11345 sgrp->Add(vol->GetID());
11348 // --- modify the face
11350 aFace->ChangeNodes(&ln[0], ln.size());
11356 //================================================================================
11358 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11359 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11360 * \return TRUE if operation has been completed successfully, FALSE otherwise
11362 //================================================================================
11364 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11366 // iterates on volume elements and detect all free faces on them
11367 SMESHDS_Mesh* aMesh = GetMeshDS();
11370 //bool res = false;
11371 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11372 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11375 const SMDS_MeshVolume* volume = vIt->next();
11376 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11377 vTool.SetExternalNormal();
11378 //const bool isPoly = volume->IsPoly();
11379 const int iQuad = volume->IsQuadratic();
11380 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11382 if (!vTool.IsFreeFace(iface))
11385 vector<const SMDS_MeshNode *> nodes;
11386 int nbFaceNodes = vTool.NbFaceNodes(iface);
11387 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11389 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11390 nodes.push_back(faceNodes[inode]);
11391 if (iQuad) { // add medium nodes
11392 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11393 nodes.push_back(faceNodes[inode]);
11394 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11395 nodes.push_back(faceNodes[8]);
11397 // add new face based on volume nodes
11398 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11400 continue; // face already exsist
11402 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11406 return ( nbFree==(nbExisted+nbCreated) );
11411 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11413 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11415 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11418 //================================================================================
11420 * \brief Creates missing boundary elements
11421 * \param elements - elements whose boundary is to be checked
11422 * \param dimension - defines type of boundary elements to create
11423 * \param group - a group to store created boundary elements in
11424 * \param targetMesh - a mesh to store created boundary elements in
11425 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11426 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11427 * boundary elements will be copied into the targetMesh
11428 * \param toAddExistingBondary - if true, not only new but also pre-existing
11429 * boundary elements will be added into the new group
11430 * \param aroundElements - if true, elements will be created on boundary of given
11431 * elements else, on boundary of the whole mesh.
11432 * \return nb of added boundary elements
11434 //================================================================================
11436 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11437 Bnd_Dimension dimension,
11438 SMESH_Group* group/*=0*/,
11439 SMESH_Mesh* targetMesh/*=0*/,
11440 bool toCopyElements/*=false*/,
11441 bool toCopyExistingBoundary/*=false*/,
11442 bool toAddExistingBondary/*= false*/,
11443 bool aroundElements/*= false*/)
11445 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11446 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11447 // hope that all elements are of the same type, do not check them all
11448 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11449 throw SALOME_Exception(LOCALIZED("wrong element type"));
11452 toCopyElements = toCopyExistingBoundary = false;
11454 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11455 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11456 int nbAddedBnd = 0;
11458 // editor adding present bnd elements and optionally holding elements to add to the group
11459 SMESH_MeshEditor* presentEditor;
11460 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11461 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11463 SMESH_MesherHelper helper( *myMesh );
11464 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11465 SMDS_VolumeTool vTool;
11466 TIDSortedElemSet avoidSet;
11467 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11470 typedef vector<const SMDS_MeshNode*> TConnectivity;
11472 SMDS_ElemIteratorPtr eIt;
11473 if (elements.empty())
11474 eIt = aMesh->elementsIterator(elemType);
11476 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11478 while (eIt->more())
11480 const SMDS_MeshElement* elem = eIt->next();
11481 const int iQuad = elem->IsQuadratic();
11483 // ------------------------------------------------------------------------------------
11484 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11485 // ------------------------------------------------------------------------------------
11486 vector<const SMDS_MeshElement*> presentBndElems;
11487 vector<TConnectivity> missingBndElems;
11488 TConnectivity nodes;
11489 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11491 vTool.SetExternalNormal();
11492 const SMDS_MeshElement* otherVol = 0;
11493 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11495 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11496 ( !aroundElements || elements.count( otherVol )))
11498 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11499 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11500 if ( missType == SMDSAbs_Edge ) // boundary edges
11502 nodes.resize( 2+iQuad );
11503 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11505 for ( int j = 0; j < nodes.size(); ++j )
11507 if ( const SMDS_MeshElement* edge =
11508 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11509 presentBndElems.push_back( edge );
11511 missingBndElems.push_back( nodes );
11514 else // boundary face
11517 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11518 nodes.push_back( nn[inode] );
11519 if (iQuad) // add medium nodes
11520 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11521 nodes.push_back( nn[inode] );
11522 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11524 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11526 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11527 SMDSAbs_Face, /*noMedium=*/false ))
11528 presentBndElems.push_back( f );
11530 missingBndElems.push_back( nodes );
11532 if ( targetMesh != myMesh )
11534 // add 1D elements on face boundary to be added to a new mesh
11535 const SMDS_MeshElement* edge;
11536 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11539 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11541 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11542 if ( edge && avoidSet.insert( edge ).second )
11543 presentBndElems.push_back( edge );
11549 else // elem is a face ------------------------------------------
11551 avoidSet.clear(), avoidSet.insert( elem );
11552 int nbNodes = elem->NbCornerNodes();
11553 nodes.resize( 2 /*+ iQuad*/);
11554 for ( int i = 0; i < nbNodes; i++ )
11556 nodes[0] = elem->GetNode(i);
11557 nodes[1] = elem->GetNode((i+1)%nbNodes);
11558 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11559 continue; // not free link
11562 //nodes[2] = elem->GetNode( i + nbNodes );
11563 if ( const SMDS_MeshElement* edge =
11564 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11565 presentBndElems.push_back( edge );
11567 missingBndElems.push_back( nodes );
11571 // ---------------------------------
11572 // 2. Add missing boundary elements
11573 // ---------------------------------
11574 if ( targetMesh != myMesh )
11575 // instead of making a map of nodes in this mesh and targetMesh,
11576 // we create nodes with same IDs.
11577 for ( int i = 0; i < missingBndElems.size(); ++i )
11579 TConnectivity& srcNodes = missingBndElems[i];
11580 TConnectivity nodes( srcNodes.size() );
11581 for ( inode = 0; inode < nodes.size(); ++inode )
11582 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11583 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11585 /*noMedium=*/false))
11587 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11591 for ( int i = 0; i < missingBndElems.size(); ++i )
11593 TConnectivity& nodes = missingBndElems[i];
11594 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11596 /*noMedium=*/false))
11598 SMDS_MeshElement* elem =
11599 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11602 // try to set a new element to a shape
11603 if ( myMesh->HasShapeToMesh() )
11606 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11607 const int nbN = nodes.size() / (iQuad+1 );
11608 for ( inode = 0; inode < nbN && ok; ++inode )
11610 pair<int, TopAbs_ShapeEnum> i_stype =
11611 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11612 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11613 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11615 if ( ok && mediumShapes.size() > 1 )
11617 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11618 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11619 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11621 if (( ok = ( stype_i->first != stype_i_0.first )))
11622 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11623 aMesh->IndexToShape( stype_i_0.second ));
11626 if ( ok && mediumShapes.begin()->first == missShapeType )
11627 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11631 // ----------------------------------
11632 // 3. Copy present boundary elements
11633 // ----------------------------------
11634 if ( toCopyExistingBoundary )
11635 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11637 const SMDS_MeshElement* e = presentBndElems[i];
11638 TConnectivity nodes( e->NbNodes() );
11639 for ( inode = 0; inode < nodes.size(); ++inode )
11640 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11641 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11643 else // store present elements to add them to a group
11644 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11646 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11649 } // loop on given elements
11651 // ---------------------------------------------
11652 // 4. Fill group with boundary elements
11653 // ---------------------------------------------
11656 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11657 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11658 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11660 tgtEditor.myLastCreatedElems.Clear();
11661 tgtEditor2.myLastCreatedElems.Clear();
11663 // -----------------------
11664 // 5. Copy given elements
11665 // -----------------------
11666 if ( toCopyElements && targetMesh != myMesh )
11668 if (elements.empty())
11669 eIt = aMesh->elementsIterator(elemType);
11671 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11672 while (eIt->more())
11674 const SMDS_MeshElement* elem = eIt->next();
11675 TConnectivity nodes( elem->NbNodes() );
11676 for ( inode = 0; inode < nodes.size(); ++inode )
11677 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11678 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11680 tgtEditor.myLastCreatedElems.Clear();