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 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
53 #include <BRepAdaptor_Surface.hxx>
54 #include <BRepBuilderAPI_MakeEdge.hxx>
55 #include <BRepClass3d_SolidClassifier.hxx>
56 #include <BRep_Tool.hxx>
58 #include <Extrema_GenExtPS.hxx>
59 #include <Extrema_POnCurv.hxx>
60 #include <Extrema_POnSurf.hxx>
61 #include <GC_MakeSegment.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAPI_ExtremaCurveCurve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Surface.hxx>
68 #include <IntAna_IntConicQuad.hxx>
69 #include <IntAna_Quadric.hxx>
70 #include <Precision.hxx>
71 #include <TColStd_ListOfInteger.hxx>
72 #include <TopAbs_State.hxx>
74 #include <TopExp_Explorer.hxx>
75 #include <TopTools_ListIteratorOfListOfShape.hxx>
76 #include <TopTools_ListOfShape.hxx>
77 #include <TopTools_SequenceOfShape.hxx>
79 #include <TopoDS_Face.hxx>
80 #include <TopoDS_Solid.hxx>
86 #include <gp_Trsf.hxx>
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshNode*> > TElemOfNodeListMap;
109 typedef map<const SMDS_MeshElement*, list<const SMDS_MeshElement*> > TElemOfElemListMap;
111 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
113 //=======================================================================
114 //function : SMESH_MeshEditor
116 //=======================================================================
118 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
119 :myMesh( theMesh ) // theMesh may be NULL
123 //=======================================================================
127 //=======================================================================
130 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
131 const SMDSAbs_ElementType type,
134 const double ballDiameter)
136 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
137 SMDS_MeshElement* e = 0;
138 int nbnode = node.size();
139 SMESHDS_Mesh* mesh = GetMeshDS();
144 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
145 else e = mesh->AddFace (node[0], node[1], node[2] );
147 else if (nbnode == 4) {
148 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
149 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
151 else if (nbnode == 6) {
152 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
153 node[4], node[5], ID);
154 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
157 else if (nbnode == 8) {
158 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
159 node[4], node[5], node[6], node[7], ID);
160 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
161 node[4], node[5], node[6], node[7] );
163 else if (nbnode == 9) {
164 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
165 node[4], node[5], node[6], node[7], node[8], ID);
166 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
167 node[4], node[5], node[6], node[7], node[8] );
170 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
171 else e = mesh->AddPolygonalFace (node );
178 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
179 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
181 else if (nbnode == 5) {
182 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
184 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
187 else if (nbnode == 6) {
188 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
189 node[4], node[5], ID);
190 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
193 else if (nbnode == 8) {
194 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
195 node[4], node[5], node[6], node[7], ID);
196 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
197 node[4], node[5], node[6], node[7] );
199 else if (nbnode == 10) {
200 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
201 node[4], node[5], node[6], node[7],
202 node[8], node[9], ID);
203 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
204 node[4], node[5], node[6], node[7],
207 else if (nbnode == 12) {
208 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], node[7],
210 node[8], node[9], node[10], node[11], ID);
211 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6], node[7],
213 node[8], node[9], node[10], node[11] );
215 else if (nbnode == 13) {
216 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],
220 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7],
222 node[8], node[9], node[10],node[11],
225 else if (nbnode == 15) {
226 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],ID);
230 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
231 node[4], node[5], node[6], node[7],
232 node[8], node[9], node[10],node[11],
233 node[12],node[13],node[14] );
235 else if (nbnode == 20) {
236 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
237 node[4], node[5], node[6], node[7],
238 node[8], node[9], node[10],node[11],
239 node[12],node[13],node[14],node[15],
240 node[16],node[17],node[18],node[19],ID);
241 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
242 node[4], node[5], node[6], node[7],
243 node[8], node[9], node[10],node[11],
244 node[12],node[13],node[14],node[15],
245 node[16],node[17],node[18],node[19] );
247 else if (nbnode == 27) {
248 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249 node[4], node[5], node[6], node[7],
250 node[8], node[9], node[10],node[11],
251 node[12],node[13],node[14],node[15],
252 node[16],node[17],node[18],node[19],
253 node[20],node[21],node[22],node[23],
254 node[24],node[25],node[26], ID);
255 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 node[4], node[5], node[6], node[7],
257 node[8], node[9], node[10],node[11],
258 node[12],node[13],node[14],node[15],
259 node[16],node[17],node[18],node[19],
260 node[20],node[21],node[22],node[23],
261 node[24],node[25],node[26] );
268 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
269 else e = mesh->AddEdge (node[0], node[1] );
271 else if ( nbnode == 3 ) {
272 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
273 else e = mesh->AddEdge (node[0], node[1], node[2] );
277 case SMDSAbs_0DElement:
279 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
280 else e = mesh->Add0DElement (node[0] );
285 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
286 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
290 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
291 else e = mesh->AddBall (node[0], ballDiameter);
296 if ( e ) myLastCreatedElems.Append( e );
300 //=======================================================================
304 //=======================================================================
306 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
307 const SMDSAbs_ElementType type,
311 vector<const SMDS_MeshNode*> nodes;
312 nodes.reserve( nodeIDs.size() );
313 vector<int>::const_iterator id = nodeIDs.begin();
314 while ( id != nodeIDs.end() ) {
315 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
316 nodes.push_back( node );
320 return AddElement( nodes, type, isPoly, ID );
323 //=======================================================================
325 //purpose : Remove a node or an element.
326 // Modify a compute state of sub-meshes which become empty
327 //=======================================================================
329 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
332 myLastCreatedElems.Clear();
333 myLastCreatedNodes.Clear();
335 SMESHDS_Mesh* aMesh = GetMeshDS();
336 set< SMESH_subMesh *> smmap;
339 list<int>::const_iterator it = theIDs.begin();
340 for ( ; it != theIDs.end(); it++ ) {
341 const SMDS_MeshElement * elem;
343 elem = aMesh->FindNode( *it );
345 elem = aMesh->FindElement( *it );
349 // Notify VERTEX sub-meshes about modification
351 const SMDS_MeshNode* node = cast2Node( elem );
352 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
353 if ( int aShapeID = node->getshapeId() )
354 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
357 // Find sub-meshes to notify about modification
358 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
359 // while ( nodeIt->more() ) {
360 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
361 // const SMDS_PositionPtr& aPosition = node->GetPosition();
362 // if ( aPosition.get() ) {
363 // if ( int aShapeID = aPosition->GetShapeId() ) {
364 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
365 // smmap.insert( sm );
372 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
374 aMesh->RemoveElement( elem );
378 // Notify sub-meshes about modification
379 if ( !smmap.empty() ) {
380 set< SMESH_subMesh *>::iterator smIt;
381 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
382 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
385 // // Check if the whole mesh becomes empty
386 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
387 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
392 //=======================================================================
393 //function : FindShape
394 //purpose : Return an index of the shape theElem is on
395 // or zero if a shape not found
396 //=======================================================================
398 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
400 myLastCreatedElems.Clear();
401 myLastCreatedNodes.Clear();
403 SMESHDS_Mesh * aMesh = GetMeshDS();
404 if ( aMesh->ShapeToMesh().IsNull() )
407 int aShapeID = theElem->getshapeId();
411 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
412 if ( sm->Contains( theElem ))
415 if ( theElem->GetType() == SMDSAbs_Node ) {
416 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
419 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
422 TopoDS_Shape aShape; // the shape a node of theElem is on
423 if ( theElem->GetType() != SMDSAbs_Node )
425 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
426 while ( nodeIt->more() ) {
427 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
428 if ((aShapeID = node->getshapeId()) > 0) {
429 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
430 if ( sm->Contains( theElem ))
432 if ( aShape.IsNull() )
433 aShape = aMesh->IndexToShape( aShapeID );
439 // None of nodes is on a proper shape,
440 // find the shape among ancestors of aShape on which a node is
441 if ( !aShape.IsNull() ) {
442 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
443 for ( ; ancIt.More(); ancIt.Next() ) {
444 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
445 if ( sm && sm->Contains( theElem ))
446 return aMesh->ShapeToIndex( ancIt.Value() );
451 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
452 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
453 for ( ; id_sm != id2sm.end(); ++id_sm )
454 if ( id_sm->second->Contains( theElem ))
458 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
462 //=======================================================================
463 //function : IsMedium
465 //=======================================================================
467 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
468 const SMDSAbs_ElementType typeToCheck)
470 bool isMedium = false;
471 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
472 while (it->more() && !isMedium ) {
473 const SMDS_MeshElement* elem = it->next();
474 isMedium = elem->IsMediumNode(node);
479 //=======================================================================
480 //function : ShiftNodesQuadTria
482 // Shift nodes in the array corresponded to quadratic triangle
483 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
484 //=======================================================================
485 static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[])
487 const SMDS_MeshNode* nd1 = aNodes[0];
488 aNodes[0] = aNodes[1];
489 aNodes[1] = aNodes[2];
491 const SMDS_MeshNode* nd2 = aNodes[3];
492 aNodes[3] = aNodes[4];
493 aNodes[4] = aNodes[5];
497 //=======================================================================
498 //function : edgeConnectivity
500 // return number of the edges connected with the theNode.
501 // if theEdges has connections with the other type of the
502 // elements, return -1
503 //=======================================================================
504 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
506 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
508 while(elemIt->more()) {
516 //=======================================================================
517 //function : GetNodesFromTwoTria
519 // Shift nodes in the array corresponded to quadratic triangle
520 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
521 //=======================================================================
522 static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1,
523 const SMDS_MeshElement * theTria2,
524 const SMDS_MeshNode* N1[],
525 const SMDS_MeshNode* N2[])
527 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
530 N1[i] = static_cast<const SMDS_MeshNode*>( it->next() );
533 if(it->more()) return false;
534 it = theTria2->nodesIterator();
537 N2[i] = static_cast<const SMDS_MeshNode*>( it->next() );
540 if(it->more()) return false;
542 int sames[3] = {-1,-1,-1};
554 if(nbsames!=2) return false;
556 ShiftNodesQuadTria(N1);
558 ShiftNodesQuadTria(N1);
561 i = sames[0] + sames[1] + sames[2];
563 ShiftNodesQuadTria(N2);
565 // now we receive following N1 and N2 (using numeration as above image)
566 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
567 // i.e. first nodes from both arrays determ new diagonal
571 //=======================================================================
572 //function : InverseDiag
573 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
574 // but having other common link.
575 // Return False if args are improper
576 //=======================================================================
578 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
579 const SMDS_MeshElement * theTria2 )
581 MESSAGE("InverseDiag");
582 myLastCreatedElems.Clear();
583 myLastCreatedNodes.Clear();
585 if (!theTria1 || !theTria2)
588 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
589 if (!F1) return false;
590 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
591 if (!F2) return false;
592 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
593 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
595 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
596 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
600 // put nodes in array and find out indices of the same ones
601 const SMDS_MeshNode* aNodes [6];
602 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
604 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
605 while ( it->more() ) {
606 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
608 if ( i > 2 ) // theTria2
609 // find same node of theTria1
610 for ( int j = 0; j < 3; j++ )
611 if ( aNodes[ i ] == aNodes[ j ]) {
620 return false; // theTria1 is not a triangle
621 it = theTria2->nodesIterator();
623 if ( i == 6 && it->more() )
624 return false; // theTria2 is not a triangle
627 // find indices of 1,2 and of A,B in theTria1
628 int iA = 0, iB = 0, i1 = 0, i2 = 0;
629 for ( i = 0; i < 6; i++ ) {
630 if ( sameInd [ i ] == 0 ) {
639 // nodes 1 and 2 should not be the same
640 if ( aNodes[ i1 ] == aNodes[ i2 ] )
644 aNodes[ iA ] = aNodes[ i2 ];
646 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
648 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
649 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
653 } // end if(F1 && F2)
655 // check case of quadratic faces
656 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle)
658 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle)
662 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
663 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
671 const SMDS_MeshNode* N1 [6];
672 const SMDS_MeshNode* N2 [6];
673 if(!GetNodesFromTwoTria(theTria1,theTria2,N1,N2))
675 // now we receive following N1 and N2 (using numeration as above image)
676 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
677 // i.e. first nodes from both arrays determ new diagonal
679 const SMDS_MeshNode* N1new [6];
680 const SMDS_MeshNode* N2new [6];
693 // replaces nodes in faces
694 GetMeshDS()->ChangeElementNodes( theTria1, N1new, 6 );
695 GetMeshDS()->ChangeElementNodes( theTria2, N2new, 6 );
700 //=======================================================================
701 //function : findTriangles
702 //purpose : find triangles sharing theNode1-theNode2 link
703 //=======================================================================
705 static bool findTriangles(const SMDS_MeshNode * theNode1,
706 const SMDS_MeshNode * theNode2,
707 const SMDS_MeshElement*& theTria1,
708 const SMDS_MeshElement*& theTria2)
710 if ( !theNode1 || !theNode2 ) return false;
712 theTria1 = theTria2 = 0;
714 set< const SMDS_MeshElement* > emap;
715 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
717 const SMDS_MeshElement* elem = it->next();
718 if ( elem->NbNodes() == 3 )
721 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
723 const SMDS_MeshElement* elem = it->next();
724 if ( emap.find( elem ) != emap.end() ) {
726 // theTria1 must be element with minimum ID
727 if( theTria1->GetID() < elem->GetID() ) {
741 return ( theTria1 && theTria2 );
744 //=======================================================================
745 //function : InverseDiag
746 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
747 // with ones built on the same 4 nodes but having other common link.
748 // Return false if proper faces not found
749 //=======================================================================
751 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
752 const SMDS_MeshNode * theNode2)
754 myLastCreatedElems.Clear();
755 myLastCreatedNodes.Clear();
757 MESSAGE( "::InverseDiag()" );
759 const SMDS_MeshElement *tr1, *tr2;
760 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
763 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
764 if (!F1) return false;
765 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
766 if (!F2) return false;
767 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
768 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
770 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
771 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
775 // put nodes in array
776 // and find indices of 1,2 and of A in tr1 and of B in tr2
777 int i, iA1 = 0, i1 = 0;
778 const SMDS_MeshNode* aNodes1 [3];
779 SMDS_ElemIteratorPtr it;
780 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
781 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
782 if ( aNodes1[ i ] == theNode1 )
783 iA1 = i; // node A in tr1
784 else if ( aNodes1[ i ] != theNode2 )
788 const SMDS_MeshNode* aNodes2 [3];
789 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
790 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
791 if ( aNodes2[ i ] == theNode2 )
792 iB2 = i; // node B in tr2
793 else if ( aNodes2[ i ] != theNode1 )
797 // nodes 1 and 2 should not be the same
798 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
802 aNodes1[ iA1 ] = aNodes2[ i2 ];
804 aNodes2[ iB2 ] = aNodes1[ i1 ];
806 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
807 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
812 // check case of quadratic faces
813 return InverseDiag(tr1,tr2);
816 //=======================================================================
817 //function : getQuadrangleNodes
818 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
819 // fusion of triangles tr1 and tr2 having shared link on
820 // theNode1 and theNode2
821 //=======================================================================
823 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
824 const SMDS_MeshNode * theNode1,
825 const SMDS_MeshNode * theNode2,
826 const SMDS_MeshElement * tr1,
827 const SMDS_MeshElement * tr2 )
829 if( tr1->NbNodes() != tr2->NbNodes() )
831 // find the 4-th node to insert into tr1
832 const SMDS_MeshNode* n4 = 0;
833 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
835 while ( !n4 && i<3 ) {
836 const SMDS_MeshNode * n = cast2Node( it->next() );
838 bool isDiag = ( n == theNode1 || n == theNode2 );
842 // Make an array of nodes to be in a quadrangle
843 int iNode = 0, iFirstDiag = -1;
844 it = tr1->nodesIterator();
847 const SMDS_MeshNode * n = cast2Node( it->next() );
849 bool isDiag = ( n == theNode1 || n == theNode2 );
851 if ( iFirstDiag < 0 )
853 else if ( iNode - iFirstDiag == 1 )
854 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
856 else if ( n == n4 ) {
857 return false; // tr1 and tr2 should not have all the same nodes
859 theQuadNodes[ iNode++ ] = n;
861 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
862 theQuadNodes[ iNode ] = n4;
867 //=======================================================================
868 //function : DeleteDiag
869 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
870 // with a quadrangle built on the same 4 nodes.
871 // Return false if proper faces not found
872 //=======================================================================
874 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
875 const SMDS_MeshNode * theNode2)
877 myLastCreatedElems.Clear();
878 myLastCreatedNodes.Clear();
880 MESSAGE( "::DeleteDiag()" );
882 const SMDS_MeshElement *tr1, *tr2;
883 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
886 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
887 if (!F1) return false;
888 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
889 if (!F2) return false;
890 SMESHDS_Mesh * aMesh = GetMeshDS();
892 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
893 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
895 const SMDS_MeshNode* aNodes [ 4 ];
896 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
899 const SMDS_MeshElement* newElem = 0;
900 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
901 myLastCreatedElems.Append(newElem);
902 AddToSameGroups( newElem, tr1, aMesh );
903 int aShapeId = tr1->getshapeId();
906 aMesh->SetMeshElementOnShape( newElem, aShapeId );
908 aMesh->RemoveElement( tr1 );
909 aMesh->RemoveElement( tr2 );
914 // check case of quadratic faces
915 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
917 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
921 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
922 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
930 const SMDS_MeshNode* N1 [6];
931 const SMDS_MeshNode* N2 [6];
932 if(!GetNodesFromTwoTria(tr1,tr2,N1,N2))
934 // now we receive following N1 and N2 (using numeration as above image)
935 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
936 // i.e. first nodes from both arrays determ new diagonal
938 const SMDS_MeshNode* aNodes[8];
948 const SMDS_MeshElement* newElem = 0;
949 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
950 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
951 myLastCreatedElems.Append(newElem);
952 AddToSameGroups( newElem, tr1, aMesh );
953 int aShapeId = tr1->getshapeId();
956 aMesh->SetMeshElementOnShape( newElem, aShapeId );
958 aMesh->RemoveElement( tr1 );
959 aMesh->RemoveElement( tr2 );
961 // remove middle node (9)
962 GetMeshDS()->RemoveNode( N1[4] );
967 //=======================================================================
968 //function : Reorient
969 //purpose : Reverse theElement orientation
970 //=======================================================================
972 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
975 myLastCreatedElems.Clear();
976 myLastCreatedNodes.Clear();
980 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
981 if ( !it || !it->more() )
984 switch ( theElem->GetType() ) {
988 if(!theElem->IsQuadratic()) {
989 int i = theElem->NbNodes();
990 vector<const SMDS_MeshNode*> aNodes( i );
992 aNodes[ --i ]= static_cast<const SMDS_MeshNode*>( it->next() );
993 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], theElem->NbNodes() );
996 // quadratic elements
997 if(theElem->GetType()==SMDSAbs_Edge) {
998 vector<const SMDS_MeshNode*> aNodes(3);
999 aNodes[1]= static_cast<const SMDS_MeshNode*>( it->next() );
1000 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1001 aNodes[2]= static_cast<const SMDS_MeshNode*>( it->next() );
1002 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], 3 );
1005 int nbn = theElem->NbNodes();
1006 vector<const SMDS_MeshNode*> aNodes(nbn);
1007 aNodes[0]= static_cast<const SMDS_MeshNode*>( it->next() );
1009 for(; i<nbn/2; i++) {
1010 aNodes[nbn/2-i]= static_cast<const SMDS_MeshNode*>( it->next() );
1012 for(i=0; i<nbn/2; i++) {
1013 aNodes[nbn-i-1]= static_cast<const SMDS_MeshNode*>( it->next() );
1015 return GetMeshDS()->ChangeElementNodes( theElem, &aNodes[0], nbn );
1019 case SMDSAbs_Volume: {
1020 if (theElem->IsPoly()) {
1021 // TODO reorient vtk polyhedron
1022 MESSAGE("reorient vtk polyhedron ?");
1023 const SMDS_VtkVolume* aPolyedre =
1024 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1026 MESSAGE("Warning: bad volumic element");
1030 int nbFaces = aPolyedre->NbFaces();
1031 vector<const SMDS_MeshNode *> poly_nodes;
1032 vector<int> quantities (nbFaces);
1034 // reverse each face of the polyedre
1035 for (int iface = 1; iface <= nbFaces; iface++) {
1036 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1037 quantities[iface - 1] = nbFaceNodes;
1039 for (inode = nbFaceNodes; inode >= 1; inode--) {
1040 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1041 poly_nodes.push_back(curNode);
1045 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1049 SMDS_VolumeTool vTool;
1050 if ( !vTool.Set( theElem ))
1053 MESSAGE("ChangeElementNodes reorient: check vTool.Inverse");
1054 return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() );
1063 //================================================================================
1065 * \brief Reorient faces.
1066 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1067 * \param theDirection - desired direction of normal of \a theFace
1068 * \param theFace - one of \a theFaces that sould be orientated according to
1069 * \a theDirection and whose orientation defines orientation of other faces
1070 * \return number of reoriented faces.
1072 //================================================================================
1074 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1075 const gp_Dir& theDirection,
1076 const SMDS_MeshElement * theFace)
1079 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1081 if ( theFaces.empty() )
1083 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1084 while ( fIt->more() )
1085 theFaces.insert( theFaces.end(), fIt->next() );
1088 // orient theFace according to theDirection
1090 SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false );
1091 if ( normal * theDirection.XYZ() < 0 )
1092 nbReori += Reorient( theFace );
1094 // Orient other faces
1096 set< const SMDS_MeshElement* > startFaces;
1097 TIDSortedElemSet avoidSet;
1098 set< SMESH_TLink > checkedLinks;
1099 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1101 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1102 theFaces.erase( theFace );
1103 startFaces.insert( theFace );
1105 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1106 while ( startFace != startFaces.end() )
1108 theFace = *startFace;
1109 const int nbNodes = theFace->NbCornerNodes();
1112 avoidSet.insert(theFace);
1114 NLink link( theFace->GetNode( 0 ), 0 );
1115 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1117 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1118 linkIt_isNew = checkedLinks.insert( link );
1119 if ( !linkIt_isNew.second )
1121 // link has already been checked and won't be encountered more
1122 // if the group (theFaces) is manifold
1123 checkedLinks.erase( linkIt_isNew.first );
1127 int nodeInd1, nodeInd2;
1128 const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second,
1130 & nodeInd1, & nodeInd2);
1131 if ( otherFace && otherFace != theFace)
1133 // link must be reversed in otherFace if orientation ot otherFace
1134 // is same as that of theFace
1135 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1137 // cout << "Reorient " << otherFace->GetID() << " near theFace=" <<theFace->GetID()
1138 // << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl;
1139 nbReori += Reorient( otherFace );
1141 startFaces.insert( otherFace );
1142 if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces
1143 theFaces.erase( otherFace );
1146 std::swap( link.first, link.second );
1148 startFaces.erase( startFace );
1149 startFace = startFaces.begin();
1154 //=======================================================================
1155 //function : getBadRate
1157 //=======================================================================
1159 static double getBadRate (const SMDS_MeshElement* theElem,
1160 SMESH::Controls::NumericalFunctorPtr& theCrit)
1162 SMESH::Controls::TSequenceOfXYZ P;
1163 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1165 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1166 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1169 //=======================================================================
1170 //function : QuadToTri
1171 //purpose : Cut quadrangles into triangles.
1172 // theCrit is used to select a diagonal to cut
1173 //=======================================================================
1175 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1176 SMESH::Controls::NumericalFunctorPtr theCrit)
1178 myLastCreatedElems.Clear();
1179 myLastCreatedNodes.Clear();
1181 MESSAGE( "::QuadToTri()" );
1183 if ( !theCrit.get() )
1186 SMESHDS_Mesh * aMesh = GetMeshDS();
1188 Handle(Geom_Surface) surface;
1189 SMESH_MesherHelper helper( *GetMesh() );
1191 TIDSortedElemSet::iterator itElem;
1192 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
1193 const SMDS_MeshElement* elem = *itElem;
1194 if ( !elem || elem->GetType() != SMDSAbs_Face )
1196 if ( elem->NbCornerNodes() != 4 )
1199 // retrieve element nodes
1200 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1202 // compare two sets of possible triangles
1203 double aBadRate1, aBadRate2; // to what extent a set is bad
1204 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1205 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1206 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1208 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1209 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1210 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1212 int aShapeId = FindShape( elem );
1213 const SMDS_MeshElement* newElem1 = 0;
1214 const SMDS_MeshElement* newElem2 = 0;
1216 if( !elem->IsQuadratic() ) {
1218 // split liner quadrangle
1220 if ( aBadRate1 <= aBadRate2 ) {
1221 // tr1 + tr2 is better
1222 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1223 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1226 // tr3 + tr4 is better
1227 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1228 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1233 // split quadratic quadrangle
1235 // get surface elem is on
1236 if ( aShapeId != helper.GetSubShapeID() ) {
1240 shape = aMesh->IndexToShape( aShapeId );
1241 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
1242 TopoDS_Face face = TopoDS::Face( shape );
1243 surface = BRep_Tool::Surface( face );
1244 if ( !surface.IsNull() )
1245 helper.SetSubShape( shape );
1248 // find middle point for (0,1,2,3)
1249 // and create a node in this point;
1250 const SMDS_MeshNode* newN = 0;
1251 if ( aNodes.size() == 9 )
1253 // SMDSEntity_BiQuad_Quadrangle
1254 newN = aNodes.back();
1259 if ( surface.IsNull() )
1261 for ( int i = 0; i < 4; i++ )
1262 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
1267 const SMDS_MeshNode* inFaceNode = 0;
1268 if ( helper.GetNodeUVneedInFaceNode() )
1269 for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i )
1270 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
1271 inFaceNode = aNodes[ i ];
1273 TopoDS_Face face = TopoDS::Face( helper.GetSubShape() );
1275 for ( int i = 0; i < 4; i++ )
1276 uv += helper.GetNodeUV( face, aNodes[i], inFaceNode );
1278 p = surface->Value( uv.X(), uv.Y() ).XYZ();
1280 newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
1281 myLastCreatedNodes.Append(newN);
1283 // create a new element
1284 if ( aBadRate1 <= aBadRate2 ) {
1285 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
1286 aNodes[6], aNodes[7], newN );
1287 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
1288 newN, aNodes[4], aNodes[5] );
1291 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
1292 aNodes[7], aNodes[4], newN );
1293 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
1294 newN, aNodes[5], aNodes[6] );
1298 // care of a new element
1300 myLastCreatedElems.Append(newElem1);
1301 myLastCreatedElems.Append(newElem2);
1302 AddToSameGroups( newElem1, elem, aMesh );
1303 AddToSameGroups( newElem2, elem, aMesh );
1305 // put a new triangle on the same shape
1308 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1309 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1311 aMesh->RemoveElement( elem );
1316 //=======================================================================
1317 //function : BestSplit
1318 //purpose : Find better diagonal for cutting.
1319 //=======================================================================
1321 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1322 SMESH::Controls::NumericalFunctorPtr theCrit)
1324 myLastCreatedElems.Clear();
1325 myLastCreatedNodes.Clear();
1330 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1333 if( theQuad->NbNodes()==4 ||
1334 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1336 // retrieve element nodes
1337 const SMDS_MeshNode* aNodes [4];
1338 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1340 //while (itN->more())
1342 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1344 // compare two sets of possible triangles
1345 double aBadRate1, aBadRate2; // to what extent a set is bad
1346 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1347 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1348 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1350 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1351 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1352 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1354 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1355 return 1; // diagonal 1-3
1357 return 2; // diagonal 2-4
1364 // Methods of splitting volumes into tetra
1366 const int theHexTo5_1[5*4+1] =
1368 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1370 const int theHexTo5_2[5*4+1] =
1372 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1374 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1376 const int theHexTo6_1[6*4+1] =
1378 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
1380 const int theHexTo6_2[6*4+1] =
1382 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
1384 const int theHexTo6_3[6*4+1] =
1386 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
1388 const int theHexTo6_4[6*4+1] =
1390 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
1392 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1394 const int thePyraTo2_1[2*4+1] =
1396 0, 1, 2, 4, 0, 2, 3, 4, -1
1398 const int thePyraTo2_2[2*4+1] =
1400 1, 2, 3, 4, 1, 3, 0, 4, -1
1402 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1404 const int thePentaTo3_1[3*4+1] =
1406 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1408 const int thePentaTo3_2[3*4+1] =
1410 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1412 const int thePentaTo3_3[3*4+1] =
1414 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1416 const int thePentaTo3_4[3*4+1] =
1418 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1420 const int thePentaTo3_5[3*4+1] =
1422 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1424 const int thePentaTo3_6[3*4+1] =
1426 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1428 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1429 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1431 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1434 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1435 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1436 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1441 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1442 bool _baryNode; //!< additional node is to be created at cell barycenter
1443 bool _ownConn; //!< to delete _connectivity in destructor
1444 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1446 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1447 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1448 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1449 bool hasFacet( const TTriangleFacet& facet ) const
1451 const int* tetConn = _connectivity;
1452 for ( ; tetConn[0] >= 0; tetConn += 4 )
1453 if (( facet.contains( tetConn[0] ) +
1454 facet.contains( tetConn[1] ) +
1455 facet.contains( tetConn[2] ) +
1456 facet.contains( tetConn[3] )) == 3 )
1462 //=======================================================================
1464 * \brief return TSplitMethod for the given element
1466 //=======================================================================
1468 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1470 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1472 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1473 // an edge and a face barycenter; tertaherdons are based on triangles and
1474 // a volume barycenter
1475 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1477 // Find out how adjacent volumes are split
1479 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1480 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1481 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1483 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1484 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1485 if ( nbNodes < 4 ) continue;
1487 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1488 const int* nInd = vol.GetFaceNodesIndices( iF );
1491 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1492 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1493 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1494 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1498 int iCom = 0; // common node of triangle faces to split into
1499 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1501 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1502 nInd[ iQ * ( (iCom+1)%nbNodes )],
1503 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1504 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1505 nInd[ iQ * ( (iCom+2)%nbNodes )],
1506 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1507 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1509 triaSplits.push_back( t012 );
1510 triaSplits.push_back( t023 );
1515 if ( !triaSplits.empty() )
1516 hasAdjacentSplits = true;
1519 // Among variants of split method select one compliant with adjacent volumes
1521 TSplitMethod method;
1522 if ( !vol.Element()->IsPoly() && !is24TetMode )
1524 int nbVariants = 2, nbTet = 0;
1525 const int** connVariants = 0;
1526 switch ( vol.Element()->GetEntityType() )
1528 case SMDSEntity_Hexa:
1529 case SMDSEntity_Quad_Hexa:
1530 case SMDSEntity_TriQuad_Hexa:
1531 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1532 connVariants = theHexTo5, nbTet = 5;
1534 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1536 case SMDSEntity_Pyramid:
1537 case SMDSEntity_Quad_Pyramid:
1538 connVariants = thePyraTo2; nbTet = 2;
1540 case SMDSEntity_Penta:
1541 case SMDSEntity_Quad_Penta:
1542 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1547 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1549 // check method compliancy with adjacent tetras,
1550 // all found splits must be among facets of tetras described by this method
1551 method = TSplitMethod( nbTet, connVariants[variant] );
1552 if ( hasAdjacentSplits && method._nbTetra > 0 )
1554 bool facetCreated = true;
1555 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1557 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1558 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1559 facetCreated = method.hasFacet( *facet );
1561 if ( !facetCreated )
1562 method = TSplitMethod(0); // incompatible method
1566 if ( method._nbTetra < 1 )
1568 // No standard method is applicable, use a generic solution:
1569 // each facet of a volume is split into triangles and
1570 // each of triangles and a volume barycenter form a tetrahedron.
1572 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1574 int* connectivity = new int[ maxTetConnSize + 1 ];
1575 method._connectivity = connectivity;
1576 method._ownConn = true;
1577 method._baryNode = !isHex27; // to create central node or not
1580 int baryCenInd = vol.NbNodes() - int( isHex27 );
1581 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1583 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1584 const int* nInd = vol.GetFaceNodesIndices( iF );
1585 // find common node of triangle facets of tetra to create
1586 int iCommon = 0; // index in linear numeration
1587 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1588 if ( !triaSplits.empty() )
1591 const TTriangleFacet* facet = &triaSplits.front();
1592 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1593 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1594 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1597 else if ( nbNodes > 3 && !is24TetMode )
1599 // find the best method of splitting into triangles by aspect ratio
1600 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1601 map< double, int > badness2iCommon;
1602 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1603 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1604 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1607 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1609 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1610 nodes[ iQ*((iLast-1)%nbNodes)],
1611 nodes[ iQ*((iLast )%nbNodes)]);
1612 badness += getBadRate( &tria, aspectRatio );
1614 badness2iCommon.insert( make_pair( badness, iCommon ));
1616 // use iCommon with lowest badness
1617 iCommon = badness2iCommon.begin()->second;
1619 if ( iCommon >= nbNodes )
1620 iCommon = 0; // something wrong
1622 // fill connectivity of tetrahedra based on a current face
1623 int nbTet = nbNodes - 2;
1624 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1629 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1630 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1634 method._faceBaryNode[ iF ] = 0;
1635 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1638 for ( int i = 0; i < nbTet; ++i )
1640 int i1 = i, i2 = (i+1) % nbNodes;
1641 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1642 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1643 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1644 connectivity[ connSize++ ] = faceBaryCenInd;
1645 connectivity[ connSize++ ] = baryCenInd;
1650 for ( int i = 0; i < nbTet; ++i )
1652 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1653 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1654 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1655 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1656 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1657 connectivity[ connSize++ ] = baryCenInd;
1660 method._nbTetra += nbTet;
1662 } // loop on volume faces
1664 connectivity[ connSize++ ] = -1;
1666 } // end of generic solution
1670 //================================================================================
1672 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1674 //================================================================================
1676 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1678 // find the tetrahedron including the three nodes of facet
1679 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1680 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1681 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1682 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1683 while ( volIt1->more() )
1685 const SMDS_MeshElement* v = volIt1->next();
1686 SMDSAbs_EntityType type = v->GetEntityType();
1687 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1689 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1690 continue; // medium node not allowed
1691 const int ind2 = v->GetNodeIndex( n2 );
1692 if ( ind2 < 0 || 3 < ind2 )
1694 const int ind3 = v->GetNodeIndex( n3 );
1695 if ( ind3 < 0 || 3 < ind3 )
1702 //=======================================================================
1704 * \brief A key of a face of volume
1706 //=======================================================================
1708 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1710 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1712 TIDSortedNodeSet sortedNodes;
1713 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1714 int nbNodes = vol.NbFaceNodes( iF );
1715 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1716 for ( int i = 0; i < nbNodes; i += iQ )
1717 sortedNodes.insert( fNodes[i] );
1718 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1719 first.first = (*(n++))->GetID();
1720 first.second = (*(n++))->GetID();
1721 second.first = (*(n++))->GetID();
1722 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1727 //=======================================================================
1728 //function : SplitVolumesIntoTetra
1729 //purpose : Split volume elements into tetrahedra.
1730 //=======================================================================
1732 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1733 const int theMethodFlags)
1735 // std-like iterator on coordinates of nodes of mesh element
1736 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1737 NXyzIterator xyzEnd;
1739 SMDS_VolumeTool volTool;
1740 SMESH_MesherHelper helper( *GetMesh());
1742 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1743 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1745 SMESH_SequenceOfElemPtr newNodes, newElems;
1747 // map face of volume to it's baricenrtic node
1748 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1751 TIDSortedElemSet::const_iterator elem = theElems.begin();
1752 for ( ; elem != theElems.end(); ++elem )
1754 if ( (*elem)->GetType() != SMDSAbs_Volume )
1756 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1757 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1760 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1762 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1763 if ( splitMethod._nbTetra < 1 ) continue;
1765 // find submesh to add new tetras to
1766 if ( !subMesh || !subMesh->Contains( *elem ))
1768 int shapeID = FindShape( *elem );
1769 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1770 subMesh = GetMeshDS()->MeshElements( shapeID );
1773 if ( (*elem)->IsQuadratic() )
1776 // add quadratic links to the helper
1777 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1779 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1780 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1781 for ( int iN = 0; iN < nbN; iN += iQ )
1782 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1784 helper.SetIsQuadratic( true );
1789 helper.SetIsQuadratic( false );
1791 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1792 helper.SetElementsOnShape( true );
1793 if ( splitMethod._baryNode )
1795 // make a node at barycenter
1796 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1797 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1798 nodes.push_back( gcNode );
1799 newNodes.Append( gcNode );
1801 if ( !splitMethod._faceBaryNode.empty() )
1803 // make or find baricentric nodes of faces
1804 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1805 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1807 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1808 volFace2BaryNode.insert
1809 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1812 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1813 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1815 nodes.push_back( iF_n->second = f_n->second );
1820 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1821 const int* tetConn = splitMethod._connectivity;
1822 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1823 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1824 nodes[ tetConn[1] ],
1825 nodes[ tetConn[2] ],
1826 nodes[ tetConn[3] ]));
1828 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1830 // Split faces on sides of the split volume
1832 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1833 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1835 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1836 if ( nbNodes < 4 ) continue;
1838 // find an existing face
1839 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
1840 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
1841 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
1842 /*noMedium=*/false))
1845 helper.SetElementsOnShape( false );
1846 vector< const SMDS_MeshElement* > triangles;
1848 // find submesh to add new triangles in
1849 if ( !fSubMesh || !fSubMesh->Contains( face ))
1851 int shapeID = FindShape( face );
1852 fSubMesh = GetMeshDS()->MeshElements( shapeID );
1854 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
1855 if ( iF_n != splitMethod._faceBaryNode.end() )
1857 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
1859 const SMDS_MeshNode* n1 = fNodes[iN];
1860 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
1861 const SMDS_MeshNode *n3 = iF_n->second;
1862 if ( !volTool.IsFaceExternal( iF ))
1864 triangles.push_back( helper.AddFace( n1,n2,n3 ));
1866 if ( fSubMesh && n3->getshapeId() < 1 )
1867 fSubMesh->AddNode( n3 );
1872 // among possible triangles create ones discribed by split method
1873 const int* nInd = volTool.GetFaceNodesIndices( iF );
1874 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1875 int iCom = 0; // common node of triangle faces to split into
1876 list< TTriangleFacet > facets;
1877 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
1879 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1880 nInd[ iQ * ( (iCom+1)%nbNodes )],
1881 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1882 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1883 nInd[ iQ * ( (iCom+2)%nbNodes )],
1884 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1885 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
1887 facets.push_back( t012 );
1888 facets.push_back( t023 );
1889 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
1890 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
1891 nInd[ iQ * ((iLast-1)%nbNodes )],
1892 nInd[ iQ * ((iLast )%nbNodes )]));
1896 list< TTriangleFacet >::iterator facet = facets.begin();
1897 for ( ; facet != facets.end(); ++facet )
1899 if ( !volTool.IsFaceExternal( iF ))
1900 swap( facet->_n2, facet->_n3 );
1901 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
1902 volNodes[ facet->_n2 ],
1903 volNodes[ facet->_n3 ]));
1906 for ( int i = 0; i < triangles.size(); ++i )
1908 if ( !triangles[i] ) continue;
1910 fSubMesh->AddElement( triangles[i]);
1911 newElems.Append( triangles[i] );
1913 ReplaceElemInGroups( face, triangles, GetMeshDS() );
1914 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
1917 } // loop on volume faces to split them into triangles
1919 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
1921 if ( geomType == SMDSEntity_TriQuad_Hexa )
1923 // remove medium nodes that could become free
1924 for ( int i = 20; i < volTool.NbNodes(); ++i )
1925 if ( volNodes[i]->NbInverseElements() == 0 )
1926 GetMeshDS()->RemoveNode( volNodes[i] );
1928 } // loop on volumes to split
1930 myLastCreatedNodes = newNodes;
1931 myLastCreatedElems = newElems;
1934 //=======================================================================
1935 //function : AddToSameGroups
1936 //purpose : add elemToAdd to the groups the elemInGroups belongs to
1937 //=======================================================================
1939 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
1940 const SMDS_MeshElement* elemInGroups,
1941 SMESHDS_Mesh * aMesh)
1943 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1944 if (!groups.empty()) {
1945 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1946 for ( ; grIt != groups.end(); grIt++ ) {
1947 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1948 if ( group && group->Contains( elemInGroups ))
1949 group->SMDSGroup().Add( elemToAdd );
1955 //=======================================================================
1956 //function : RemoveElemFromGroups
1957 //purpose : Remove removeelem to the groups the elemInGroups belongs to
1958 //=======================================================================
1959 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
1960 SMESHDS_Mesh * aMesh)
1962 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1963 if (!groups.empty())
1965 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
1966 for (; GrIt != groups.end(); GrIt++)
1968 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
1969 if (!grp || grp->IsEmpty()) continue;
1970 grp->SMDSGroup().Remove(removeelem);
1975 //================================================================================
1977 * \brief Replace elemToRm by elemToAdd in the all groups
1979 //================================================================================
1981 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
1982 const SMDS_MeshElement* elemToAdd,
1983 SMESHDS_Mesh * aMesh)
1985 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
1986 if (!groups.empty()) {
1987 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
1988 for ( ; grIt != groups.end(); grIt++ ) {
1989 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
1990 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
1991 group->SMDSGroup().Add( elemToAdd );
1996 //================================================================================
1998 * \brief Replace elemToRm by elemToAdd in the all groups
2000 //================================================================================
2002 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2003 const vector<const SMDS_MeshElement*>& elemToAdd,
2004 SMESHDS_Mesh * aMesh)
2006 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2007 if (!groups.empty())
2009 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2010 for ( ; grIt != groups.end(); grIt++ ) {
2011 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2012 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2013 for ( int i = 0; i < elemToAdd.size(); ++i )
2014 group->SMDSGroup().Add( elemToAdd[ i ] );
2019 //=======================================================================
2020 //function : QuadToTri
2021 //purpose : Cut quadrangles into triangles.
2022 // theCrit is used to select a diagonal to cut
2023 //=======================================================================
2025 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2026 const bool the13Diag)
2028 myLastCreatedElems.Clear();
2029 myLastCreatedNodes.Clear();
2031 MESSAGE( "::QuadToTri()" );
2033 SMESHDS_Mesh * aMesh = GetMeshDS();
2035 Handle(Geom_Surface) surface;
2036 SMESH_MesherHelper helper( *GetMesh() );
2038 TIDSortedElemSet::iterator itElem;
2039 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2040 const SMDS_MeshElement* elem = *itElem;
2041 if ( !elem || elem->GetType() != SMDSAbs_Face )
2043 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2044 if(!isquad) continue;
2046 if(elem->NbNodes()==4) {
2047 // retrieve element nodes
2048 const SMDS_MeshNode* aNodes [4];
2049 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2051 while ( itN->more() )
2052 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2054 int aShapeId = FindShape( elem );
2055 const SMDS_MeshElement* newElem1 = 0;
2056 const SMDS_MeshElement* newElem2 = 0;
2058 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2059 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2062 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2063 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2065 myLastCreatedElems.Append(newElem1);
2066 myLastCreatedElems.Append(newElem2);
2067 // put a new triangle on the same shape and add to the same groups
2070 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2071 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2073 AddToSameGroups( newElem1, elem, aMesh );
2074 AddToSameGroups( newElem2, elem, aMesh );
2075 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2076 aMesh->RemoveElement( elem );
2079 // Quadratic quadrangle
2081 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2083 // get surface elem is on
2084 int aShapeId = FindShape( elem );
2085 if ( aShapeId != helper.GetSubShapeID() ) {
2089 shape = aMesh->IndexToShape( aShapeId );
2090 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2091 TopoDS_Face face = TopoDS::Face( shape );
2092 surface = BRep_Tool::Surface( face );
2093 if ( !surface.IsNull() )
2094 helper.SetSubShape( shape );
2098 const SMDS_MeshNode* aNodes [8];
2099 const SMDS_MeshNode* inFaceNode = 0;
2100 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2102 while ( itN->more() ) {
2103 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2104 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2105 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2107 inFaceNode = aNodes[ i-1 ];
2111 // find middle point for (0,1,2,3)
2112 // and create a node in this point;
2114 if ( surface.IsNull() ) {
2116 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2120 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2123 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2125 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2127 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2128 myLastCreatedNodes.Append(newN);
2130 // create a new element
2131 const SMDS_MeshElement* newElem1 = 0;
2132 const SMDS_MeshElement* newElem2 = 0;
2134 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2135 aNodes[6], aNodes[7], newN );
2136 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2137 newN, aNodes[4], aNodes[5] );
2140 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2141 aNodes[7], aNodes[4], newN );
2142 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2143 newN, aNodes[5], aNodes[6] );
2145 myLastCreatedElems.Append(newElem1);
2146 myLastCreatedElems.Append(newElem2);
2147 // put a new triangle on the same shape and add to the same groups
2150 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2151 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2153 AddToSameGroups( newElem1, elem, aMesh );
2154 AddToSameGroups( newElem2, elem, aMesh );
2155 aMesh->RemoveElement( elem );
2162 //=======================================================================
2163 //function : getAngle
2165 //=======================================================================
2167 double getAngle(const SMDS_MeshElement * tr1,
2168 const SMDS_MeshElement * tr2,
2169 const SMDS_MeshNode * n1,
2170 const SMDS_MeshNode * n2)
2172 double angle = 2. * M_PI; // bad angle
2175 SMESH::Controls::TSequenceOfXYZ P1, P2;
2176 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2177 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2180 if(!tr1->IsQuadratic())
2181 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2183 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2184 if ( N1.SquareMagnitude() <= gp::Resolution() )
2186 if(!tr2->IsQuadratic())
2187 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2189 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2190 if ( N2.SquareMagnitude() <= gp::Resolution() )
2193 // find the first diagonal node n1 in the triangles:
2194 // take in account a diagonal link orientation
2195 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2196 for ( int t = 0; t < 2; t++ ) {
2197 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2198 int i = 0, iDiag = -1;
2199 while ( it->more()) {
2200 const SMDS_MeshElement *n = it->next();
2201 if ( n == n1 || n == n2 ) {
2205 if ( i - iDiag == 1 )
2206 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2215 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2218 angle = N1.Angle( N2 );
2223 // =================================================
2224 // class generating a unique ID for a pair of nodes
2225 // and able to return nodes by that ID
2226 // =================================================
2230 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2231 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2234 long GetLinkID (const SMDS_MeshNode * n1,
2235 const SMDS_MeshNode * n2) const
2237 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2240 bool GetNodes (const long theLinkID,
2241 const SMDS_MeshNode* & theNode1,
2242 const SMDS_MeshNode* & theNode2) const
2244 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2245 if ( !theNode1 ) return false;
2246 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2247 if ( !theNode2 ) return false;
2253 const SMESHDS_Mesh* myMesh;
2258 //=======================================================================
2259 //function : TriToQuad
2260 //purpose : Fuse neighbour triangles into quadrangles.
2261 // theCrit is used to select a neighbour to fuse with.
2262 // theMaxAngle is a max angle between element normals at which
2263 // fusion is still performed.
2264 //=======================================================================
2266 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2267 SMESH::Controls::NumericalFunctorPtr theCrit,
2268 const double theMaxAngle)
2270 myLastCreatedElems.Clear();
2271 myLastCreatedNodes.Clear();
2273 MESSAGE( "::TriToQuad()" );
2275 if ( !theCrit.get() )
2278 SMESHDS_Mesh * aMesh = GetMeshDS();
2280 // Prepare data for algo: build
2281 // 1. map of elements with their linkIDs
2282 // 2. map of linkIDs with their elements
2284 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2285 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2286 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2287 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2289 TIDSortedElemSet::iterator itElem;
2290 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2291 const SMDS_MeshElement* elem = *itElem;
2292 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2293 bool IsTria = elem->NbNodes()==3 || (elem->NbNodes()==6 && elem->IsQuadratic());
2294 if(!IsTria) continue;
2296 // retrieve element nodes
2297 const SMDS_MeshNode* aNodes [4];
2298 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2301 aNodes[ i++ ] = cast2Node( itN->next() );
2302 aNodes[ 3 ] = aNodes[ 0 ];
2305 for ( i = 0; i < 3; i++ ) {
2306 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2307 // check if elements sharing a link can be fused
2308 itLE = mapLi_listEl.find( link );
2309 if ( itLE != mapLi_listEl.end() ) {
2310 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2312 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2313 //if ( FindShape( elem ) != FindShape( elem2 ))
2314 // continue; // do not fuse triangles laying on different shapes
2315 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2316 continue; // avoid making badly shaped quads
2317 (*itLE).second.push_back( elem );
2320 mapLi_listEl[ link ].push_back( elem );
2322 mapEl_setLi [ elem ].insert( link );
2325 // Clean the maps from the links shared by a sole element, ie
2326 // links to which only one element is bound in mapLi_listEl
2328 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2329 int nbElems = (*itLE).second.size();
2330 if ( nbElems < 2 ) {
2331 const SMDS_MeshElement* elem = (*itLE).second.front();
2332 SMESH_TLink link = (*itLE).first;
2333 mapEl_setLi[ elem ].erase( link );
2334 if ( mapEl_setLi[ elem ].empty() )
2335 mapEl_setLi.erase( elem );
2339 // Algo: fuse triangles into quadrangles
2341 while ( ! mapEl_setLi.empty() ) {
2342 // Look for the start element:
2343 // the element having the least nb of shared links
2344 const SMDS_MeshElement* startElem = 0;
2346 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2347 int nbLinks = (*itEL).second.size();
2348 if ( nbLinks < minNbLinks ) {
2349 startElem = (*itEL).first;
2350 minNbLinks = nbLinks;
2351 if ( minNbLinks == 1 )
2356 // search elements to fuse starting from startElem or links of elements
2357 // fused earlyer - startLinks
2358 list< SMESH_TLink > startLinks;
2359 while ( startElem || !startLinks.empty() ) {
2360 while ( !startElem && !startLinks.empty() ) {
2361 // Get an element to start, by a link
2362 SMESH_TLink linkId = startLinks.front();
2363 startLinks.pop_front();
2364 itLE = mapLi_listEl.find( linkId );
2365 if ( itLE != mapLi_listEl.end() ) {
2366 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2367 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2368 for ( ; itE != listElem.end() ; itE++ )
2369 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2371 mapLi_listEl.erase( itLE );
2376 // Get candidates to be fused
2377 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2378 const SMESH_TLink *link12, *link13;
2380 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2381 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2382 ASSERT( !setLi.empty() );
2383 set< SMESH_TLink >::iterator itLi;
2384 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2386 const SMESH_TLink & link = (*itLi);
2387 itLE = mapLi_listEl.find( link );
2388 if ( itLE == mapLi_listEl.end() )
2391 const SMDS_MeshElement* elem = (*itLE).second.front();
2393 elem = (*itLE).second.back();
2394 mapLi_listEl.erase( itLE );
2395 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2406 // add other links of elem to list of links to re-start from
2407 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2408 set< SMESH_TLink >::iterator it;
2409 for ( it = links.begin(); it != links.end(); it++ ) {
2410 const SMESH_TLink& link2 = (*it);
2411 if ( link2 != link )
2412 startLinks.push_back( link2 );
2416 // Get nodes of possible quadrangles
2417 const SMDS_MeshNode *n12 [4], *n13 [4];
2418 bool Ok12 = false, Ok13 = false;
2419 const SMDS_MeshNode *linkNode1, *linkNode2;
2421 linkNode1 = link12->first;
2422 linkNode2 = link12->second;
2423 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2427 linkNode1 = link13->first;
2428 linkNode2 = link13->second;
2429 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2433 // Choose a pair to fuse
2434 if ( Ok12 && Ok13 ) {
2435 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2436 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2437 double aBadRate12 = getBadRate( &quad12, theCrit );
2438 double aBadRate13 = getBadRate( &quad13, theCrit );
2439 if ( aBadRate13 < aBadRate12 )
2446 // and remove fused elems and removed links from the maps
2447 mapEl_setLi.erase( tr1 );
2449 mapEl_setLi.erase( tr2 );
2450 mapLi_listEl.erase( *link12 );
2451 if(tr1->NbNodes()==3) {
2452 const SMDS_MeshElement* newElem = 0;
2453 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2454 myLastCreatedElems.Append(newElem);
2455 AddToSameGroups( newElem, tr1, aMesh );
2456 int aShapeId = tr1->getshapeId();
2459 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2461 aMesh->RemoveElement( tr1 );
2462 aMesh->RemoveElement( tr2 );
2465 const SMDS_MeshNode* N1 [6];
2466 const SMDS_MeshNode* N2 [6];
2467 GetNodesFromTwoTria(tr1,tr2,N1,N2);
2468 // now we receive following N1 and N2 (using numeration as above image)
2469 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2470 // i.e. first nodes from both arrays determ new diagonal
2471 const SMDS_MeshNode* aNodes[8];
2480 const SMDS_MeshElement* newElem = 0;
2481 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2482 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2483 myLastCreatedElems.Append(newElem);
2484 AddToSameGroups( newElem, tr1, aMesh );
2485 int aShapeId = tr1->getshapeId();
2488 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2490 aMesh->RemoveElement( tr1 );
2491 aMesh->RemoveElement( tr2 );
2492 // remove middle node (9)
2493 GetMeshDS()->RemoveNode( N1[4] );
2497 mapEl_setLi.erase( tr3 );
2498 mapLi_listEl.erase( *link13 );
2499 if(tr1->NbNodes()==3) {
2500 const SMDS_MeshElement* newElem = 0;
2501 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2502 myLastCreatedElems.Append(newElem);
2503 AddToSameGroups( newElem, tr1, aMesh );
2504 int aShapeId = tr1->getshapeId();
2507 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2509 aMesh->RemoveElement( tr1 );
2510 aMesh->RemoveElement( tr3 );
2513 const SMDS_MeshNode* N1 [6];
2514 const SMDS_MeshNode* N2 [6];
2515 GetNodesFromTwoTria(tr1,tr3,N1,N2);
2516 // now we receive following N1 and N2 (using numeration as above image)
2517 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2518 // i.e. first nodes from both arrays determ new diagonal
2519 const SMDS_MeshNode* aNodes[8];
2528 const SMDS_MeshElement* newElem = 0;
2529 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2530 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2531 myLastCreatedElems.Append(newElem);
2532 AddToSameGroups( newElem, tr1, aMesh );
2533 int aShapeId = tr1->getshapeId();
2536 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2538 aMesh->RemoveElement( tr1 );
2539 aMesh->RemoveElement( tr3 );
2540 // remove middle node (9)
2541 GetMeshDS()->RemoveNode( N1[4] );
2545 // Next element to fuse: the rejected one
2547 startElem = Ok12 ? tr3 : tr2;
2549 } // if ( startElem )
2550 } // while ( startElem || !startLinks.empty() )
2551 } // while ( ! mapEl_setLi.empty() )
2557 /*#define DUMPSO(txt) \
2558 // cout << txt << endl;
2559 //=============================================================================
2563 //=============================================================================
2564 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2568 int tmp = idNodes[ i1 ];
2569 idNodes[ i1 ] = idNodes[ i2 ];
2570 idNodes[ i2 ] = tmp;
2571 gp_Pnt Ptmp = P[ i1 ];
2574 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2577 //=======================================================================
2578 //function : SortQuadNodes
2579 //purpose : Set 4 nodes of a quadrangle face in a good order.
2580 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2582 //=======================================================================
2584 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2589 for ( i = 0; i < 4; i++ ) {
2590 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2592 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2595 gp_Vec V1(P[0], P[1]);
2596 gp_Vec V2(P[0], P[2]);
2597 gp_Vec V3(P[0], P[3]);
2599 gp_Vec Cross1 = V1 ^ V2;
2600 gp_Vec Cross2 = V2 ^ V3;
2603 if (Cross1.Dot(Cross2) < 0)
2608 if (Cross1.Dot(Cross2) < 0)
2612 swap ( i, i + 1, idNodes, P );
2614 // for ( int ii = 0; ii < 4; ii++ ) {
2615 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2616 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2622 //=======================================================================
2623 //function : SortHexaNodes
2624 //purpose : Set 8 nodes of a hexahedron in a good order.
2625 // Return success status
2626 //=======================================================================
2628 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2633 DUMPSO( "INPUT: ========================================");
2634 for ( i = 0; i < 8; i++ ) {
2635 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2636 if ( !n ) return false;
2637 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2638 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2640 DUMPSO( "========================================");
2643 set<int> faceNodes; // ids of bottom face nodes, to be found
2644 set<int> checkedId1; // ids of tried 2-nd nodes
2645 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2646 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2647 int iMin, iLoop1 = 0;
2649 // Loop to try the 2-nd nodes
2651 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2653 // Find not checked 2-nd node
2654 for ( i = 1; i < 8; i++ )
2655 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2656 int id1 = idNodes[i];
2657 swap ( 1, i, idNodes, P );
2658 checkedId1.insert ( id1 );
2662 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2663 // ie that all but meybe one (id3 which is on the same face) nodes
2664 // lay on the same side from the triangle plane.
2666 bool manyInPlane = false; // more than 4 nodes lay in plane
2668 while ( ++iLoop2 < 6 ) {
2670 // get 1-2-3 plane coeffs
2671 Standard_Real A, B, C, D;
2672 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2673 if ( N.SquareMagnitude() > gp::Resolution() )
2675 gp_Pln pln ( P[0], N );
2676 pln.Coefficients( A, B, C, D );
2678 // find the node (iMin) closest to pln
2679 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2681 for ( i = 3; i < 8; i++ ) {
2682 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2683 if ( fabs( dist[i] ) < minDist ) {
2684 minDist = fabs( dist[i] );
2687 if ( fabs( dist[i] ) <= tol )
2688 idInPln.insert( idNodes[i] );
2691 // there should not be more than 4 nodes in bottom plane
2692 if ( idInPln.size() > 1 )
2694 DUMPSO( "### idInPln.size() = " << idInPln.size());
2695 // idInPlane does not contain the first 3 nodes
2696 if ( manyInPlane || idInPln.size() == 5)
2697 return false; // all nodes in one plane
2700 // set the 1-st node to be not in plane
2701 for ( i = 3; i < 8; i++ ) {
2702 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2703 DUMPSO( "### Reset 0-th node");
2704 swap( 0, i, idNodes, P );
2709 // reset to re-check second nodes
2710 leastDist = DBL_MAX;
2714 break; // from iLoop2;
2717 // check that the other 4 nodes are on the same side
2718 bool sameSide = true;
2719 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2720 for ( i = 3; sameSide && i < 8; i++ ) {
2722 sameSide = ( isNeg == dist[i] <= 0.);
2725 // keep best solution
2726 if ( sameSide && minDist < leastDist ) {
2727 leastDist = minDist;
2729 faceNodes.insert( idNodes[ 1 ] );
2730 faceNodes.insert( idNodes[ 2 ] );
2731 faceNodes.insert( idNodes[ iMin ] );
2732 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2733 << " leastDist = " << leastDist);
2734 if ( leastDist <= DBL_MIN )
2739 // set next 3-d node to check
2740 int iNext = 2 + iLoop2;
2742 DUMPSO( "Try 2-nd");
2743 swap ( 2, iNext, idNodes, P );
2745 } // while ( iLoop2 < 6 )
2748 if ( faceNodes.empty() ) return false;
2750 // Put the faceNodes in proper places
2751 for ( i = 4; i < 8; i++ ) {
2752 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2753 // find a place to put
2755 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2757 DUMPSO( "Set faceNodes");
2758 swap ( iTo, i, idNodes, P );
2763 // Set nodes of the found bottom face in good order
2764 DUMPSO( " Found bottom face: ");
2765 i = SortQuadNodes( theMesh, idNodes );
2767 gp_Pnt Ptmp = P[ i ];
2772 // for ( int ii = 0; ii < 4; ii++ ) {
2773 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2774 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2777 // Gravity center of the top and bottom faces
2778 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2779 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2781 // Get direction from the bottom to the top face
2782 gp_Vec upDir ( aGCb, aGCt );
2783 Standard_Real upDirSize = upDir.Magnitude();
2784 if ( upDirSize <= gp::Resolution() ) return false;
2787 // Assure that the bottom face normal points up
2788 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2789 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2790 if ( Nb.Dot( upDir ) < 0 ) {
2791 DUMPSO( "Reverse bottom face");
2792 swap( 1, 3, idNodes, P );
2795 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2796 Standard_Real minDist = DBL_MAX;
2797 for ( i = 4; i < 8; i++ ) {
2798 // projection of P[i] to the plane defined by P[0] and upDir
2799 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2800 Standard_Real sqDist = P[0].SquareDistance( Pp );
2801 if ( sqDist < minDist ) {
2806 DUMPSO( "Set 4-th");
2807 swap ( 4, iMin, idNodes, P );
2809 // Set nodes of the top face in good order
2810 DUMPSO( "Sort top face");
2811 i = SortQuadNodes( theMesh, &idNodes[4] );
2814 gp_Pnt Ptmp = P[ i ];
2819 // Assure that direction of the top face normal is from the bottom face
2820 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2821 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2822 if ( Nt.Dot( upDir ) < 0 ) {
2823 DUMPSO( "Reverse top face");
2824 swap( 5, 7, idNodes, P );
2827 // DUMPSO( "OUTPUT: ========================================");
2828 // for ( i = 0; i < 8; i++ ) {
2829 // float *p = ugrid->GetPoint(idNodes[i]);
2830 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
2836 //================================================================================
2838 * \brief Return nodes linked to the given one
2839 * \param theNode - the node
2840 * \param linkedNodes - the found nodes
2841 * \param type - the type of elements to check
2843 * Medium nodes are ignored
2845 //================================================================================
2847 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
2848 TIDSortedElemSet & linkedNodes,
2849 SMDSAbs_ElementType type )
2851 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
2852 while ( elemIt->more() )
2854 const SMDS_MeshElement* elem = elemIt->next();
2855 if(elem->GetType() == SMDSAbs_0DElement)
2858 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
2859 if ( elem->GetType() == SMDSAbs_Volume )
2861 SMDS_VolumeTool vol( elem );
2862 while ( nodeIt->more() ) {
2863 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2864 if ( theNode != n && vol.IsLinked( theNode, n ))
2865 linkedNodes.insert( n );
2870 for ( int i = 0; nodeIt->more(); ++i ) {
2871 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
2872 if ( n == theNode ) {
2873 int iBefore = i - 1;
2875 if ( elem->IsQuadratic() ) {
2876 int nb = elem->NbNodes() / 2;
2877 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
2878 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
2880 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
2881 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
2888 //=======================================================================
2889 //function : laplacianSmooth
2890 //purpose : pulls theNode toward the center of surrounding nodes directly
2891 // connected to that node along an element edge
2892 //=======================================================================
2894 void laplacianSmooth(const SMDS_MeshNode* theNode,
2895 const Handle(Geom_Surface)& theSurface,
2896 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2898 // find surrounding nodes
2900 TIDSortedElemSet nodeSet;
2901 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
2903 // compute new coodrs
2905 double coord[] = { 0., 0., 0. };
2906 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
2907 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
2908 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
2909 if ( theSurface.IsNull() ) { // smooth in 3D
2910 coord[0] += node->X();
2911 coord[1] += node->Y();
2912 coord[2] += node->Z();
2914 else { // smooth in 2D
2915 ASSERT( theUVMap.find( node ) != theUVMap.end() );
2916 gp_XY* uv = theUVMap[ node ];
2917 coord[0] += uv->X();
2918 coord[1] += uv->Y();
2921 int nbNodes = nodeSet.size();
2924 coord[0] /= nbNodes;
2925 coord[1] /= nbNodes;
2927 if ( !theSurface.IsNull() ) {
2928 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
2929 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
2930 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
2936 coord[2] /= nbNodes;
2940 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
2943 //=======================================================================
2944 //function : centroidalSmooth
2945 //purpose : pulls theNode toward the element-area-weighted centroid of the
2946 // surrounding elements
2947 //=======================================================================
2949 void centroidalSmooth(const SMDS_MeshNode* theNode,
2950 const Handle(Geom_Surface)& theSurface,
2951 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
2953 gp_XYZ aNewXYZ(0.,0.,0.);
2954 SMESH::Controls::Area anAreaFunc;
2955 double totalArea = 0.;
2960 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
2961 while ( elemIt->more() )
2963 const SMDS_MeshElement* elem = elemIt->next();
2966 gp_XYZ elemCenter(0.,0.,0.);
2967 SMESH::Controls::TSequenceOfXYZ aNodePoints;
2968 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2969 int nn = elem->NbNodes();
2970 if(elem->IsQuadratic()) nn = nn/2;
2972 //while ( itN->more() ) {
2974 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
2976 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
2977 aNodePoints.push_back( aP );
2978 if ( !theSurface.IsNull() ) { // smooth in 2D
2979 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
2980 gp_XY* uv = theUVMap[ aNode ];
2981 aP.SetCoord( uv->X(), uv->Y(), 0. );
2985 double elemArea = anAreaFunc.GetValue( aNodePoints );
2986 totalArea += elemArea;
2988 aNewXYZ += elemCenter * elemArea;
2990 aNewXYZ /= totalArea;
2991 if ( !theSurface.IsNull() ) {
2992 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
2993 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
2998 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3001 //=======================================================================
3002 //function : getClosestUV
3003 //purpose : return UV of closest projection
3004 //=======================================================================
3006 static bool getClosestUV (Extrema_GenExtPS& projector,
3007 const gp_Pnt& point,
3010 projector.Perform( point );
3011 if ( projector.IsDone() ) {
3012 double u, v, minVal = DBL_MAX;
3013 for ( int i = projector.NbExt(); i > 0; i-- )
3014 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3015 if ( projector.SquareDistance( i ) < minVal ) {
3016 minVal = projector.SquareDistance( i );
3018 if ( projector.Value( i ) < minVal ) {
3019 minVal = projector.Value( i );
3021 projector.Point( i ).Parameter( u, v );
3023 result.SetCoord( u, v );
3029 //=======================================================================
3031 //purpose : Smooth theElements during theNbIterations or until a worst
3032 // element has aspect ratio <= theTgtAspectRatio.
3033 // Aspect Ratio varies in range [1.0, inf].
3034 // If theElements is empty, the whole mesh is smoothed.
3035 // theFixedNodes contains additionally fixed nodes. Nodes built
3036 // on edges and boundary nodes are always fixed.
3037 //=======================================================================
3039 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3040 set<const SMDS_MeshNode*> & theFixedNodes,
3041 const SmoothMethod theSmoothMethod,
3042 const int theNbIterations,
3043 double theTgtAspectRatio,
3046 myLastCreatedElems.Clear();
3047 myLastCreatedNodes.Clear();
3049 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3051 if ( theTgtAspectRatio < 1.0 )
3052 theTgtAspectRatio = 1.0;
3054 const double disttol = 1.e-16;
3056 SMESH::Controls::AspectRatio aQualityFunc;
3058 SMESHDS_Mesh* aMesh = GetMeshDS();
3060 if ( theElems.empty() ) {
3061 // add all faces to theElems
3062 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3063 while ( fIt->more() ) {
3064 const SMDS_MeshElement* face = fIt->next();
3065 theElems.insert( face );
3068 // get all face ids theElems are on
3069 set< int > faceIdSet;
3070 TIDSortedElemSet::iterator itElem;
3072 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3073 int fId = FindShape( *itElem );
3074 // check that corresponding submesh exists and a shape is face
3076 faceIdSet.find( fId ) == faceIdSet.end() &&
3077 aMesh->MeshElements( fId )) {
3078 TopoDS_Shape F = aMesh->IndexToShape( fId );
3079 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3080 faceIdSet.insert( fId );
3083 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3085 // ===============================================
3086 // smooth elements on each TopoDS_Face separately
3087 // ===============================================
3089 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3090 for ( ; fId != faceIdSet.rend(); ++fId ) {
3091 // get face surface and submesh
3092 Handle(Geom_Surface) surface;
3093 SMESHDS_SubMesh* faceSubMesh = 0;
3095 double fToler2 = 0, f,l;
3096 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3097 bool isUPeriodic = false, isVPeriodic = false;
3099 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3100 surface = BRep_Tool::Surface( face );
3101 faceSubMesh = aMesh->MeshElements( *fId );
3102 fToler2 = BRep_Tool::Tolerance( face );
3103 fToler2 *= fToler2 * 10.;
3104 isUPeriodic = surface->IsUPeriodic();
3107 isVPeriodic = surface->IsVPeriodic();
3110 surface->Bounds( u1, u2, v1, v2 );
3112 // ---------------------------------------------------------
3113 // for elements on a face, find movable and fixed nodes and
3114 // compute UV for them
3115 // ---------------------------------------------------------
3116 bool checkBoundaryNodes = false;
3117 bool isQuadratic = false;
3118 set<const SMDS_MeshNode*> setMovableNodes;
3119 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3120 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3121 list< const SMDS_MeshElement* > elemsOnFace;
3123 Extrema_GenExtPS projector;
3124 GeomAdaptor_Surface surfAdaptor;
3125 if ( !surface.IsNull() ) {
3126 surfAdaptor.Load( surface );
3127 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3129 int nbElemOnFace = 0;
3130 itElem = theElems.begin();
3131 // loop on not yet smoothed elements: look for elems on a face
3132 while ( itElem != theElems.end() ) {
3133 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3134 break; // all elements found
3136 const SMDS_MeshElement* elem = *itElem;
3137 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3138 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3142 elemsOnFace.push_back( elem );
3143 theElems.erase( itElem++ );
3147 isQuadratic = elem->IsQuadratic();
3149 // get movable nodes of elem
3150 const SMDS_MeshNode* node;
3151 SMDS_TypeOfPosition posType;
3152 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3153 int nn = 0, nbn = elem->NbNodes();
3154 if(elem->IsQuadratic())
3156 while ( nn++ < nbn ) {
3157 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3158 const SMDS_PositionPtr& pos = node->GetPosition();
3159 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3160 if (posType != SMDS_TOP_EDGE &&
3161 posType != SMDS_TOP_VERTEX &&
3162 theFixedNodes.find( node ) == theFixedNodes.end())
3164 // check if all faces around the node are on faceSubMesh
3165 // because a node on edge may be bound to face
3166 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3168 if ( faceSubMesh ) {
3169 while ( eIt->more() && all ) {
3170 const SMDS_MeshElement* e = eIt->next();
3171 all = faceSubMesh->Contains( e );
3175 setMovableNodes.insert( node );
3177 checkBoundaryNodes = true;
3179 if ( posType == SMDS_TOP_3DSPACE )
3180 checkBoundaryNodes = true;
3183 if ( surface.IsNull() )
3186 // get nodes to check UV
3187 list< const SMDS_MeshNode* > uvCheckNodes;
3188 itN = elem->nodesIterator();
3189 nn = 0; nbn = elem->NbNodes();
3190 if(elem->IsQuadratic())
3192 while ( nn++ < nbn ) {
3193 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3194 if ( uvMap.find( node ) == uvMap.end() )
3195 uvCheckNodes.push_back( node );
3196 // add nodes of elems sharing node
3197 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3198 // while ( eIt->more() ) {
3199 // const SMDS_MeshElement* e = eIt->next();
3200 // if ( e != elem ) {
3201 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3202 // while ( nIt->more() ) {
3203 // const SMDS_MeshNode* n =
3204 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3205 // if ( uvMap.find( n ) == uvMap.end() )
3206 // uvCheckNodes.push_back( n );
3212 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3213 for ( ; n != uvCheckNodes.end(); ++n ) {
3216 const SMDS_PositionPtr& pos = node->GetPosition();
3217 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3219 switch ( posType ) {
3220 case SMDS_TOP_FACE: {
3221 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3222 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3225 case SMDS_TOP_EDGE: {
3226 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3227 Handle(Geom2d_Curve) pcurve;
3228 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3229 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3230 if ( !pcurve.IsNull() ) {
3231 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3232 uv = pcurve->Value( u ).XY();
3236 case SMDS_TOP_VERTEX: {
3237 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3238 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3239 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3244 // check existing UV
3245 bool project = true;
3246 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3247 double dist1 = DBL_MAX, dist2 = 0;
3248 if ( posType != SMDS_TOP_3DSPACE ) {
3249 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3250 project = dist1 > fToler2;
3252 if ( project ) { // compute new UV
3254 if ( !getClosestUV( projector, pNode, newUV )) {
3255 MESSAGE("Node Projection Failed " << node);
3259 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3261 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3263 if ( posType != SMDS_TOP_3DSPACE )
3264 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3265 if ( dist2 < dist1 )
3269 // store UV in the map
3270 listUV.push_back( uv );
3271 uvMap.insert( make_pair( node, &listUV.back() ));
3273 } // loop on not yet smoothed elements
3275 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3276 checkBoundaryNodes = true;
3278 // fix nodes on mesh boundary
3280 if ( checkBoundaryNodes ) {
3281 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3282 map< SMESH_TLink, int >::iterator link_nb;
3283 // put all elements links to linkNbMap
3284 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3285 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3286 const SMDS_MeshElement* elem = (*elemIt);
3287 int nbn = elem->NbCornerNodes();
3288 // loop on elem links: insert them in linkNbMap
3289 for ( int iN = 0; iN < nbn; ++iN ) {
3290 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3291 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3292 SMESH_TLink link( n1, n2 );
3293 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3297 // remove nodes that are in links encountered only once from setMovableNodes
3298 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3299 if ( link_nb->second == 1 ) {
3300 setMovableNodes.erase( link_nb->first.node1() );
3301 setMovableNodes.erase( link_nb->first.node2() );
3306 // -----------------------------------------------------
3307 // for nodes on seam edge, compute one more UV ( uvMap2 );
3308 // find movable nodes linked to nodes on seam and which
3309 // are to be smoothed using the second UV ( uvMap2 )
3310 // -----------------------------------------------------
3312 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3313 if ( !surface.IsNull() ) {
3314 TopExp_Explorer eExp( face, TopAbs_EDGE );
3315 for ( ; eExp.More(); eExp.Next() ) {
3316 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3317 if ( !BRep_Tool::IsClosed( edge, face ))
3319 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3320 if ( !sm ) continue;
3321 // find out which parameter varies for a node on seam
3324 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3325 if ( pcurve.IsNull() ) continue;
3326 uv1 = pcurve->Value( f );
3328 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3329 if ( pcurve.IsNull() ) continue;
3330 uv2 = pcurve->Value( f );
3331 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3333 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3334 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3336 // get nodes on seam and its vertices
3337 list< const SMDS_MeshNode* > seamNodes;
3338 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3339 while ( nSeamIt->more() ) {
3340 const SMDS_MeshNode* node = nSeamIt->next();
3341 if ( !isQuadratic || !IsMedium( node ))
3342 seamNodes.push_back( node );
3344 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3345 for ( ; vExp.More(); vExp.Next() ) {
3346 sm = aMesh->MeshElements( vExp.Current() );
3348 nSeamIt = sm->GetNodes();
3349 while ( nSeamIt->more() )
3350 seamNodes.push_back( nSeamIt->next() );
3353 // loop on nodes on seam
3354 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3355 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3356 const SMDS_MeshNode* nSeam = *noSeIt;
3357 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3358 if ( n_uv == uvMap.end() )
3361 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3362 // set the second UV
3363 listUV.push_back( *n_uv->second );
3364 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3365 if ( uvMap2.empty() )
3366 uvMap2 = uvMap; // copy the uvMap contents
3367 uvMap2[ nSeam ] = &listUV.back();
3369 // collect movable nodes linked to ones on seam in nodesNearSeam
3370 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3371 while ( eIt->more() ) {
3372 const SMDS_MeshElement* e = eIt->next();
3373 int nbUseMap1 = 0, nbUseMap2 = 0;
3374 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3375 int nn = 0, nbn = e->NbNodes();
3376 if(e->IsQuadratic()) nbn = nbn/2;
3377 while ( nn++ < nbn )
3379 const SMDS_MeshNode* n =
3380 static_cast<const SMDS_MeshNode*>( nIt->next() );
3382 setMovableNodes.find( n ) == setMovableNodes.end() )
3384 // add only nodes being closer to uv2 than to uv1
3385 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3386 0.5 * ( n->Y() + nSeam->Y() ),
3387 0.5 * ( n->Z() + nSeam->Z() ));
3389 getClosestUV( projector, pMid, uv );
3390 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3391 nodesNearSeam.insert( n );
3397 // for centroidalSmooth all element nodes must
3398 // be on one side of a seam
3399 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3400 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3402 while ( nn++ < nbn ) {
3403 const SMDS_MeshNode* n =
3404 static_cast<const SMDS_MeshNode*>( nIt->next() );
3405 setMovableNodes.erase( n );
3409 } // loop on nodes on seam
3410 } // loop on edge of a face
3411 } // if ( !face.IsNull() )
3413 if ( setMovableNodes.empty() ) {
3414 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3415 continue; // goto next face
3423 double maxRatio = -1., maxDisplacement = -1.;
3424 set<const SMDS_MeshNode*>::iterator nodeToMove;
3425 for ( it = 0; it < theNbIterations; it++ ) {
3426 maxDisplacement = 0.;
3427 nodeToMove = setMovableNodes.begin();
3428 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3429 const SMDS_MeshNode* node = (*nodeToMove);
3430 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3433 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3434 if ( theSmoothMethod == LAPLACIAN )
3435 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3437 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3439 // node displacement
3440 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3441 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3442 if ( aDispl > maxDisplacement )
3443 maxDisplacement = aDispl;
3445 // no node movement => exit
3446 //if ( maxDisplacement < 1.e-16 ) {
3447 if ( maxDisplacement < disttol ) {
3448 MESSAGE("-- no node movement --");
3452 // check elements quality
3454 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3455 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3456 const SMDS_MeshElement* elem = (*elemIt);
3457 if ( !elem || elem->GetType() != SMDSAbs_Face )
3459 SMESH::Controls::TSequenceOfXYZ aPoints;
3460 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3461 double aValue = aQualityFunc.GetValue( aPoints );
3462 if ( aValue > maxRatio )
3466 if ( maxRatio <= theTgtAspectRatio ) {
3467 MESSAGE("-- quality achived --");
3470 if (it+1 == theNbIterations) {
3471 MESSAGE("-- Iteration limit exceeded --");
3473 } // smoothing iterations
3475 MESSAGE(" Face id: " << *fId <<
3476 " Nb iterstions: " << it <<
3477 " Displacement: " << maxDisplacement <<
3478 " Aspect Ratio " << maxRatio);
3480 // ---------------------------------------
3481 // new nodes positions are computed,
3482 // record movement in DS and set new UV
3483 // ---------------------------------------
3484 nodeToMove = setMovableNodes.begin();
3485 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3486 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3487 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3488 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3489 if ( node_uv != uvMap.end() ) {
3490 gp_XY* uv = node_uv->second;
3492 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3496 // move medium nodes of quadratic elements
3499 SMESH_MesherHelper helper( *GetMesh() );
3500 if ( !face.IsNull() )
3501 helper.SetSubShape( face );
3502 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3503 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3504 const SMDS_VtkFace* QF =
3505 dynamic_cast<const SMDS_VtkFace*> (*elemIt);
3506 if(QF && QF->IsQuadratic()) {
3507 vector<const SMDS_MeshNode*> Ns;
3508 Ns.reserve(QF->NbNodes()+1);
3509 SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator();
3510 while ( anIter->more() )
3511 Ns.push_back( cast2Node(anIter->next()) );
3512 Ns.push_back( Ns[0] );
3514 for(int i=0; i<QF->NbNodes(); i=i+2) {
3515 if ( !surface.IsNull() ) {
3516 gp_XY uv1 = helper.GetNodeUV( face, Ns[i], Ns[i+2] );
3517 gp_XY uv2 = helper.GetNodeUV( face, Ns[i+2], Ns[i] );
3518 gp_XY uv = ( uv1 + uv2 ) / 2.;
3519 gp_Pnt xyz = surface->Value( uv.X(), uv.Y() );
3520 x = xyz.X(); y = xyz.Y(); z = xyz.Z();
3523 x = (Ns[i]->X() + Ns[i+2]->X())/2;
3524 y = (Ns[i]->Y() + Ns[i+2]->Y())/2;
3525 z = (Ns[i]->Z() + Ns[i+2]->Z())/2;
3527 if( fabs( Ns[i+1]->X() - x ) > disttol ||
3528 fabs( Ns[i+1]->Y() - y ) > disttol ||
3529 fabs( Ns[i+1]->Z() - z ) > disttol ) {
3530 // we have to move i+1 node
3531 aMesh->MoveNode( Ns[i+1], x, y, z );
3538 } // loop on face ids
3542 //=======================================================================
3543 //function : isReverse
3544 //purpose : Return true if normal of prevNodes is not co-directied with
3545 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3546 // iNotSame is where prevNodes and nextNodes are different.
3547 // If result is true then future volume orientation is OK
3548 //=======================================================================
3550 static bool isReverse(const SMDS_MeshElement* face,
3551 const vector<const SMDS_MeshNode*>& prevNodes,
3552 const vector<const SMDS_MeshNode*>& nextNodes,
3556 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3557 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3558 gp_XYZ extrDir( pN - pP ), faceNorm;
3559 SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false );
3561 return faceNorm * extrDir < 0.0;
3564 //=======================================================================
3566 * \brief Create elements by sweeping an element
3567 * \param elem - element to sweep
3568 * \param newNodesItVec - nodes generated from each node of the element
3569 * \param newElems - generated elements
3570 * \param nbSteps - number of sweeping steps
3571 * \param srcElements - to append elem for each generated element
3573 //=======================================================================
3575 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3576 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3577 list<const SMDS_MeshElement*>& newElems,
3579 SMESH_SequenceOfElemPtr& srcElements)
3581 //MESSAGE("sweepElement " << nbSteps);
3582 SMESHDS_Mesh* aMesh = GetMeshDS();
3584 const int nbNodes = elem->NbNodes();
3585 const int nbCorners = elem->NbCornerNodes();
3586 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3587 polyhedron creation !!! */
3588 // Loop on elem nodes:
3589 // find new nodes and detect same nodes indices
3590 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3591 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3592 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3593 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3595 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3596 vector<int> sames(nbNodes);
3597 vector<bool> isSingleNode(nbNodes);
3599 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3600 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3601 const SMDS_MeshNode* node = nnIt->first;
3602 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3603 if ( listNewNodes.empty() )
3606 itNN [ iNode ] = listNewNodes.begin();
3607 prevNod[ iNode ] = node;
3608 nextNod[ iNode ] = listNewNodes.front();
3610 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3611 corner node of linear */
3612 if ( prevNod[ iNode ] != nextNod [ iNode ])
3613 nbDouble += !isSingleNode[iNode];
3615 if( iNode < nbCorners ) { // check corners only
3616 if ( prevNod[ iNode ] == nextNod [ iNode ])
3617 sames[nbSame++] = iNode;
3619 iNotSameNode = iNode;
3623 if ( nbSame == nbNodes || nbSame > 2) {
3624 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3628 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3630 // fix nodes order to have bottom normal external
3631 if ( baseType == SMDSEntity_Polygon )
3633 std::reverse( itNN.begin(), itNN.end() );
3634 std::reverse( prevNod.begin(), prevNod.end() );
3635 std::reverse( midlNod.begin(), midlNod.end() );
3636 std::reverse( nextNod.begin(), nextNod.end() );
3637 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3641 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3642 SMDS_MeshCell::applyInterlace( ind, itNN );
3643 SMDS_MeshCell::applyInterlace( ind, prevNod );
3644 SMDS_MeshCell::applyInterlace( ind, nextNod );
3645 SMDS_MeshCell::applyInterlace( ind, midlNod );
3646 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3649 sames[nbSame] = iNotSameNode;
3650 for ( int j = 0; j <= nbSame; ++j )
3651 for ( size_t i = 0; i < ind.size(); ++i )
3652 if ( ind[i] == sames[j] )
3657 iNotSameNode = sames[nbSame];
3662 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3664 iSameNode = sames[ nbSame-1 ];
3665 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3666 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3667 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3670 // make new elements
3671 for (int iStep = 0; iStep < nbSteps; iStep++ )
3674 for ( iNode = 0; iNode < nbNodes; iNode++ )
3676 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3677 nextNod[ iNode ] = *itNN[ iNode ]++;
3680 SMDS_MeshElement* aNewElem = 0;
3681 /*if(!elem->IsPoly())*/ {
3682 switch ( baseType ) {
3684 case SMDSEntity_Node: { // sweep NODE
3685 if ( nbSame == 0 ) {
3686 if ( isSingleNode[0] )
3687 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3689 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3695 case SMDSEntity_Edge: { // sweep EDGE
3696 if ( nbDouble == 0 )
3698 if ( nbSame == 0 ) // ---> quadrangle
3699 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3700 nextNod[ 1 ], nextNod[ 0 ] );
3701 else // ---> triangle
3702 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3703 nextNod[ iNotSameNode ] );
3705 else // ---> polygon
3707 vector<const SMDS_MeshNode*> poly_nodes;
3708 poly_nodes.push_back( prevNod[0] );
3709 poly_nodes.push_back( prevNod[1] );
3710 if ( prevNod[1] != nextNod[1] )
3712 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3713 poly_nodes.push_back( nextNod[1] );
3715 if ( prevNod[0] != nextNod[0] )
3717 poly_nodes.push_back( nextNod[0] );
3718 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3720 switch ( poly_nodes.size() ) {
3722 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3725 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3726 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3729 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3734 case SMDSEntity_Triangle: // TRIANGLE --->
3736 if ( nbDouble > 0 ) break;
3737 if ( nbSame == 0 ) // ---> pentahedron
3738 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3739 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3741 else if ( nbSame == 1 ) // ---> pyramid
3742 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3743 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3744 nextNod[ iSameNode ]);
3746 else // 2 same nodes: ---> tetrahedron
3747 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3748 nextNod[ iNotSameNode ]);
3751 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3755 if ( nbDouble+nbSame == 2 )
3757 if(nbSame==0) { // ---> quadratic quadrangle
3758 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3759 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3761 else { //(nbSame==1) // ---> quadratic triangle
3763 return; // medium node on axis
3765 else if(sames[0]==0)
3766 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3767 nextNod[2], midlNod[1], prevNod[2]);
3769 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3770 midlNod[0], nextNod[2], prevNod[2]);
3773 else if ( nbDouble == 3 )
3775 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3776 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3777 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3784 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3785 if ( nbDouble > 0 ) break;
3787 if ( nbSame == 0 ) // ---> hexahedron
3788 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3789 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3791 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3792 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3793 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3794 nextNod[ iSameNode ]);
3795 newElems.push_back( aNewElem );
3796 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3797 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3798 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3800 else if ( nbSame == 2 ) { // ---> pentahedron
3801 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3802 // iBeforeSame is same too
3803 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3804 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3805 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3807 // iAfterSame is same too
3808 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3809 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3810 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3814 case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE --->
3815 if ( nbDouble+nbSame != 3 ) break;
3817 // ---> pentahedron with 15 nodes
3818 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3819 nextNod[0], nextNod[1], nextNod[2],
3820 prevNod[3], prevNod[4], prevNod[5],
3821 nextNod[3], nextNod[4], nextNod[5],
3822 midlNod[0], midlNod[1], midlNod[2]);
3824 else if(nbSame==1) {
3825 // ---> 2d order pyramid of 13 nodes
3826 int apex = iSameNode;
3827 int i0 = ( apex + 1 ) % nbCorners;
3828 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
3832 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
3833 nextNod[i0], nextNod[i1], prevNod[apex],
3834 prevNod[i01], midlNod[i0],
3835 nextNod[i01], midlNod[i1],
3836 prevNod[i1a], prevNod[i0a],
3837 nextNod[i0a], nextNod[i1a]);
3839 else if(nbSame==2) {
3840 // ---> 2d order tetrahedron of 10 nodes
3841 int n1 = iNotSameNode;
3842 int n2 = ( n1 + 1 ) % nbCorners;
3843 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
3847 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
3848 prevNod[n12], prevNod[n23], prevNod[n31],
3849 midlNod[n1], nextNod[n12], nextNod[n31]);
3853 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
3855 if ( nbDouble != 4 ) break;
3856 // ---> hexahedron with 20 nodes
3857 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3858 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3859 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3860 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3861 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
3863 else if(nbSame==1) {
3864 // ---> pyramid + pentahedron - can not be created since it is needed
3865 // additional middle node at the center of face
3866 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
3869 else if( nbSame == 2 ) {
3870 if ( nbDouble != 2 ) break;
3871 // ---> 2d order Pentahedron with 15 nodes
3873 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
3874 // iBeforeSame is same too
3881 // iAfterSame is same too
3891 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
3892 prevNod[n4], prevNod[n5], nextNod[n5],
3893 prevNod[n12], midlNod[n2], nextNod[n12],
3894 prevNod[n45], midlNod[n5], nextNod[n45],
3895 prevNod[n14], prevNod[n25], nextNod[n25]);
3899 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
3901 if( nbSame == 0 && nbDouble == 9 ) {
3902 // ---> tri-quadratic hexahedron with 27 nodes
3903 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
3904 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
3905 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
3906 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
3907 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
3908 prevNod[8], // bottom center
3909 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
3910 nextNod[8], // top center
3911 midlNod[8]);// elem center
3919 case SMDSEntity_Polygon: { // sweep POLYGON
3921 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
3922 // ---> hexagonal prism
3923 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3924 prevNod[3], prevNod[4], prevNod[5],
3925 nextNod[0], nextNod[1], nextNod[2],
3926 nextNod[3], nextNod[4], nextNod[5]);
3930 case SMDSEntity_Ball:
3938 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
3940 if ( baseType != SMDSEntity_Polygon )
3942 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
3943 SMDS_MeshCell::applyInterlace( ind, prevNod );
3944 SMDS_MeshCell::applyInterlace( ind, nextNod );
3945 SMDS_MeshCell::applyInterlace( ind, midlNod );
3946 SMDS_MeshCell::applyInterlace( ind, itNN );
3947 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3948 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
3950 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
3951 vector<int> quantities (nbNodes + 2);
3952 polyedre_nodes.clear();
3956 for (int inode = 0; inode < nbNodes; inode++)
3957 polyedre_nodes.push_back( prevNod[inode] );
3958 quantities.push_back( nbNodes );
3961 polyedre_nodes.push_back( nextNod[0] );
3962 for (int inode = nbNodes; inode-1; --inode )
3963 polyedre_nodes.push_back( nextNod[inode-1] );
3964 quantities.push_back( nbNodes );
3967 for (int iface = 0; iface < nbNodes; iface++)
3969 const int prevNbNodes = polyedre_nodes.size();
3970 int inextface = (iface+1) % nbNodes;
3971 polyedre_nodes.push_back( prevNod[inextface] );
3972 polyedre_nodes.push_back( prevNod[iface] );
3973 if ( prevNod[iface] != nextNod[iface] )
3975 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
3976 polyedre_nodes.push_back( nextNod[iface] );
3978 if ( prevNod[inextface] != nextNod[inextface] )
3980 polyedre_nodes.push_back( nextNod[inextface] );
3981 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
3983 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
3984 if ( nbFaceNodes > 2 )
3985 quantities.push_back( nbFaceNodes );
3986 else // degenerated face
3987 polyedre_nodes.resize( prevNbNodes );
3989 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
3993 newElems.push_back( aNewElem );
3994 myLastCreatedElems.Append(aNewElem);
3995 srcElements.Append( elem );
3998 // set new prev nodes
3999 for ( iNode = 0; iNode < nbNodes; iNode++ )
4000 prevNod[ iNode ] = nextNod[ iNode ];
4005 //=======================================================================
4007 * \brief Create 1D and 2D elements around swept elements
4008 * \param mapNewNodes - source nodes and ones generated from them
4009 * \param newElemsMap - source elements and ones generated from them
4010 * \param elemNewNodesMap - nodes generated from each node of each element
4011 * \param elemSet - all swept elements
4012 * \param nbSteps - number of sweeping steps
4013 * \param srcElements - to append elem for each generated element
4015 //=======================================================================
4017 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4018 TElemOfElemListMap & newElemsMap,
4019 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4020 TIDSortedElemSet& elemSet,
4022 SMESH_SequenceOfElemPtr& srcElements)
4024 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4025 SMESHDS_Mesh* aMesh = GetMeshDS();
4027 // Find nodes belonging to only one initial element - sweep them to get edges.
4029 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4030 for ( ; nList != mapNewNodes.end(); nList++ )
4032 const SMDS_MeshNode* node =
4033 static_cast<const SMDS_MeshNode*>( nList->first );
4034 if ( newElemsMap.count( node ))
4035 continue; // node was extruded into edge
4036 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4037 int nbInitElems = 0;
4038 const SMDS_MeshElement* el = 0;
4039 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4040 while ( eIt->more() && nbInitElems < 2 ) {
4042 SMDSAbs_ElementType type = el->GetType();
4043 if ( type == SMDSAbs_Volume || type < highType ) continue;
4044 if ( type > highType ) {
4048 nbInitElems += elemSet.count(el);
4050 if ( nbInitElems < 2 ) {
4051 bool NotCreateEdge = el && el->IsMediumNode(node);
4052 if(!NotCreateEdge) {
4053 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4054 list<const SMDS_MeshElement*> newEdges;
4055 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4060 // Make a ceiling for each element ie an equal element of last new nodes.
4061 // Find free links of faces - make edges and sweep them into faces.
4063 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4064 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4065 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4067 const SMDS_MeshElement* elem = itElem->first;
4068 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4070 if(itElem->second.size()==0) continue;
4072 const bool isQuadratic = elem->IsQuadratic();
4074 if ( elem->GetType() == SMDSAbs_Edge ) {
4075 // create a ceiling edge
4076 if ( !isQuadratic ) {
4077 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4078 vecNewNodes[ 1 ]->second.back())) {
4079 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4080 vecNewNodes[ 1 ]->second.back()));
4081 srcElements.Append( myLastCreatedElems.Last() );
4085 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4086 vecNewNodes[ 1 ]->second.back(),
4087 vecNewNodes[ 2 ]->second.back())) {
4088 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4089 vecNewNodes[ 1 ]->second.back(),
4090 vecNewNodes[ 2 ]->second.back()));
4091 srcElements.Append( myLastCreatedElems.Last() );
4095 if ( elem->GetType() != SMDSAbs_Face )
4098 bool hasFreeLinks = false;
4100 TIDSortedElemSet avoidSet;
4101 avoidSet.insert( elem );
4103 set<const SMDS_MeshNode*> aFaceLastNodes;
4104 int iNode, nbNodes = vecNewNodes.size();
4105 if ( !isQuadratic ) {
4106 // loop on the face nodes
4107 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4108 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4109 // look for free links of the face
4110 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4111 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4112 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4113 // check if a link is free
4114 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4115 hasFreeLinks = true;
4116 // make an edge and a ceiling for a new edge
4117 if ( !aMesh->FindEdge( n1, n2 )) {
4118 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4119 srcElements.Append( myLastCreatedElems.Last() );
4121 n1 = vecNewNodes[ iNode ]->second.back();
4122 n2 = vecNewNodes[ iNext ]->second.back();
4123 if ( !aMesh->FindEdge( n1, n2 )) {
4124 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4125 srcElements.Append( myLastCreatedElems.Last() );
4130 else { // elem is quadratic face
4131 int nbn = nbNodes/2;
4132 for ( iNode = 0; iNode < nbn; iNode++ ) {
4133 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4134 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4135 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4136 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4137 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4138 // check if a link is free
4139 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4140 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4141 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4142 hasFreeLinks = true;
4143 // make an edge and a ceiling for a new edge
4145 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4146 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4147 srcElements.Append( myLastCreatedElems.Last() );
4149 n1 = vecNewNodes[ iNode ]->second.back();
4150 n2 = vecNewNodes[ iNext ]->second.back();
4151 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4152 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4153 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4154 srcElements.Append( myLastCreatedElems.Last() );
4158 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4159 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4163 // sweep free links into faces
4165 if ( hasFreeLinks ) {
4166 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4167 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4169 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4170 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4171 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4172 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4174 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4175 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4176 std::advance( v, volNb );
4177 // find indices of free faces of a volume and their source edges
4178 list< int > freeInd;
4179 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4180 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4181 int iF, nbF = vTool.NbFaces();
4182 for ( iF = 0; iF < nbF; iF ++ ) {
4183 if (vTool.IsFreeFace( iF ) &&
4184 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4185 initNodeSet != faceNodeSet) // except an initial face
4187 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4189 freeInd.push_back( iF );
4190 // find source edge of a free face iF
4191 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4192 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4193 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4194 initNodeSet.begin(), initNodeSet.end(),
4195 commonNodes.begin());
4196 if ( (*v)->IsQuadratic() )
4197 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4199 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4201 if ( !srcEdges.back() )
4203 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4204 << iF << " of volume #" << vTool.ID() << endl;
4209 if ( freeInd.empty() )
4212 // create faces for all steps;
4213 // if such a face has been already created by sweep of edge,
4214 // assure that its orientation is OK
4215 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4216 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4217 vTool.SetExternalNormal();
4218 const int nextShift = vTool.IsForward() ? +1 : -1;
4219 list< int >::iterator ind = freeInd.begin();
4220 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4221 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4223 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4224 int nbn = vTool.NbFaceNodes( *ind );
4225 const SMDS_MeshElement * f = 0;
4226 if ( nbn == 3 ) ///// triangle
4228 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4230 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4232 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4234 nodes[ 1 + nextShift ] };
4236 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4238 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4242 else if ( nbn == 4 ) ///// quadrangle
4244 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4246 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4248 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4249 nodes[ 2 ], nodes[ 2+nextShift ] };
4251 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4253 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4254 newOrder[ 2 ], newOrder[ 3 ]));
4257 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4259 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4261 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4263 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4265 nodes[2 + 2*nextShift],
4266 nodes[3 - 2*nextShift],
4268 nodes[3 + 2*nextShift]};
4270 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4272 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4280 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4282 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4283 nodes[1], nodes[3], nodes[5], nodes[7] );
4285 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4287 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4288 nodes[4 - 2*nextShift],
4290 nodes[4 + 2*nextShift],
4292 nodes[5 - 2*nextShift],
4294 nodes[5 + 2*nextShift] };
4296 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4298 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4299 newOrder[ 2 ], newOrder[ 3 ],
4300 newOrder[ 4 ], newOrder[ 5 ],
4301 newOrder[ 6 ], newOrder[ 7 ]));
4304 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4306 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4307 SMDSAbs_Face, /*noMedium=*/false);
4309 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4311 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4312 nodes[4 - 2*nextShift],
4314 nodes[4 + 2*nextShift],
4316 nodes[5 - 2*nextShift],
4318 nodes[5 + 2*nextShift],
4321 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4323 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4324 newOrder[ 2 ], newOrder[ 3 ],
4325 newOrder[ 4 ], newOrder[ 5 ],
4326 newOrder[ 6 ], newOrder[ 7 ],
4330 else //////// polygon
4332 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4333 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4335 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4337 if ( !vTool.IsForward() )
4338 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4340 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4342 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4346 while ( srcElements.Length() < myLastCreatedElems.Length() )
4347 srcElements.Append( *srcEdge );
4349 } // loop on free faces
4351 // go to the next volume
4353 while ( iVol++ < nbVolumesByStep ) v++;
4356 } // loop on volumes of one step
4357 } // sweep free links into faces
4359 // Make a ceiling face with a normal external to a volume
4361 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4363 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4365 lastVol.SetExternalNormal();
4366 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4367 int nbn = lastVol.NbFaceNodes( iF );
4369 if (!hasFreeLinks ||
4370 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4371 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4373 else if ( nbn == 4 )
4375 if (!hasFreeLinks ||
4376 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4377 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4379 else if ( nbn == 6 && isQuadratic )
4381 if (!hasFreeLinks ||
4382 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4383 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4384 nodes[1], nodes[3], nodes[5]));
4386 else if ( nbn == 8 && isQuadratic )
4388 if (!hasFreeLinks ||
4389 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4390 nodes[1], nodes[3], nodes[5], nodes[7]) )
4391 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4392 nodes[1], nodes[3], nodes[5], nodes[7]));
4394 else if ( nbn == 9 && isQuadratic )
4396 if (!hasFreeLinks ||
4397 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4398 SMDSAbs_Face, /*noMedium=*/false) )
4399 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4400 nodes[1], nodes[3], nodes[5], nodes[7],
4404 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4405 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4406 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4409 while ( srcElements.Length() < myLastCreatedElems.Length() )
4410 srcElements.Append( myLastCreatedElems.Last() );
4412 } // loop on swept elements
4415 //=======================================================================
4416 //function : RotationSweep
4418 //=======================================================================
4420 SMESH_MeshEditor::PGroupIDs
4421 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4422 const gp_Ax1& theAxis,
4423 const double theAngle,
4424 const int theNbSteps,
4425 const double theTol,
4426 const bool theMakeGroups,
4427 const bool theMakeWalls)
4429 myLastCreatedElems.Clear();
4430 myLastCreatedNodes.Clear();
4432 // source elements for each generated one
4433 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4435 MESSAGE( "RotationSweep()");
4437 aTrsf.SetRotation( theAxis, theAngle );
4439 aTrsf2.SetRotation( theAxis, theAngle/2. );
4441 gp_Lin aLine( theAxis );
4442 double aSqTol = theTol * theTol;
4444 SMESHDS_Mesh* aMesh = GetMeshDS();
4446 TNodeOfNodeListMap mapNewNodes;
4447 TElemOfVecOfNnlmiMap mapElemNewNodes;
4448 TElemOfElemListMap newElemsMap;
4450 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4451 myMesh->NbFaces(ORDER_QUADRATIC) +
4452 myMesh->NbVolumes(ORDER_QUADRATIC) );
4454 TIDSortedElemSet::iterator itElem;
4455 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4456 const SMDS_MeshElement* elem = *itElem;
4457 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4459 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4460 newNodesItVec.reserve( elem->NbNodes() );
4462 // loop on elem nodes
4463 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4464 while ( itN->more() )
4466 // check if a node has been already sweeped
4467 const SMDS_MeshNode* node = cast2Node( itN->next() );
4469 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4471 aXYZ.Coord( coord[0], coord[1], coord[2] );
4472 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4474 TNodeOfNodeListMapItr nIt =
4475 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4476 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4477 if ( listNewNodes.empty() )
4479 // check if we are to create medium nodes between corner ones
4480 bool needMediumNodes = false;
4481 if ( isQuadraticMesh )
4483 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4484 while (it->more() && !needMediumNodes )
4486 const SMDS_MeshElement* invElem = it->next();
4487 if ( invElem != elem && !theElems.count( invElem )) continue;
4488 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4489 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4490 needMediumNodes = true;
4495 const SMDS_MeshNode * newNode = node;
4496 for ( int i = 0; i < theNbSteps; i++ ) {
4498 if ( needMediumNodes ) // create a medium node
4500 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4501 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4502 myLastCreatedNodes.Append(newNode);
4503 srcNodes.Append( node );
4504 listNewNodes.push_back( newNode );
4505 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4508 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4510 // create a corner node
4511 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4512 myLastCreatedNodes.Append(newNode);
4513 srcNodes.Append( node );
4514 listNewNodes.push_back( newNode );
4517 listNewNodes.push_back( newNode );
4518 // if ( needMediumNodes )
4519 // listNewNodes.push_back( newNode );
4523 newNodesItVec.push_back( nIt );
4525 // make new elements
4526 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4530 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4532 PGroupIDs newGroupIDs;
4533 if ( theMakeGroups )
4534 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4540 //=======================================================================
4541 //function : CreateNode
4543 //=======================================================================
4544 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4547 const double tolnode,
4548 SMESH_SequenceOfNode& aNodes)
4550 // myLastCreatedElems.Clear();
4551 // myLastCreatedNodes.Clear();
4554 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4556 // try to search in sequence of existing nodes
4557 // if aNodes.Length()>0 we 'nave to use given sequence
4558 // else - use all nodes of mesh
4559 if(aNodes.Length()>0) {
4561 for(i=1; i<=aNodes.Length(); i++) {
4562 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4563 if(P1.Distance(P2)<tolnode)
4564 return aNodes.Value(i);
4568 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4569 while(itn->more()) {
4570 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4571 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4572 if(P1.Distance(P2)<tolnode)
4577 // create new node and return it
4578 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4579 //myLastCreatedNodes.Append(NewNode);
4584 //=======================================================================
4585 //function : ExtrusionSweep
4587 //=======================================================================
4589 SMESH_MeshEditor::PGroupIDs
4590 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4591 const gp_Vec& theStep,
4592 const int theNbSteps,
4593 TElemOfElemListMap& newElemsMap,
4594 const bool theMakeGroups,
4596 const double theTolerance)
4598 ExtrusParam aParams;
4599 aParams.myDir = gp_Dir(theStep);
4600 aParams.myNodes.Clear();
4601 aParams.mySteps = new TColStd_HSequenceOfReal;
4603 for(i=1; i<=theNbSteps; i++)
4604 aParams.mySteps->Append(theStep.Magnitude());
4607 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4611 //=======================================================================
4612 //function : ExtrusionSweep
4614 //=======================================================================
4616 SMESH_MeshEditor::PGroupIDs
4617 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4618 ExtrusParam& theParams,
4619 TElemOfElemListMap& newElemsMap,
4620 const bool theMakeGroups,
4622 const double theTolerance)
4624 myLastCreatedElems.Clear();
4625 myLastCreatedNodes.Clear();
4627 // source elements for each generated one
4628 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4630 SMESHDS_Mesh* aMesh = GetMeshDS();
4632 int nbsteps = theParams.mySteps->Length();
4634 TNodeOfNodeListMap mapNewNodes;
4635 //TNodeOfNodeVecMap mapNewNodes;
4636 TElemOfVecOfNnlmiMap mapElemNewNodes;
4637 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4639 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4640 myMesh->NbFaces(ORDER_QUADRATIC) +
4641 myMesh->NbVolumes(ORDER_QUADRATIC) );
4643 TIDSortedElemSet::iterator itElem;
4644 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4645 // check element type
4646 const SMDS_MeshElement* elem = *itElem;
4647 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4650 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4651 newNodesItVec.reserve( elem->NbNodes() );
4653 // loop on elem nodes
4654 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4655 while ( itN->more() )
4657 // check if a node has been already sweeped
4658 const SMDS_MeshNode* node = cast2Node( itN->next() );
4659 TNodeOfNodeListMap::iterator nIt =
4660 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4661 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4662 if ( listNewNodes.empty() )
4666 // check if we are to create medium nodes between corner ones
4667 bool needMediumNodes = false;
4668 if ( isQuadraticMesh )
4670 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4671 while (it->more() && !needMediumNodes )
4673 const SMDS_MeshElement* invElem = it->next();
4674 if ( invElem != elem && !theElems.count( invElem )) continue;
4675 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4676 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4677 needMediumNodes = true;
4681 double coord[] = { node->X(), node->Y(), node->Z() };
4682 for ( int i = 0; i < nbsteps; i++ )
4684 if ( needMediumNodes ) // create a medium node
4686 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4687 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4688 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4689 if( theFlags & EXTRUSION_FLAG_SEW ) {
4690 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4691 theTolerance, theParams.myNodes);
4692 listNewNodes.push_back( newNode );
4695 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4696 myLastCreatedNodes.Append(newNode);
4697 srcNodes.Append( node );
4698 listNewNodes.push_back( newNode );
4701 // create a corner node
4702 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4703 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4704 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4705 if( theFlags & EXTRUSION_FLAG_SEW ) {
4706 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4707 theTolerance, theParams.myNodes);
4708 listNewNodes.push_back( newNode );
4711 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4712 myLastCreatedNodes.Append(newNode);
4713 srcNodes.Append( node );
4714 listNewNodes.push_back( newNode );
4718 newNodesItVec.push_back( nIt );
4720 // make new elements
4721 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4724 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4725 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4727 PGroupIDs newGroupIDs;
4728 if ( theMakeGroups )
4729 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4734 //=======================================================================
4735 //function : ExtrusionAlongTrack
4737 //=======================================================================
4738 SMESH_MeshEditor::Extrusion_Error
4739 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4740 SMESH_subMesh* theTrack,
4741 const SMDS_MeshNode* theN1,
4742 const bool theHasAngles,
4743 list<double>& theAngles,
4744 const bool theLinearVariation,
4745 const bool theHasRefPoint,
4746 const gp_Pnt& theRefPoint,
4747 const bool theMakeGroups)
4749 MESSAGE("ExtrusionAlongTrack");
4750 myLastCreatedElems.Clear();
4751 myLastCreatedNodes.Clear();
4754 std::list<double> aPrms;
4755 TIDSortedElemSet::iterator itElem;
4758 TopoDS_Edge aTrackEdge;
4759 TopoDS_Vertex aV1, aV2;
4761 SMDS_ElemIteratorPtr aItE;
4762 SMDS_NodeIteratorPtr aItN;
4763 SMDSAbs_ElementType aTypeE;
4765 TNodeOfNodeListMap mapNewNodes;
4768 aNbE = theElements.size();
4771 return EXTR_NO_ELEMENTS;
4773 // 1.1 Track Pattern
4776 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4778 aItE = pSubMeshDS->GetElements();
4779 while ( aItE->more() ) {
4780 const SMDS_MeshElement* pE = aItE->next();
4781 aTypeE = pE->GetType();
4782 // Pattern must contain links only
4783 if ( aTypeE != SMDSAbs_Edge )
4784 return EXTR_PATH_NOT_EDGE;
4787 list<SMESH_MeshEditor_PathPoint> fullList;
4789 const TopoDS_Shape& aS = theTrack->GetSubShape();
4790 // Sub-shape for the Pattern must be an Edge or Wire
4791 if( aS.ShapeType() == TopAbs_EDGE ) {
4792 aTrackEdge = TopoDS::Edge( aS );
4793 // the Edge must not be degenerated
4794 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4795 return EXTR_BAD_PATH_SHAPE;
4796 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4797 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4798 const SMDS_MeshNode* aN1 = aItN->next();
4799 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4800 const SMDS_MeshNode* aN2 = aItN->next();
4801 // starting node must be aN1 or aN2
4802 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4803 return EXTR_BAD_STARTING_NODE;
4804 aItN = pSubMeshDS->GetNodes();
4805 while ( aItN->more() ) {
4806 const SMDS_MeshNode* pNode = aItN->next();
4807 const SMDS_EdgePosition* pEPos =
4808 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4809 double aT = pEPos->GetUParameter();
4810 aPrms.push_back( aT );
4812 //Extrusion_Error err =
4813 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4814 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4815 list< SMESH_subMesh* > LSM;
4816 TopTools_SequenceOfShape Edges;
4817 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4818 while(itSM->more()) {
4819 SMESH_subMesh* SM = itSM->next();
4821 const TopoDS_Shape& aS = SM->GetSubShape();
4824 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4825 int startNid = theN1->GetID();
4826 TColStd_MapOfInteger UsedNums;
4828 int NbEdges = Edges.Length();
4830 for(; i<=NbEdges; i++) {
4832 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4833 for(; itLSM!=LSM.end(); itLSM++) {
4835 if(UsedNums.Contains(k)) continue;
4836 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4837 SMESH_subMesh* locTrack = *itLSM;
4838 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4839 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4840 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4841 const SMDS_MeshNode* aN1 = aItN->next();
4842 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4843 const SMDS_MeshNode* aN2 = aItN->next();
4844 // starting node must be aN1 or aN2
4845 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4846 // 2. Collect parameters on the track edge
4848 aItN = locMeshDS->GetNodes();
4849 while ( aItN->more() ) {
4850 const SMDS_MeshNode* pNode = aItN->next();
4851 const SMDS_EdgePosition* pEPos =
4852 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4853 double aT = pEPos->GetUParameter();
4854 aPrms.push_back( aT );
4856 list<SMESH_MeshEditor_PathPoint> LPP;
4857 //Extrusion_Error err =
4858 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4859 LLPPs.push_back(LPP);
4861 // update startN for search following egde
4862 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4863 else startNid = aN1->GetID();
4867 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4868 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4869 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4870 for(; itPP!=firstList.end(); itPP++) {
4871 fullList.push_back( *itPP );
4873 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4874 fullList.pop_back();
4876 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4877 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4878 itPP = currList.begin();
4879 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4880 gp_Dir D1 = PP1.Tangent();
4881 gp_Dir D2 = PP2.Tangent();
4882 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4883 (D1.Z()+D2.Z())/2 ) );
4884 PP1.SetTangent(Dnew);
4885 fullList.push_back(PP1);
4887 for(; itPP!=firstList.end(); itPP++) {
4888 fullList.push_back( *itPP );
4890 PP1 = fullList.back();
4891 fullList.pop_back();
4893 // if wire not closed
4894 fullList.push_back(PP1);
4898 return EXTR_BAD_PATH_SHAPE;
4901 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4902 theHasRefPoint, theRefPoint, theMakeGroups);
4906 //=======================================================================
4907 //function : ExtrusionAlongTrack
4909 //=======================================================================
4910 SMESH_MeshEditor::Extrusion_Error
4911 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4912 SMESH_Mesh* theTrack,
4913 const SMDS_MeshNode* theN1,
4914 const bool theHasAngles,
4915 list<double>& theAngles,
4916 const bool theLinearVariation,
4917 const bool theHasRefPoint,
4918 const gp_Pnt& theRefPoint,
4919 const bool theMakeGroups)
4921 myLastCreatedElems.Clear();
4922 myLastCreatedNodes.Clear();
4925 std::list<double> aPrms;
4926 TIDSortedElemSet::iterator itElem;
4929 TopoDS_Edge aTrackEdge;
4930 TopoDS_Vertex aV1, aV2;
4932 SMDS_ElemIteratorPtr aItE;
4933 SMDS_NodeIteratorPtr aItN;
4934 SMDSAbs_ElementType aTypeE;
4936 TNodeOfNodeListMap mapNewNodes;
4939 aNbE = theElements.size();
4942 return EXTR_NO_ELEMENTS;
4944 // 1.1 Track Pattern
4947 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4949 aItE = pMeshDS->elementsIterator();
4950 while ( aItE->more() ) {
4951 const SMDS_MeshElement* pE = aItE->next();
4952 aTypeE = pE->GetType();
4953 // Pattern must contain links only
4954 if ( aTypeE != SMDSAbs_Edge )
4955 return EXTR_PATH_NOT_EDGE;
4958 list<SMESH_MeshEditor_PathPoint> fullList;
4960 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4962 if( aS == SMESH_Mesh::PseudoShape() ) {
4963 //Mesh without shape
4964 const SMDS_MeshNode* currentNode = NULL;
4965 const SMDS_MeshNode* prevNode = theN1;
4966 std::vector<const SMDS_MeshNode*> aNodesList;
4967 aNodesList.push_back(theN1);
4968 int nbEdges = 0, conn=0;
4969 const SMDS_MeshElement* prevElem = NULL;
4970 const SMDS_MeshElement* currentElem = NULL;
4971 int totalNbEdges = theTrack->NbEdges();
4972 SMDS_ElemIteratorPtr nIt;
4975 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4976 return EXTR_BAD_STARTING_NODE;
4979 conn = nbEdgeConnectivity(theN1);
4981 return EXTR_PATH_NOT_EDGE;
4983 aItE = theN1->GetInverseElementIterator();
4984 prevElem = aItE->next();
4985 currentElem = prevElem;
4987 if(totalNbEdges == 1 ) {
4988 nIt = currentElem->nodesIterator();
4989 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4990 if(currentNode == prevNode)
4991 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4992 aNodesList.push_back(currentNode);
4994 nIt = currentElem->nodesIterator();
4995 while( nIt->more() ) {
4996 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4997 if(currentNode == prevNode)
4998 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4999 aNodesList.push_back(currentNode);
5001 //case of the closed mesh
5002 if(currentNode == theN1) {
5007 conn = nbEdgeConnectivity(currentNode);
5009 return EXTR_PATH_NOT_EDGE;
5010 }else if( conn == 1 && nbEdges > 0 ) {
5015 prevNode = currentNode;
5016 aItE = currentNode->GetInverseElementIterator();
5017 currentElem = aItE->next();
5018 if( currentElem == prevElem)
5019 currentElem = aItE->next();
5020 nIt = currentElem->nodesIterator();
5021 prevElem = currentElem;
5027 if(nbEdges != totalNbEdges)
5028 return EXTR_PATH_NOT_EDGE;
5030 TopTools_SequenceOfShape Edges;
5031 double x1,x2,y1,y2,z1,z2;
5032 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5033 int startNid = theN1->GetID();
5034 for(int i = 1; i < aNodesList.size(); i++) {
5035 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5036 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5037 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5038 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5039 list<SMESH_MeshEditor_PathPoint> LPP;
5041 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5042 LLPPs.push_back(LPP);
5043 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5044 else startNid = aNodesList[i-1]->GetID();
5048 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5049 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5050 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5051 for(; itPP!=firstList.end(); itPP++) {
5052 fullList.push_back( *itPP );
5055 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5056 SMESH_MeshEditor_PathPoint PP2;
5057 fullList.pop_back();
5059 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5060 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5061 itPP = currList.begin();
5062 PP2 = currList.front();
5063 gp_Dir D1 = PP1.Tangent();
5064 gp_Dir D2 = PP2.Tangent();
5065 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5066 (D1.Z()+D2.Z())/2 ) );
5067 PP1.SetTangent(Dnew);
5068 fullList.push_back(PP1);
5070 for(; itPP!=currList.end(); itPP++) {
5071 fullList.push_back( *itPP );
5073 PP1 = fullList.back();
5074 fullList.pop_back();
5076 fullList.push_back(PP1);
5078 } // Sub-shape for the Pattern must be an Edge or Wire
5079 else if( aS.ShapeType() == TopAbs_EDGE ) {
5080 aTrackEdge = TopoDS::Edge( aS );
5081 // the Edge must not be degenerated
5082 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5083 return EXTR_BAD_PATH_SHAPE;
5084 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5085 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5086 const SMDS_MeshNode* aN1 = aItN->next();
5087 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5088 const SMDS_MeshNode* aN2 = aItN->next();
5089 // starting node must be aN1 or aN2
5090 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5091 return EXTR_BAD_STARTING_NODE;
5092 aItN = pMeshDS->nodesIterator();
5093 while ( aItN->more() ) {
5094 const SMDS_MeshNode* pNode = aItN->next();
5095 if( pNode==aN1 || pNode==aN2 ) continue;
5096 const SMDS_EdgePosition* pEPos =
5097 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5098 double aT = pEPos->GetUParameter();
5099 aPrms.push_back( aT );
5101 //Extrusion_Error err =
5102 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5104 else if( aS.ShapeType() == TopAbs_WIRE ) {
5105 list< SMESH_subMesh* > LSM;
5106 TopTools_SequenceOfShape Edges;
5107 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5108 for(; eExp.More(); eExp.Next()) {
5109 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5110 if( BRep_Tool::Degenerated(E) ) continue;
5111 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5117 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5118 int startNid = theN1->GetID();
5119 TColStd_MapOfInteger UsedNums;
5120 int NbEdges = Edges.Length();
5122 for(; i<=NbEdges; i++) {
5124 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5125 for(; itLSM!=LSM.end(); itLSM++) {
5127 if(UsedNums.Contains(k)) continue;
5128 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5129 SMESH_subMesh* locTrack = *itLSM;
5130 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5131 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5132 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5133 const SMDS_MeshNode* aN1 = aItN->next();
5134 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5135 const SMDS_MeshNode* aN2 = aItN->next();
5136 // starting node must be aN1 or aN2
5137 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5138 // 2. Collect parameters on the track edge
5140 aItN = locMeshDS->GetNodes();
5141 while ( aItN->more() ) {
5142 const SMDS_MeshNode* pNode = aItN->next();
5143 const SMDS_EdgePosition* pEPos =
5144 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5145 double aT = pEPos->GetUParameter();
5146 aPrms.push_back( aT );
5148 list<SMESH_MeshEditor_PathPoint> LPP;
5149 //Extrusion_Error err =
5150 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5151 LLPPs.push_back(LPP);
5153 // update startN for search following egde
5154 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5155 else startNid = aN1->GetID();
5159 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5160 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5161 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5162 for(; itPP!=firstList.end(); itPP++) {
5163 fullList.push_back( *itPP );
5165 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5166 fullList.pop_back();
5168 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5169 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5170 itPP = currList.begin();
5171 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5172 gp_Dir D1 = PP1.Tangent();
5173 gp_Dir D2 = PP2.Tangent();
5174 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5175 (D1.Z()+D2.Z())/2 ) );
5176 PP1.SetTangent(Dnew);
5177 fullList.push_back(PP1);
5179 for(; itPP!=currList.end(); itPP++) {
5180 fullList.push_back( *itPP );
5182 PP1 = fullList.back();
5183 fullList.pop_back();
5185 // if wire not closed
5186 fullList.push_back(PP1);
5190 return EXTR_BAD_PATH_SHAPE;
5193 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5194 theHasRefPoint, theRefPoint, theMakeGroups);
5198 //=======================================================================
5199 //function : MakeEdgePathPoints
5200 //purpose : auxilary for ExtrusionAlongTrack
5201 //=======================================================================
5202 SMESH_MeshEditor::Extrusion_Error
5203 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5204 const TopoDS_Edge& aTrackEdge,
5206 list<SMESH_MeshEditor_PathPoint>& LPP)
5208 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5210 aTolVec2=aTolVec*aTolVec;
5212 TopoDS_Vertex aV1, aV2;
5213 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5214 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5215 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5216 // 2. Collect parameters on the track edge
5217 aPrms.push_front( aT1 );
5218 aPrms.push_back( aT2 );
5221 if( FirstIsStart ) {
5232 SMESH_MeshEditor_PathPoint aPP;
5233 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5234 std::list<double>::iterator aItD = aPrms.begin();
5235 for(; aItD != aPrms.end(); ++aItD) {
5239 aC3D->D1( aT, aP3D, aVec );
5240 aL2 = aVec.SquareMagnitude();
5241 if ( aL2 < aTolVec2 )
5242 return EXTR_CANT_GET_TANGENT;
5243 gp_Dir aTgt( aVec );
5245 aPP.SetTangent( aTgt );
5246 aPP.SetParameter( aT );
5253 //=======================================================================
5254 //function : MakeExtrElements
5255 //purpose : auxilary for ExtrusionAlongTrack
5256 //=======================================================================
5257 SMESH_MeshEditor::Extrusion_Error
5258 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5259 list<SMESH_MeshEditor_PathPoint>& fullList,
5260 const bool theHasAngles,
5261 list<double>& theAngles,
5262 const bool theLinearVariation,
5263 const bool theHasRefPoint,
5264 const gp_Pnt& theRefPoint,
5265 const bool theMakeGroups)
5267 MESSAGE("MakeExtrElements");
5268 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5269 int aNbTP = fullList.size();
5270 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5272 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5273 LinearAngleVariation(aNbTP-1, theAngles);
5275 vector<double> aAngles( aNbTP );
5277 for(; j<aNbTP; ++j) {
5280 if ( theHasAngles ) {
5282 std::list<double>::iterator aItD = theAngles.begin();
5283 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5285 aAngles[j] = anAngle;
5288 // fill vector of path points with angles
5289 //aPPs.resize(fullList.size());
5291 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5292 for(; itPP!=fullList.end(); itPP++) {
5294 SMESH_MeshEditor_PathPoint PP = *itPP;
5295 PP.SetAngle(aAngles[j]);
5299 TNodeOfNodeListMap mapNewNodes;
5300 TElemOfVecOfNnlmiMap mapElemNewNodes;
5301 TElemOfElemListMap newElemsMap;
5302 TIDSortedElemSet::iterator itElem;
5305 SMDSAbs_ElementType aTypeE;
5306 // source elements for each generated one
5307 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5309 // 3. Center of rotation aV0
5310 gp_Pnt aV0 = theRefPoint;
5312 if ( !theHasRefPoint ) {
5314 aGC.SetCoord( 0.,0.,0. );
5316 itElem = theElements.begin();
5317 for ( ; itElem != theElements.end(); itElem++ ) {
5318 const SMDS_MeshElement* elem = *itElem;
5320 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5321 while ( itN->more() ) {
5322 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5327 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5328 list<const SMDS_MeshNode*> aLNx;
5329 mapNewNodes[node] = aLNx;
5331 gp_XYZ aXYZ( aX, aY, aZ );
5339 } // if (!theHasRefPoint) {
5340 mapNewNodes.clear();
5342 // 4. Processing the elements
5343 SMESHDS_Mesh* aMesh = GetMeshDS();
5345 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5346 // check element type
5347 const SMDS_MeshElement* elem = *itElem;
5348 aTypeE = elem->GetType();
5349 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5352 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5353 newNodesItVec.reserve( elem->NbNodes() );
5355 // loop on elem nodes
5357 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5358 while ( itN->more() )
5361 // check if a node has been already processed
5362 const SMDS_MeshNode* node =
5363 static_cast<const SMDS_MeshNode*>( itN->next() );
5364 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5365 if ( nIt == mapNewNodes.end() ) {
5366 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5367 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5370 aX = node->X(); aY = node->Y(); aZ = node->Z();
5372 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5373 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5374 gp_Ax1 anAx1, anAxT1T0;
5375 gp_Dir aDT1x, aDT0x, aDT1T0;
5380 aPN0.SetCoord(aX, aY, aZ);
5382 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5384 aDT0x= aPP0.Tangent();
5385 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5387 for ( j = 1; j < aNbTP; ++j ) {
5388 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5390 aDT1x = aPP1.Tangent();
5391 aAngle1x = aPP1.Angle();
5393 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5395 gp_Vec aV01x( aP0x, aP1x );
5396 aTrsf.SetTranslation( aV01x );
5399 aV1x = aV0x.Transformed( aTrsf );
5400 aPN1 = aPN0.Transformed( aTrsf );
5402 // rotation 1 [ T1,T0 ]
5403 aAngleT1T0=-aDT1x.Angle( aDT0x );
5404 if (fabs(aAngleT1T0) > aTolAng) {
5406 anAxT1T0.SetLocation( aV1x );
5407 anAxT1T0.SetDirection( aDT1T0 );
5408 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5410 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5414 if ( theHasAngles ) {
5415 anAx1.SetLocation( aV1x );
5416 anAx1.SetDirection( aDT1x );
5417 aTrsfRot.SetRotation( anAx1, aAngle1x );
5419 aPN1 = aPN1.Transformed( aTrsfRot );
5423 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5424 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5425 // create additional node
5426 double x = ( aPN1.X() + aPN0.X() )/2.;
5427 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5428 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5429 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5430 myLastCreatedNodes.Append(newNode);
5431 srcNodes.Append( node );
5432 listNewNodes.push_back( newNode );
5437 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5438 myLastCreatedNodes.Append(newNode);
5439 srcNodes.Append( node );
5440 listNewNodes.push_back( newNode );
5450 // if current elem is quadratic and current node is not medium
5451 // we have to check - may be it is needed to insert additional nodes
5452 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5453 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5454 if(listNewNodes.size()==aNbTP-1) {
5455 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5456 gp_XYZ P(node->X(), node->Y(), node->Z());
5457 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5459 for(i=0; i<aNbTP-1; i++) {
5460 const SMDS_MeshNode* N = *it;
5461 double x = ( N->X() + P.X() )/2.;
5462 double y = ( N->Y() + P.Y() )/2.;
5463 double z = ( N->Z() + P.Z() )/2.;
5464 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5465 srcNodes.Append( node );
5466 myLastCreatedNodes.Append(newN);
5469 P = gp_XYZ(N->X(),N->Y(),N->Z());
5471 listNewNodes.clear();
5472 for(i=0; i<2*(aNbTP-1); i++) {
5473 listNewNodes.push_back(aNodes[i]);
5479 newNodesItVec.push_back( nIt );
5481 // make new elements
5482 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5483 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5484 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5487 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5489 if ( theMakeGroups )
5490 generateGroups( srcNodes, srcElems, "extruded");
5496 //=======================================================================
5497 //function : LinearAngleVariation
5498 //purpose : auxilary for ExtrusionAlongTrack
5499 //=======================================================================
5500 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5501 list<double>& Angles)
5503 int nbAngles = Angles.size();
5504 if( nbSteps > nbAngles ) {
5505 vector<double> theAngles(nbAngles);
5506 list<double>::iterator it = Angles.begin();
5508 for(; it!=Angles.end(); it++) {
5510 theAngles[i] = (*it);
5513 double rAn2St = double( nbAngles ) / double( nbSteps );
5514 double angPrev = 0, angle;
5515 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5516 double angCur = rAn2St * ( iSt+1 );
5517 double angCurFloor = floor( angCur );
5518 double angPrevFloor = floor( angPrev );
5519 if ( angPrevFloor == angCurFloor )
5520 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5522 int iP = int( angPrevFloor );
5523 double angPrevCeil = ceil(angPrev);
5524 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5526 int iC = int( angCurFloor );
5527 if ( iC < nbAngles )
5528 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5530 iP = int( angPrevCeil );
5532 angle += theAngles[ iC ];
5534 res.push_back(angle);
5539 for(; it!=res.end(); it++)
5540 Angles.push_back( *it );
5545 //================================================================================
5547 * \brief Move or copy theElements applying theTrsf to their nodes
5548 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5549 * \param theTrsf - transformation to apply
5550 * \param theCopy - if true, create translated copies of theElems
5551 * \param theMakeGroups - if true and theCopy, create translated groups
5552 * \param theTargetMesh - mesh to copy translated elements into
5553 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5555 //================================================================================
5557 SMESH_MeshEditor::PGroupIDs
5558 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5559 const gp_Trsf& theTrsf,
5561 const bool theMakeGroups,
5562 SMESH_Mesh* theTargetMesh)
5564 myLastCreatedElems.Clear();
5565 myLastCreatedNodes.Clear();
5567 bool needReverse = false;
5568 string groupPostfix;
5569 switch ( theTrsf.Form() ) {
5571 MESSAGE("gp_PntMirror");
5573 groupPostfix = "mirrored";
5576 MESSAGE("gp_Ax1Mirror");
5577 groupPostfix = "mirrored";
5580 MESSAGE("gp_Ax2Mirror");
5582 groupPostfix = "mirrored";
5585 MESSAGE("gp_Rotation");
5586 groupPostfix = "rotated";
5588 case gp_Translation:
5589 MESSAGE("gp_Translation");
5590 groupPostfix = "translated";
5593 MESSAGE("gp_Scale");
5594 groupPostfix = "scaled";
5596 case gp_CompoundTrsf: // different scale by axis
5597 MESSAGE("gp_CompoundTrsf");
5598 groupPostfix = "scaled";
5602 needReverse = false;
5603 groupPostfix = "transformed";
5606 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5607 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5608 SMESHDS_Mesh* aMesh = GetMeshDS();
5611 // map old node to new one
5612 TNodeNodeMap nodeMap;
5614 // elements sharing moved nodes; those of them which have all
5615 // nodes mirrored but are not in theElems are to be reversed
5616 TIDSortedElemSet inverseElemSet;
5618 // source elements for each generated one
5619 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5621 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5622 TIDSortedElemSet orphanNode;
5624 if ( theElems.empty() ) // transform the whole mesh
5627 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5628 while ( eIt->more() ) theElems.insert( eIt->next() );
5630 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5631 while ( nIt->more() )
5633 const SMDS_MeshNode* node = nIt->next();
5634 if ( node->NbInverseElements() == 0)
5635 orphanNode.insert( node );
5639 // loop on elements to transform nodes : first orphan nodes then elems
5640 TIDSortedElemSet::iterator itElem;
5641 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5642 for (int i=0; i<2; i++)
5643 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5644 const SMDS_MeshElement* elem = *itElem;
5648 // loop on elem nodes
5649 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5650 while ( itN->more() ) {
5652 const SMDS_MeshNode* node = cast2Node( itN->next() );
5653 // check if a node has been already transformed
5654 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5655 nodeMap.insert( make_pair ( node, node ));
5656 if ( !n2n_isnew.second )
5660 coord[0] = node->X();
5661 coord[1] = node->Y();
5662 coord[2] = node->Z();
5663 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5664 if ( theTargetMesh ) {
5665 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5666 n2n_isnew.first->second = newNode;
5667 myLastCreatedNodes.Append(newNode);
5668 srcNodes.Append( node );
5670 else if ( theCopy ) {
5671 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5672 n2n_isnew.first->second = newNode;
5673 myLastCreatedNodes.Append(newNode);
5674 srcNodes.Append( node );
5677 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5678 // node position on shape becomes invalid
5679 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5680 ( SMDS_SpacePosition::originSpacePosition() );
5683 // keep inverse elements
5684 if ( !theCopy && !theTargetMesh && needReverse ) {
5685 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5686 while ( invElemIt->more() ) {
5687 const SMDS_MeshElement* iel = invElemIt->next();
5688 inverseElemSet.insert( iel );
5694 // either create new elements or reverse mirrored ones
5695 if ( !theCopy && !needReverse && !theTargetMesh )
5698 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5699 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5700 theElems.insert( *invElemIt );
5702 // Replicate or reverse elements
5704 std::vector<int> iForw;
5705 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5707 const SMDS_MeshElement* elem = *itElem;
5708 if ( !elem ) continue;
5710 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5711 int nbNodes = elem->NbNodes();
5712 if ( geomType == SMDSGeom_POINT ) continue; // node
5714 switch ( geomType ) {
5716 case SMDSGeom_POLYGON: // ---------------------- polygon
5718 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5720 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5721 while (itN->more()) {
5722 const SMDS_MeshNode* node =
5723 static_cast<const SMDS_MeshNode*>(itN->next());
5724 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5725 if (nodeMapIt == nodeMap.end())
5726 break; // not all nodes transformed
5728 // reverse mirrored faces and volumes
5729 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5731 poly_nodes[iNode] = (*nodeMapIt).second;
5735 if ( iNode != nbNodes )
5736 continue; // not all nodes transformed
5738 if ( theTargetMesh ) {
5739 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5740 srcElems.Append( elem );
5742 else if ( theCopy ) {
5743 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5744 srcElems.Append( elem );
5747 aMesh->ChangePolygonNodes(elem, poly_nodes);
5752 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5754 const SMDS_VtkVolume* aPolyedre =
5755 dynamic_cast<const SMDS_VtkVolume*>( elem );
5757 MESSAGE("Warning: bad volumic element");
5761 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5762 vector<int> quantities; quantities.reserve( nbNodes );
5764 bool allTransformed = true;
5765 int nbFaces = aPolyedre->NbFaces();
5766 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5767 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5768 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5769 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5770 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5771 if (nodeMapIt == nodeMap.end()) {
5772 allTransformed = false; // not all nodes transformed
5774 poly_nodes.push_back((*nodeMapIt).second);
5776 if ( needReverse && allTransformed )
5777 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5779 quantities.push_back(nbFaceNodes);
5781 if ( !allTransformed )
5782 continue; // not all nodes transformed
5784 if ( theTargetMesh ) {
5785 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5786 srcElems.Append( elem );
5788 else if ( theCopy ) {
5789 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5790 srcElems.Append( elem );
5793 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5798 case SMDSGeom_BALL: // -------------------- Ball
5800 if ( !theCopy && !theTargetMesh ) continue;
5802 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5803 if (nodeMapIt == nodeMap.end())
5804 continue; // not all nodes transformed
5806 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5807 if ( theTargetMesh ) {
5808 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5809 srcElems.Append( elem );
5812 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5813 srcElems.Append( elem );
5818 default: // ----------------------- Regular elements
5820 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5821 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5822 const std::vector<int>& i = needReverse ? iRev : iForw;
5824 // find transformed nodes
5825 vector<const SMDS_MeshNode*> nodes(nbNodes);
5827 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5828 while ( itN->more() ) {
5829 const SMDS_MeshNode* node =
5830 static_cast<const SMDS_MeshNode*>( itN->next() );
5831 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5832 if ( nodeMapIt == nodeMap.end() )
5833 break; // not all nodes transformed
5834 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5836 if ( iNode != nbNodes )
5837 continue; // not all nodes transformed
5839 if ( theTargetMesh ) {
5840 if ( SMDS_MeshElement* copy =
5841 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5842 myLastCreatedElems.Append( copy );
5843 srcElems.Append( elem );
5846 else if ( theCopy ) {
5847 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5848 srcElems.Append( elem );
5851 // reverse element as it was reversed by transformation
5853 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5855 } // switch ( geomType )
5857 } // loop on elements
5859 PGroupIDs newGroupIDs;
5861 if ( ( theMakeGroups && theCopy ) ||
5862 ( theMakeGroups && theTargetMesh ) )
5863 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5868 //=======================================================================
5870 * \brief Create groups of elements made during transformation
5871 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5872 * \param elemGens - elements making corresponding myLastCreatedElems
5873 * \param postfix - to append to names of new groups
5875 //=======================================================================
5877 SMESH_MeshEditor::PGroupIDs
5878 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5879 const SMESH_SequenceOfElemPtr& elemGens,
5880 const std::string& postfix,
5881 SMESH_Mesh* targetMesh)
5883 PGroupIDs newGroupIDs( new list<int> );
5884 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5886 // Sort existing groups by types and collect their names
5888 // to store an old group and a generated new one
5889 typedef pair< SMESHDS_GroupBase*, SMESHDS_Group* > TOldNewGroup;
5890 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5891 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
5893 set< string > groupNames;
5895 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5896 if ( !groupIt->more() ) return newGroupIDs;
5898 int newGroupID = mesh->GetGroupIds().back()+1;
5899 while ( groupIt->more() )
5901 SMESH_Group * group = groupIt->next();
5902 if ( !group ) continue;
5903 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5904 if ( !groupDS || groupDS->IsEmpty() ) continue;
5905 groupNames.insert( group->GetName() );
5906 groupDS->SetStoreName( group->GetName() );
5907 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(),
5908 groupDS->GetType() );
5909 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, newGroup ));
5910 orderedOldNewGroups.push_back( & groupsByType[ groupDS->GetType() ].back() );
5913 // Loop on nodes and elements to add them in new groups
5915 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5917 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5918 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5919 if ( gens.Length() != elems.Length() )
5920 throw SALOME_Exception(LOCALIZED("invalid args"));
5922 // loop on created elements
5923 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5925 const SMDS_MeshElement* sourceElem = gens( iElem );
5926 if ( !sourceElem ) {
5927 MESSAGE("generateGroups(): NULL source element");
5930 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5931 if ( groupsOldNew.empty() ) { // no groups of this type at all
5932 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5933 ++iElem; // skip all elements made by sourceElem
5936 // collect all elements made by sourceElem
5937 list< const SMDS_MeshElement* > resultElems;
5938 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5939 if ( resElem != sourceElem )
5940 resultElems.push_back( resElem );
5941 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5942 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5943 if ( resElem != sourceElem )
5944 resultElems.push_back( resElem );
5946 // add resultElems to groups made by ones the sourceElem belongs to
5947 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5948 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5950 SMESHDS_GroupBase* oldGroup = gOldNew->first;
5951 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
5953 // fill in a new group
5954 SMDS_MeshGroup & newGroup = gOldNew->second->SMDSGroup();
5955 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5956 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5957 newGroup.Add( *resElemIt );
5960 } // loop on created elements
5961 }// loop on nodes and elements
5963 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
5965 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
5967 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->first;
5968 SMESHDS_Group* newGroupDS = orderedOldNewGroups[i]->second;
5969 if ( newGroupDS->IsEmpty() )
5971 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
5976 string name = oldGroupDS->GetStoreName();
5977 if ( !targetMesh ) {
5981 while ( !groupNames.insert( name ).second ) // name exists
5982 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << postfix << "_" << nb++;
5984 newGroupDS->SetStoreName( name.c_str() );
5986 // make a SMESH_Groups
5987 mesh->AddGroup( newGroupDS );
5988 newGroupIDs->push_back( newGroupDS->GetID() );
5995 //================================================================================
5997 * \brief Return list of group of nodes close to each other within theTolerance
5998 * Search among theNodes or in the whole mesh if theNodes is empty using
5999 * an Octree algorithm
6001 //================================================================================
6003 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6004 const double theTolerance,
6005 TListOfListOfNodes & theGroupsOfNodes)
6007 myLastCreatedElems.Clear();
6008 myLastCreatedNodes.Clear();
6010 if ( theNodes.empty() )
6011 { // get all nodes in the mesh
6012 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6013 while ( nIt->more() )
6014 theNodes.insert( theNodes.end(),nIt->next());
6017 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6021 //=======================================================================
6023 * \brief Implementation of search for the node closest to point
6025 //=======================================================================
6027 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6029 //---------------------------------------------------------------------
6031 * \brief Constructor
6033 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6035 myMesh = ( SMESHDS_Mesh* ) theMesh;
6037 TIDSortedNodeSet nodes;
6039 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6040 while ( nIt->more() )
6041 nodes.insert( nodes.end(), nIt->next() );
6043 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6045 // get max size of a leaf box
6046 SMESH_OctreeNode* tree = myOctreeNode;
6047 while ( !tree->isLeaf() )
6049 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6053 myHalfLeafSize = tree->maxSize() / 2.;
6056 //---------------------------------------------------------------------
6058 * \brief Move node and update myOctreeNode accordingly
6060 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6062 myOctreeNode->UpdateByMoveNode( node, toPnt );
6063 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6066 //---------------------------------------------------------------------
6068 * \brief Do it's job
6070 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6072 map<double, const SMDS_MeshNode*> dist2Nodes;
6073 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6074 if ( !dist2Nodes.empty() )
6075 return dist2Nodes.begin()->second;
6076 list<const SMDS_MeshNode*> nodes;
6077 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6079 double minSqDist = DBL_MAX;
6080 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6082 // sort leafs by their distance from thePnt
6083 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6084 TDistTreeMap treeMap;
6085 list< SMESH_OctreeNode* > treeList;
6086 list< SMESH_OctreeNode* >::iterator trIt;
6087 treeList.push_back( myOctreeNode );
6089 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6090 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6091 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6093 SMESH_OctreeNode* tree = *trIt;
6094 if ( !tree->isLeaf() ) // put children to the queue
6096 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6097 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6098 while ( cIt->more() )
6099 treeList.push_back( cIt->next() );
6101 else if ( tree->NbNodes() ) // put a tree to the treeMap
6103 const Bnd_B3d& box = tree->getBox();
6104 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6105 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6106 if ( !it_in.second ) // not unique distance to box center
6107 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6110 // find distance after which there is no sense to check tree's
6111 double sqLimit = DBL_MAX;
6112 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6113 if ( treeMap.size() > 5 ) {
6114 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6115 const Bnd_B3d& box = closestTree->getBox();
6116 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6117 sqLimit = limit * limit;
6119 // get all nodes from trees
6120 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6121 if ( sqDist_tree->first > sqLimit )
6123 SMESH_OctreeNode* tree = sqDist_tree->second;
6124 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6127 // find closest among nodes
6128 minSqDist = DBL_MAX;
6129 const SMDS_MeshNode* closestNode = 0;
6130 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6131 for ( ; nIt != nodes.end(); ++nIt ) {
6132 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6133 if ( minSqDist > sqDist ) {
6141 //---------------------------------------------------------------------
6145 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6147 //---------------------------------------------------------------------
6149 * \brief Return the node tree
6151 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6154 SMESH_OctreeNode* myOctreeNode;
6155 SMESHDS_Mesh* myMesh;
6156 double myHalfLeafSize; // max size of a leaf box
6159 //=======================================================================
6161 * \brief Return SMESH_NodeSearcher
6163 //=======================================================================
6165 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6167 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6170 // ========================================================================
6171 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6173 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6174 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6175 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6177 //=======================================================================
6179 * \brief Octal tree of bounding boxes of elements
6181 //=======================================================================
6183 class ElementBndBoxTree : public SMESH_Octree
6187 ElementBndBoxTree(const SMDS_Mesh& mesh,
6188 SMDSAbs_ElementType elemType,
6189 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6190 double tolerance = NodeRadius );
6191 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6192 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6193 void getElementsInSphere ( const gp_XYZ& center,
6194 const double radius, TIDSortedElemSet& foundElems);
6195 size_t getSize() { return std::max( _size, _elements.size() ); }
6196 ~ElementBndBoxTree();
6199 ElementBndBoxTree():_size(0) {}
6200 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6201 void buildChildrenData();
6202 Bnd_B3d* buildRootBox();
6204 //!< Bounding box of element
6205 struct ElementBox : public Bnd_B3d
6207 const SMDS_MeshElement* _element;
6208 int _refCount; // an ElementBox can be included in several tree branches
6209 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6211 vector< ElementBox* > _elements;
6215 //================================================================================
6217 * \brief ElementBndBoxTree creation
6219 //================================================================================
6221 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6222 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6224 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6225 _elements.reserve( nbElems );
6227 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6228 while ( elemIt->more() )
6229 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6234 //================================================================================
6238 //================================================================================
6240 ElementBndBoxTree::~ElementBndBoxTree()
6242 for ( int i = 0; i < _elements.size(); ++i )
6243 if ( --_elements[i]->_refCount <= 0 )
6244 delete _elements[i];
6247 //================================================================================
6249 * \brief Return the maximal box
6251 //================================================================================
6253 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6255 Bnd_B3d* box = new Bnd_B3d;
6256 for ( int i = 0; i < _elements.size(); ++i )
6257 box->Add( *_elements[i] );
6261 //================================================================================
6263 * \brief Redistrubute element boxes among children
6265 //================================================================================
6267 void ElementBndBoxTree::buildChildrenData()
6269 for ( int i = 0; i < _elements.size(); ++i )
6271 for (int j = 0; j < 8; j++)
6273 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6275 _elements[i]->_refCount++;
6276 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6279 _elements[i]->_refCount--;
6281 _size = _elements.size();
6282 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6284 for (int j = 0; j < 8; j++)
6286 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6287 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6288 child->myIsLeaf = true;
6290 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6291 SMESHUtils::CompactVector( child->_elements );
6295 //================================================================================
6297 * \brief Return elements which can include the point
6299 //================================================================================
6301 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6302 TIDSortedElemSet& foundElems)
6304 if ( getBox().IsOut( point.XYZ() ))
6309 for ( int i = 0; i < _elements.size(); ++i )
6310 if ( !_elements[i]->IsOut( point.XYZ() ))
6311 foundElems.insert( _elements[i]->_element );
6315 for (int i = 0; i < 8; i++)
6316 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6320 //================================================================================
6322 * \brief Return elements which can be intersected by the line
6324 //================================================================================
6326 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6327 TIDSortedElemSet& foundElems)
6329 if ( getBox().IsOut( line ))
6334 for ( int i = 0; i < _elements.size(); ++i )
6335 if ( !_elements[i]->IsOut( line ))
6336 foundElems.insert( _elements[i]->_element );
6340 for (int i = 0; i < 8; i++)
6341 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6345 //================================================================================
6347 * \brief Return elements from leaves intersecting the sphere
6349 //================================================================================
6351 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6352 const double radius,
6353 TIDSortedElemSet& foundElems)
6355 if ( getBox().IsOut( center, radius ))
6360 for ( int i = 0; i < _elements.size(); ++i )
6361 if ( !_elements[i]->IsOut( center, radius ))
6362 foundElems.insert( _elements[i]->_element );
6366 for (int i = 0; i < 8; i++)
6367 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6371 //================================================================================
6373 * \brief Construct the element box
6375 //================================================================================
6377 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6381 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6382 while ( nIt->more() )
6383 Add( SMESH_TNodeXYZ( nIt->next() ));
6384 Enlarge( tolerance );
6389 //=======================================================================
6391 * \brief Implementation of search for the elements by point and
6392 * of classification of point in 2D mesh
6394 //=======================================================================
6396 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6398 SMESHDS_Mesh* _mesh;
6399 SMDS_ElemIteratorPtr _meshPartIt;
6400 ElementBndBoxTree* _ebbTree;
6401 SMESH_NodeSearcherImpl* _nodeSearcher;
6402 SMDSAbs_ElementType _elementType;
6404 bool _outerFacesFound;
6405 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6407 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6408 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6409 ~SMESH_ElementSearcherImpl()
6411 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6412 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6414 virtual int FindElementsByPoint(const gp_Pnt& point,
6415 SMDSAbs_ElementType type,
6416 vector< const SMDS_MeshElement* >& foundElements);
6417 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6418 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6419 SMDSAbs_ElementType type );
6421 void GetElementsNearLine( const gp_Ax1& line,
6422 SMDSAbs_ElementType type,
6423 vector< const SMDS_MeshElement* >& foundElems);
6424 double getTolerance();
6425 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6426 const double tolerance, double & param);
6427 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6428 bool isOuterBoundary(const SMDS_MeshElement* face) const
6430 return _outerFaces.empty() || _outerFaces.count(face);
6432 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6434 const SMDS_MeshElement* _face;
6436 bool _coincides; //!< the line lays in face plane
6437 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6438 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6440 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6443 TIDSortedElemSet _faces;
6444 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6445 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6449 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6451 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6452 << ", _coincides="<<i._coincides << ")";
6455 //=======================================================================
6457 * \brief define tolerance for search
6459 //=======================================================================
6461 double SMESH_ElementSearcherImpl::getTolerance()
6463 if ( _tolerance < 0 )
6465 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6468 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6470 double boxSize = _nodeSearcher->getTree()->maxSize();
6471 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6473 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6475 double boxSize = _ebbTree->maxSize();
6476 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6478 if ( _tolerance == 0 )
6480 // define tolerance by size of a most complex element
6481 int complexType = SMDSAbs_Volume;
6482 while ( complexType > SMDSAbs_All &&
6483 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6485 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6487 if ( complexType == int( SMDSAbs_Node ))
6489 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6491 if ( meshInfo.NbNodes() > 2 )
6492 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6496 SMDS_ElemIteratorPtr elemIt =
6497 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6498 const SMDS_MeshElement* elem = elemIt->next();
6499 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6500 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6502 while ( nodeIt->more() )
6504 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6505 elemSize = max( dist, elemSize );
6508 _tolerance = 1e-4 * elemSize;
6514 //================================================================================
6516 * \brief Find intersection of the line and an edge of face and return parameter on line
6518 //================================================================================
6520 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6521 const SMDS_MeshElement* face,
6528 GeomAPI_ExtremaCurveCurve anExtCC;
6529 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6531 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6532 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6534 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6535 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6536 anExtCC.Init( lineCurve, edge);
6537 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6539 Quantity_Parameter pl, pe;
6540 anExtCC.LowerDistanceParameters( pl, pe );
6542 if ( ++nbInts == 2 )
6546 if ( nbInts > 0 ) param /= nbInts;
6549 //================================================================================
6551 * \brief Find all faces belonging to the outer boundary of mesh
6553 //================================================================================
6555 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6557 if ( _outerFacesFound ) return;
6559 // Collect all outer faces by passing from one outer face to another via their links
6560 // and BTW find out if there are internal faces at all.
6562 // checked links and links where outer boundary meets internal one
6563 set< SMESH_TLink > visitedLinks, seamLinks;
6565 // links to treat with already visited faces sharing them
6566 list < TFaceLink > startLinks;
6568 // load startLinks with the first outerFace
6569 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6570 _outerFaces.insert( outerFace );
6572 TIDSortedElemSet emptySet;
6573 while ( !startLinks.empty() )
6575 const SMESH_TLink& link = startLinks.front()._link;
6576 TIDSortedElemSet& faces = startLinks.front()._faces;
6578 outerFace = *faces.begin();
6579 // find other faces sharing the link
6580 const SMDS_MeshElement* f;
6581 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6584 // select another outer face among the found
6585 const SMDS_MeshElement* outerFace2 = 0;
6586 if ( faces.size() == 2 )
6588 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6590 else if ( faces.size() > 2 )
6592 seamLinks.insert( link );
6594 // link direction within the outerFace
6595 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6596 SMESH_TNodeXYZ( link.node2()));
6597 int i1 = outerFace->GetNodeIndex( link.node1() );
6598 int i2 = outerFace->GetNodeIndex( link.node2() );
6599 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6600 if ( rev ) n1n2.Reverse();
6602 gp_XYZ ofNorm, fNorm;
6603 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6605 // direction from the link inside outerFace
6606 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6607 // sort all other faces by angle with the dirInOF
6608 map< double, const SMDS_MeshElement* > angle2Face;
6609 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6610 for ( ; face != faces.end(); ++face )
6612 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6614 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6615 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6616 if ( angle < 0 ) angle += 2. * M_PI;
6617 angle2Face.insert( make_pair( angle, *face ));
6619 if ( !angle2Face.empty() )
6620 outerFace2 = angle2Face.begin()->second;
6623 // store the found outer face and add its links to continue seaching from
6626 _outerFaces.insert( outerFace );
6627 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6628 for ( int i = 0; i < nbNodes; ++i )
6630 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6631 if ( visitedLinks.insert( link2 ).second )
6632 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6635 startLinks.pop_front();
6637 _outerFacesFound = true;
6639 if ( !seamLinks.empty() )
6641 // There are internal boundaries touching the outher one,
6642 // find all faces of internal boundaries in order to find
6643 // faces of boundaries of holes, if any.
6648 _outerFaces.clear();
6652 //=======================================================================
6654 * \brief Find elements of given type where the given point is IN or ON.
6655 * Returns nb of found elements and elements them-selves.
6657 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6659 //=======================================================================
6661 int SMESH_ElementSearcherImpl::
6662 FindElementsByPoint(const gp_Pnt& point,
6663 SMDSAbs_ElementType type,
6664 vector< const SMDS_MeshElement* >& foundElements)
6666 foundElements.clear();
6668 double tolerance = getTolerance();
6670 // =================================================================================
6671 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6673 if ( !_nodeSearcher )
6674 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6676 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6677 if ( !closeNode ) return foundElements.size();
6679 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6680 return foundElements.size(); // to far from any node
6682 if ( type == SMDSAbs_Node )
6684 foundElements.push_back( closeNode );
6688 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6689 while ( elemIt->more() )
6690 foundElements.push_back( elemIt->next() );
6693 // =================================================================================
6694 else // elements more complex than 0D
6696 if ( !_ebbTree || _elementType != type )
6698 if ( _ebbTree ) delete _ebbTree;
6699 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6701 TIDSortedElemSet suspectElems;
6702 _ebbTree->getElementsNearPoint( point, suspectElems );
6703 TIDSortedElemSet::iterator elem = suspectElems.begin();
6704 for ( ; elem != suspectElems.end(); ++elem )
6705 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6706 foundElements.push_back( *elem );
6708 return foundElements.size();
6711 //=======================================================================
6713 * \brief Find an element of given type most close to the given point
6715 * WARNING: Only face search is implemeneted so far
6717 //=======================================================================
6719 const SMDS_MeshElement*
6720 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6721 SMDSAbs_ElementType type )
6723 const SMDS_MeshElement* closestElem = 0;
6725 if ( type == SMDSAbs_Face )
6727 if ( !_ebbTree || _elementType != type )
6729 if ( _ebbTree ) delete _ebbTree;
6730 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6732 TIDSortedElemSet suspectElems;
6733 _ebbTree->getElementsNearPoint( point, suspectElems );
6735 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6737 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox().CornerMin() +
6738 _ebbTree->getBox().CornerMax() );
6740 if ( _ebbTree->getBox().IsOut( point.XYZ() ))
6741 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6743 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6744 while ( suspectElems.empty() )
6746 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6750 double minDist = std::numeric_limits<double>::max();
6751 multimap< double, const SMDS_MeshElement* > dist2face;
6752 TIDSortedElemSet::iterator elem = suspectElems.begin();
6753 for ( ; elem != suspectElems.end(); ++elem )
6755 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6757 if ( dist < minDist + 1e-10)
6760 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6763 if ( !dist2face.empty() )
6765 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6766 closestElem = d2f->second;
6767 // if there are several elements at the same distance, select one
6768 // with GC closest to the point
6769 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6770 double minDistToGC = 0;
6771 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6773 if ( minDistToGC == 0 )
6776 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6777 TXyzIterator(), gc ) / closestElem->NbNodes();
6778 minDistToGC = point.SquareDistance( gc );
6781 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6782 TXyzIterator(), gc ) / d2f->second->NbNodes();
6783 double d = point.SquareDistance( gc );
6784 if ( d < minDistToGC )
6787 closestElem = d2f->second;
6790 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6791 // <<closestElem->GetID() << " DIST " << minDist << endl;
6796 // NOT IMPLEMENTED SO FAR
6802 //================================================================================
6804 * \brief Classify the given point in the closed 2D mesh
6806 //================================================================================
6808 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6810 double tolerance = getTolerance();
6811 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6813 if ( _ebbTree ) delete _ebbTree;
6814 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6816 // Algo: analyse transition of a line starting at the point through mesh boundary;
6817 // try three lines parallel to axis of the coordinate system and perform rough
6818 // analysis. If solution is not clear perform thorough analysis.
6820 const int nbAxes = 3;
6821 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6822 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6823 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6824 multimap< int, int > nbInt2Axis; // to find the simplest case
6825 for ( int axis = 0; axis < nbAxes; ++axis )
6827 gp_Ax1 lineAxis( point, axisDir[axis]);
6828 gp_Lin line ( lineAxis );
6830 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6831 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6833 // Intersect faces with the line
6835 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6836 TIDSortedElemSet::iterator face = suspectFaces.begin();
6837 for ( ; face != suspectFaces.end(); ++face )
6841 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6842 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6844 // perform intersection
6845 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6846 if ( !intersection.IsDone() )
6848 if ( intersection.IsInQuadric() )
6850 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6852 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6854 gp_Pnt intersectionPoint = intersection.Point(1);
6855 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6856 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6859 // Analyse intersections roughly
6861 int nbInter = u2inters.size();
6865 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6866 if ( nbInter == 1 ) // not closed mesh
6867 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6869 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6872 if ( (f<0) == (l<0) )
6875 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6876 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6877 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6880 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6882 if ( _outerFacesFound ) break; // pass to thorough analysis
6884 } // three attempts - loop on CS axes
6886 // Analyse intersections thoroughly.
6887 // We make two loops maximum, on the first one we only exclude touching intersections,
6888 // on the second, if situation is still unclear, we gather and use information on
6889 // position of faces (internal or outer). If faces position is already gathered,
6890 // we make the second loop right away.
6892 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6894 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6895 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6897 int axis = nb_axis->second;
6898 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6900 gp_Ax1 lineAxis( point, axisDir[axis]);
6901 gp_Lin line ( lineAxis );
6903 // add tangent intersections to u2inters
6905 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6906 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6907 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6908 u2inters.insert(make_pair( param, *tgtInt ));
6909 tangentInters[ axis ].clear();
6911 // Count intersections before and after the point excluding touching ones.
6912 // If hasPositionInfo we count intersections of outer boundary only
6914 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6915 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6916 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6917 bool ok = ! u_int1->second._coincides;
6918 while ( ok && u_int1 != u2inters.end() )
6920 double u = u_int1->first;
6921 bool touchingInt = false;
6922 if ( ++u_int2 != u2inters.end() )
6924 // skip intersections at the same point (if the line passes through edge or node)
6926 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6932 // skip tangent intersections
6934 const SMDS_MeshElement* prevFace = u_int1->second._face;
6935 while ( ok && u_int2->second._coincides )
6937 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6943 ok = ( u_int2 != u2inters.end() );
6948 // skip intersections at the same point after tangent intersections
6951 double u2 = u_int2->first;
6953 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6959 // decide if we skipped a touching intersection
6960 if ( nbSamePnt + nbTgt > 0 )
6962 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6963 map< double, TInters >::iterator u_int = u_int1;
6964 for ( ; u_int != u_int2; ++u_int )
6966 if ( u_int->second._coincides ) continue;
6967 double dot = u_int->second._faceNorm * line.Direction();
6968 if ( dot > maxDot ) maxDot = dot;
6969 if ( dot < minDot ) minDot = dot;
6971 touchingInt = ( minDot*maxDot < 0 );
6976 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6987 u_int1 = u_int2; // to next intersection
6989 } // loop on intersections with one line
6993 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6996 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6999 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7000 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7002 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7005 if ( (f<0) == (l<0) )
7008 if ( hasPositionInfo )
7009 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7011 } // loop on intersections of the tree lines - thorough analysis
7013 if ( !hasPositionInfo )
7015 // gather info on faces position - is face in the outer boundary or not
7016 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7017 findOuterBoundary( u2inters.begin()->second._face );
7020 } // two attempts - with and w/o faces position info in the mesh
7022 return TopAbs_UNKNOWN;
7025 //=======================================================================
7027 * \brief Return elements possibly intersecting the line
7029 //=======================================================================
7031 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7032 SMDSAbs_ElementType type,
7033 vector< const SMDS_MeshElement* >& foundElems)
7035 if ( !_ebbTree || _elementType != type )
7037 if ( _ebbTree ) delete _ebbTree;
7038 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7040 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7041 _ebbTree->getElementsNearLine( line, suspectFaces );
7042 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7045 //=======================================================================
7047 * \brief Return SMESH_ElementSearcher
7049 //=======================================================================
7051 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7053 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7056 //=======================================================================
7058 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7060 //=======================================================================
7062 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7064 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7067 //=======================================================================
7069 * \brief Return true if the point is IN or ON of the element
7071 //=======================================================================
7073 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7075 if ( element->GetType() == SMDSAbs_Volume)
7077 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7080 // get ordered nodes
7082 vector< gp_XYZ > xyz;
7083 vector<const SMDS_MeshNode*> nodeList;
7085 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7086 if ( element->IsQuadratic() ) {
7087 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7088 nodeIt = f->interlacedNodesElemIterator();
7089 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7090 nodeIt = e->interlacedNodesElemIterator();
7092 while ( nodeIt->more() )
7094 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7095 xyz.push_back( SMESH_TNodeXYZ(node) );
7096 nodeList.push_back(node);
7099 int i, nbNodes = element->NbNodes();
7101 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7103 // compute face normal
7104 gp_Vec faceNorm(0,0,0);
7105 xyz.push_back( xyz.front() );
7106 nodeList.push_back( nodeList.front() );
7107 for ( i = 0; i < nbNodes; ++i )
7109 gp_Vec edge1( xyz[i+1], xyz[i]);
7110 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7111 faceNorm += edge1 ^ edge2;
7113 double normSize = faceNorm.Magnitude();
7114 if ( normSize <= tol )
7116 // degenerated face: point is out if it is out of all face edges
7117 for ( i = 0; i < nbNodes; ++i )
7119 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7120 if ( !IsOut( &edge, point, tol ))
7125 faceNorm /= normSize;
7127 // check if the point lays on face plane
7128 gp_Vec n2p( xyz[0], point );
7129 if ( fabs( n2p * faceNorm ) > tol )
7130 return true; // not on face plane
7132 // check if point is out of face boundary:
7133 // define it by closest transition of a ray point->infinity through face boundary
7134 // on the face plane.
7135 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7136 // to find intersections of the ray with the boundary.
7138 gp_Vec plnNorm = ray ^ faceNorm;
7139 normSize = plnNorm.Magnitude();
7140 if ( normSize <= tol ) return false; // point coincides with the first node
7141 plnNorm /= normSize;
7142 // for each node of the face, compute its signed distance to the plane
7143 vector<double> dist( nbNodes + 1);
7144 for ( i = 0; i < nbNodes; ++i )
7146 gp_Vec n2p( xyz[i], point );
7147 dist[i] = n2p * plnNorm;
7149 dist.back() = dist.front();
7150 // find the closest intersection
7152 double rClosest, distClosest = 1e100;;
7154 for ( i = 0; i < nbNodes; ++i )
7157 if ( fabs( dist[i]) < tol )
7159 else if ( fabs( dist[i+1]) < tol )
7161 else if ( dist[i] * dist[i+1] < 0 )
7162 r = dist[i] / ( dist[i] - dist[i+1] );
7164 continue; // no intersection
7165 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7166 gp_Vec p2int ( point, pInt);
7167 if ( p2int * ray > -tol ) // right half-space
7169 double intDist = p2int.SquareMagnitude();
7170 if ( intDist < distClosest )
7175 distClosest = intDist;
7180 return true; // no intesections - out
7182 // analyse transition
7183 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7184 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7185 gp_Vec p2int ( point, pClosest );
7186 bool out = (edgeNorm * p2int) < -tol;
7187 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7190 // ray pass through a face node; analyze transition through an adjacent edge
7191 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7192 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7193 gp_Vec edgeAdjacent( p1, p2 );
7194 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7195 bool out2 = (edgeNorm2 * p2int) < -tol;
7197 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7198 return covexCorner ? (out || out2) : (out && out2);
7200 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7202 // point is out of edge if it is NOT ON any straight part of edge
7203 // (we consider quadratic edge as being composed of two straight parts)
7204 for ( i = 1; i < nbNodes; ++i )
7206 gp_Vec edge( xyz[i-1], xyz[i]);
7207 gp_Vec n1p ( xyz[i-1], point);
7208 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7211 gp_Vec n2p( xyz[i], point );
7212 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7214 return false; // point is ON this part
7218 // Node or 0D element -------------------------------------------------------------------------
7220 gp_Vec n2p ( xyz[0], point );
7221 return n2p.Magnitude() <= tol;
7226 //=======================================================================
7230 // Position of a point relative to a segment
7234 // VERTEX 1 o----ON-----> VERTEX 2
7238 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7239 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7243 int _index; // index of vertex or segment
7245 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7246 bool operator < (const PointPos& other ) const
7248 if ( _name == other._name )
7249 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7250 return _name < other._name;
7254 //================================================================================
7256 * \brief Return of a point relative to a segment
7257 * \param point2D - the point to analyze position of
7258 * \param xyVec - end points of segments
7259 * \param index0 - 0-based index of the first point of segment
7260 * \param posToFindOut - flags of positions to detect
7261 * \retval PointPos - point position
7263 //================================================================================
7265 PointPos getPointPosition( const gp_XY& point2D,
7266 const gp_XY* segEnds,
7267 const int index0 = 0,
7268 const int posToFindOut = POS_ALL)
7270 const gp_XY& p1 = segEnds[ index0 ];
7271 const gp_XY& p2 = segEnds[ index0+1 ];
7272 const gp_XY grad = p2 - p1;
7274 if ( posToFindOut & POS_VERTEX )
7276 // check if the point2D is at "vertex 1" zone
7277 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7278 p1.Y() + grad.X() ) };
7279 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7280 return PointPos( POS_VERTEX, index0 );
7282 // check if the point2D is at "vertex 2" zone
7283 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7284 p2.Y() + grad.X() ) };
7285 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7286 return PointPos( POS_VERTEX, index0 + 1);
7288 double edgeEquation =
7289 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7290 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7294 //=======================================================================
7296 * \brief Return minimal distance from a point to a face
7298 * Currently we ignore non-planarity and 2nd order of face
7300 //=======================================================================
7302 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7303 const gp_Pnt& point )
7305 double badDistance = -1;
7306 if ( !face ) return badDistance;
7308 // coordinates of nodes (medium nodes, if any, ignored)
7309 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7310 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7311 xyz.resize( face->NbCornerNodes()+1 );
7313 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7314 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7316 gp_Vec OZ ( xyz[0], xyz[1] );
7317 gp_Vec OX ( xyz[0], xyz[2] );
7318 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7320 if ( xyz.size() < 4 ) return badDistance;
7321 OZ = gp_Vec ( xyz[0], xyz[2] );
7322 OX = gp_Vec ( xyz[0], xyz[3] );
7326 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7328 catch ( Standard_Failure ) {
7331 trsf.SetTransformation( tgtCS );
7333 // move all the nodes to 2D
7334 vector<gp_XY> xy( xyz.size() );
7335 for ( size_t i = 0;i < xyz.size()-1; ++i )
7337 gp_XYZ p3d = xyz[i];
7338 trsf.Transforms( p3d );
7339 xy[i].SetCoord( p3d.X(), p3d.Z() );
7341 xyz.back() = xyz.front();
7342 xy.back() = xy.front();
7344 // // move the point in 2D
7345 gp_XYZ tmpPnt = point.XYZ();
7346 trsf.Transforms( tmpPnt );
7347 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7349 // loop on segments of the face to analyze point position ralative to the face
7350 set< PointPos > pntPosSet;
7351 for ( size_t i = 1; i < xy.size(); ++i )
7353 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7354 pntPosSet.insert( pos );
7358 PointPos pos = *pntPosSet.begin();
7359 // cout << "Face " << face->GetID() << " DIST: ";
7360 switch ( pos._name )
7363 // point is most close to a segment
7364 gp_Vec p0p1( point, xyz[ pos._index ] );
7365 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7367 double projDist = p0p1 * p1p2; // distance projected to the segment
7368 gp_Vec projVec = p1p2 * projDist;
7369 gp_Vec distVec = p0p1 - projVec;
7370 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7371 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7372 return distVec.Magnitude();
7375 // point is inside the face
7376 double distToFacePlane = tmpPnt.Y();
7377 // cout << distToFacePlane << ", INSIDE " << endl;
7378 return Abs( distToFacePlane );
7381 // point is most close to a node
7382 gp_Vec distVec( point, xyz[ pos._index ]);
7383 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7384 return distVec.Magnitude();
7390 //=======================================================================
7391 //function : SimplifyFace
7393 //=======================================================================
7394 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7395 vector<const SMDS_MeshNode *>& poly_nodes,
7396 vector<int>& quantities) const
7398 int nbNodes = faceNodes.size();
7403 set<const SMDS_MeshNode*> nodeSet;
7405 // get simple seq of nodes
7406 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7407 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7408 int iSimple = 0, nbUnique = 0;
7410 simpleNodes[iSimple++] = faceNodes[0];
7412 for (int iCur = 1; iCur < nbNodes; iCur++) {
7413 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7414 simpleNodes[iSimple++] = faceNodes[iCur];
7415 if (nodeSet.insert( faceNodes[iCur] ).second)
7419 int nbSimple = iSimple;
7420 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7430 bool foundLoop = (nbSimple > nbUnique);
7433 set<const SMDS_MeshNode*> loopSet;
7434 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7435 const SMDS_MeshNode* n = simpleNodes[iSimple];
7436 if (!loopSet.insert( n ).second) {
7440 int iC = 0, curLast = iSimple;
7441 for (; iC < curLast; iC++) {
7442 if (simpleNodes[iC] == n) break;
7444 int loopLen = curLast - iC;
7446 // create sub-element
7448 quantities.push_back(loopLen);
7449 for (; iC < curLast; iC++) {
7450 poly_nodes.push_back(simpleNodes[iC]);
7453 // shift the rest nodes (place from the first loop position)
7454 for (iC = curLast + 1; iC < nbSimple; iC++) {
7455 simpleNodes[iC - loopLen] = simpleNodes[iC];
7457 nbSimple -= loopLen;
7460 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7461 } // while (foundLoop)
7465 quantities.push_back(iSimple);
7466 for (int i = 0; i < iSimple; i++)
7467 poly_nodes.push_back(simpleNodes[i]);
7473 //=======================================================================
7474 //function : MergeNodes
7475 //purpose : In each group, the cdr of nodes are substituted by the first one
7477 //=======================================================================
7479 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7481 MESSAGE("MergeNodes");
7482 myLastCreatedElems.Clear();
7483 myLastCreatedNodes.Clear();
7485 SMESHDS_Mesh* aMesh = GetMeshDS();
7487 TNodeNodeMap nodeNodeMap; // node to replace - new node
7488 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7489 list< int > rmElemIds, rmNodeIds;
7491 // Fill nodeNodeMap and elems
7493 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7494 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7495 list<const SMDS_MeshNode*>& nodes = *grIt;
7496 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7497 const SMDS_MeshNode* nToKeep = *nIt;
7498 //MESSAGE("node to keep " << nToKeep->GetID());
7499 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7500 const SMDS_MeshNode* nToRemove = *nIt;
7501 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7502 if ( nToRemove != nToKeep ) {
7503 //MESSAGE(" node to remove " << nToRemove->GetID());
7504 rmNodeIds.push_back( nToRemove->GetID() );
7505 AddToSameGroups( nToKeep, nToRemove, aMesh );
7508 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7509 while ( invElemIt->more() ) {
7510 const SMDS_MeshElement* elem = invElemIt->next();
7515 // Change element nodes or remove an element
7517 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7518 for ( ; eIt != elems.end(); eIt++ ) {
7519 const SMDS_MeshElement* elem = *eIt;
7520 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7521 int nbNodes = elem->NbNodes();
7522 int aShapeId = FindShape( elem );
7524 set<const SMDS_MeshNode*> nodeSet;
7525 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7526 int iUnique = 0, iCur = 0, nbRepl = 0;
7527 vector<int> iRepl( nbNodes );
7529 // get new seq of nodes
7530 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7531 while ( itN->more() ) {
7532 const SMDS_MeshNode* n =
7533 static_cast<const SMDS_MeshNode*>( itN->next() );
7535 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7536 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7538 // BUG 0020185: begin
7540 bool stopRecur = false;
7541 set<const SMDS_MeshNode*> nodesRecur;
7542 nodesRecur.insert(n);
7543 while (!stopRecur) {
7544 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7545 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7546 n = (*nnIt_i).second;
7547 if (!nodesRecur.insert(n).second) {
7548 // error: recursive dependancy
7558 curNodes[ iCur ] = n;
7559 bool isUnique = nodeSet.insert( n ).second;
7561 uniqueNodes[ iUnique++ ] = n;
7563 iRepl[ nbRepl++ ] = iCur;
7567 // Analyse element topology after replacement
7570 int nbUniqueNodes = nodeSet.size();
7571 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7572 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7573 // Polygons and Polyhedral volumes
7574 if (elem->IsPoly()) {
7576 if (elem->GetType() == SMDSAbs_Face) {
7578 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7580 for (; inode < nbNodes; inode++) {
7581 face_nodes[inode] = curNodes[inode];
7584 vector<const SMDS_MeshNode *> polygons_nodes;
7585 vector<int> quantities;
7586 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7589 for (int iface = 0; iface < nbNew; iface++) {
7590 int nbNodes = quantities[iface];
7591 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7592 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7593 poly_nodes[ii] = polygons_nodes[inode];
7595 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7596 myLastCreatedElems.Append(newElem);
7598 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7601 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7602 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7603 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7605 if (nbNew > 0) quid = nbNew - 1;
7606 vector<int> newquant(quantities.begin()+quid, quantities.end());
7607 const SMDS_MeshElement* newElem = 0;
7608 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7609 myLastCreatedElems.Append(newElem);
7610 if ( aShapeId && newElem )
7611 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7612 rmElemIds.push_back(elem->GetID());
7615 rmElemIds.push_back(elem->GetID());
7619 else if (elem->GetType() == SMDSAbs_Volume) {
7620 // Polyhedral volume
7621 if (nbUniqueNodes < 4) {
7622 rmElemIds.push_back(elem->GetID());
7625 // each face has to be analyzed in order to check volume validity
7626 const SMDS_VtkVolume* aPolyedre =
7627 dynamic_cast<const SMDS_VtkVolume*>( elem );
7629 int nbFaces = aPolyedre->NbFaces();
7631 vector<const SMDS_MeshNode *> poly_nodes;
7632 vector<int> quantities;
7634 for (int iface = 1; iface <= nbFaces; iface++) {
7635 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7636 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7638 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7639 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7640 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7641 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7642 faceNode = (*nnIt).second;
7644 faceNodes[inode - 1] = faceNode;
7647 SimplifyFace(faceNodes, poly_nodes, quantities);
7650 if (quantities.size() > 3) {
7651 // to be done: remove coincident faces
7654 if (quantities.size() > 3)
7656 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7657 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7658 const SMDS_MeshElement* newElem = 0;
7659 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7660 myLastCreatedElems.Append(newElem);
7661 if ( aShapeId && newElem )
7662 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7663 rmElemIds.push_back(elem->GetID());
7667 rmElemIds.push_back(elem->GetID());
7678 // TODO not all the possible cases are solved. Find something more generic?
7679 switch ( nbNodes ) {
7680 case 2: ///////////////////////////////////// EDGE
7681 isOk = false; break;
7682 case 3: ///////////////////////////////////// TRIANGLE
7683 isOk = false; break;
7685 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7687 else { //////////////////////////////////// QUADRANGLE
7688 if ( nbUniqueNodes < 3 )
7690 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7691 isOk = false; // opposite nodes stick
7692 //MESSAGE("isOk " << isOk);
7695 case 6: ///////////////////////////////////// PENTAHEDRON
7696 if ( nbUniqueNodes == 4 ) {
7697 // ---------------------------------> tetrahedron
7699 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7700 // all top nodes stick: reverse a bottom
7701 uniqueNodes[ 0 ] = curNodes [ 1 ];
7702 uniqueNodes[ 1 ] = curNodes [ 0 ];
7704 else if (nbRepl == 3 &&
7705 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7706 // all bottom nodes stick: set a top before
7707 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7708 uniqueNodes[ 0 ] = curNodes [ 3 ];
7709 uniqueNodes[ 1 ] = curNodes [ 4 ];
7710 uniqueNodes[ 2 ] = curNodes [ 5 ];
7712 else if (nbRepl == 4 &&
7713 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7714 // a lateral face turns into a line: reverse a bottom
7715 uniqueNodes[ 0 ] = curNodes [ 1 ];
7716 uniqueNodes[ 1 ] = curNodes [ 0 ];
7721 else if ( nbUniqueNodes == 5 ) {
7722 // PENTAHEDRON --------------------> 2 tetrahedrons
7723 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7724 // a bottom node sticks with a linked top one
7726 SMDS_MeshElement* newElem =
7727 aMesh->AddVolume(curNodes[ 3 ],
7730 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7731 myLastCreatedElems.Append(newElem);
7733 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7734 // 2. : reverse a bottom
7735 uniqueNodes[ 0 ] = curNodes [ 1 ];
7736 uniqueNodes[ 1 ] = curNodes [ 0 ];
7746 if(elem->IsQuadratic()) { // Quadratic quadrangle
7758 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7761 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7763 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7764 uniqueNodes[0] = curNodes[0];
7765 uniqueNodes[1] = curNodes[2];
7766 uniqueNodes[2] = curNodes[3];
7767 uniqueNodes[3] = curNodes[5];
7768 uniqueNodes[4] = curNodes[6];
7769 uniqueNodes[5] = curNodes[7];
7772 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7773 uniqueNodes[0] = curNodes[0];
7774 uniqueNodes[1] = curNodes[1];
7775 uniqueNodes[2] = curNodes[2];
7776 uniqueNodes[3] = curNodes[4];
7777 uniqueNodes[4] = curNodes[5];
7778 uniqueNodes[5] = curNodes[6];
7781 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7782 uniqueNodes[0] = curNodes[1];
7783 uniqueNodes[1] = curNodes[2];
7784 uniqueNodes[2] = curNodes[3];
7785 uniqueNodes[3] = curNodes[5];
7786 uniqueNodes[4] = curNodes[6];
7787 uniqueNodes[5] = curNodes[0];
7790 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7791 uniqueNodes[0] = curNodes[0];
7792 uniqueNodes[1] = curNodes[1];
7793 uniqueNodes[2] = curNodes[3];
7794 uniqueNodes[3] = curNodes[4];
7795 uniqueNodes[4] = curNodes[6];
7796 uniqueNodes[5] = curNodes[7];
7799 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7800 uniqueNodes[0] = curNodes[0];
7801 uniqueNodes[1] = curNodes[2];
7802 uniqueNodes[2] = curNodes[3];
7803 uniqueNodes[3] = curNodes[1];
7804 uniqueNodes[4] = curNodes[6];
7805 uniqueNodes[5] = curNodes[7];
7808 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7809 uniqueNodes[0] = curNodes[0];
7810 uniqueNodes[1] = curNodes[1];
7811 uniqueNodes[2] = curNodes[2];
7812 uniqueNodes[3] = curNodes[4];
7813 uniqueNodes[4] = curNodes[5];
7814 uniqueNodes[5] = curNodes[7];
7817 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7818 uniqueNodes[0] = curNodes[0];
7819 uniqueNodes[1] = curNodes[1];
7820 uniqueNodes[2] = curNodes[3];
7821 uniqueNodes[3] = curNodes[4];
7822 uniqueNodes[4] = curNodes[2];
7823 uniqueNodes[5] = curNodes[7];
7826 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7827 uniqueNodes[0] = curNodes[0];
7828 uniqueNodes[1] = curNodes[1];
7829 uniqueNodes[2] = curNodes[2];
7830 uniqueNodes[3] = curNodes[4];
7831 uniqueNodes[4] = curNodes[5];
7832 uniqueNodes[5] = curNodes[3];
7837 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7840 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7844 //////////////////////////////////// HEXAHEDRON
7846 SMDS_VolumeTool hexa (elem);
7847 hexa.SetExternalNormal();
7848 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7849 //////////////////////// HEX ---> 1 tetrahedron
7850 for ( int iFace = 0; iFace < 6; iFace++ ) {
7851 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7852 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7853 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7854 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7855 // one face turns into a point ...
7856 int iOppFace = hexa.GetOppFaceIndex( iFace );
7857 ind = hexa.GetFaceNodesIndices( iOppFace );
7859 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7860 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7863 if ( nbStick == 1 ) {
7864 // ... and the opposite one - into a triangle.
7866 ind = hexa.GetFaceNodesIndices( iFace );
7867 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7874 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7875 //////////////////////// HEX ---> 1 prism
7876 int nbTria = 0, iTria[3];
7877 const int *ind; // indices of face nodes
7878 // look for triangular faces
7879 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7880 ind = hexa.GetFaceNodesIndices( iFace );
7881 TIDSortedNodeSet faceNodes;
7882 for ( iCur = 0; iCur < 4; iCur++ )
7883 faceNodes.insert( curNodes[ind[iCur]] );
7884 if ( faceNodes.size() == 3 )
7885 iTria[ nbTria++ ] = iFace;
7887 // check if triangles are opposite
7888 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7891 // set nodes of the bottom triangle
7892 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7894 for ( iCur = 0; iCur < 4; iCur++ )
7895 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7896 indB.push_back( ind[iCur] );
7897 if ( !hexa.IsForward() )
7898 std::swap( indB[0], indB[2] );
7899 for ( iCur = 0; iCur < 3; iCur++ )
7900 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7901 // set nodes of the top triangle
7902 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7903 for ( iCur = 0; iCur < 3; ++iCur )
7904 for ( int j = 0; j < 4; ++j )
7905 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7907 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7913 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7914 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7915 for ( int iFace = 0; iFace < 6; iFace++ ) {
7916 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7917 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7918 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7919 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7920 // one face turns into a point ...
7921 int iOppFace = hexa.GetOppFaceIndex( iFace );
7922 ind = hexa.GetFaceNodesIndices( iOppFace );
7924 iUnique = 2; // reverse a tetrahedron 1 bottom
7925 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7926 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7928 else if ( iUnique >= 0 )
7929 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7931 if ( nbStick == 0 ) {
7932 // ... and the opposite one is a quadrangle
7934 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7935 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7938 SMDS_MeshElement* newElem =
7939 aMesh->AddVolume(curNodes[ind[ 0 ]],
7942 curNodes[indTop[ 0 ]]);
7943 myLastCreatedElems.Append(newElem);
7945 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7952 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7953 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7954 // find indices of quad and tri faces
7955 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7956 for ( iFace = 0; iFace < 6; iFace++ ) {
7957 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7959 for ( iCur = 0; iCur < 4; iCur++ )
7960 nodeSet.insert( curNodes[ind[ iCur ]] );
7961 nbUniqueNodes = nodeSet.size();
7962 if ( nbUniqueNodes == 3 )
7963 iTriFace[ nbTri++ ] = iFace;
7964 else if ( nbUniqueNodes == 4 )
7965 iQuadFace[ nbQuad++ ] = iFace;
7967 if (nbQuad == 2 && nbTri == 4 &&
7968 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7969 // 2 opposite quadrangles stuck with a diagonal;
7970 // sample groups of merged indices: (0-4)(2-6)
7971 // --------------------------------------------> 2 tetrahedrons
7972 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7973 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7974 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7975 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7976 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7977 // stuck with 0-2 diagonal
7985 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7986 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7987 // stuck with 1-3 diagonal
7999 uniqueNodes[ 0 ] = curNodes [ i0 ];
8000 uniqueNodes[ 1 ] = curNodes [ i1d ];
8001 uniqueNodes[ 2 ] = curNodes [ i3d ];
8002 uniqueNodes[ 3 ] = curNodes [ i0t ];
8005 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8009 myLastCreatedElems.Append(newElem);
8011 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8014 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8015 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8016 // --------------------------------------------> prism
8017 // find 2 opposite triangles
8019 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8020 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8021 // find indices of kept and replaced nodes
8022 // and fill unique nodes of 2 opposite triangles
8023 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8024 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8025 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8026 // fill unique nodes
8029 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8030 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8031 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8033 // iCur of a linked node of the opposite face (make normals co-directed):
8034 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8035 // check that correspondent corners of triangles are linked
8036 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8039 uniqueNodes[ iUnique ] = n;
8040 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8049 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8052 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8059 } // switch ( nbNodes )
8061 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8063 if ( isOk ) { // the elem remains valid after sticking nodes
8064 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8066 // Change nodes of polyedre
8067 const SMDS_VtkVolume* aPolyedre =
8068 dynamic_cast<const SMDS_VtkVolume*>( elem );
8070 int nbFaces = aPolyedre->NbFaces();
8072 vector<const SMDS_MeshNode *> poly_nodes;
8073 vector<int> quantities (nbFaces);
8075 for (int iface = 1; iface <= nbFaces; iface++) {
8076 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8077 quantities[iface - 1] = nbFaceNodes;
8079 for (inode = 1; inode <= nbFaceNodes; inode++) {
8080 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8082 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8083 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8084 curNode = (*nnIt).second;
8086 poly_nodes.push_back(curNode);
8089 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8092 else // replace non-polyhedron elements
8094 const SMDSAbs_ElementType etyp = elem->GetType();
8095 const int elemId = elem->GetID();
8096 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8097 uniqueNodes.resize(nbUniqueNodes);
8099 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8101 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8102 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8103 if ( sm && newElem )
8104 sm->AddElement( newElem );
8105 if ( elem != newElem )
8106 ReplaceElemInGroups( elem, newElem, aMesh );
8110 // Remove invalid regular element or invalid polygon
8111 rmElemIds.push_back( elem->GetID() );
8114 } // loop on elements
8116 // Remove bad elements, then equal nodes (order important)
8118 Remove( rmElemIds, false );
8119 Remove( rmNodeIds, true );
8124 // ========================================================
8125 // class : SortableElement
8126 // purpose : allow sorting elements basing on their nodes
8127 // ========================================================
8128 class SortableElement : public set <const SMDS_MeshElement*>
8132 SortableElement( const SMDS_MeshElement* theElem )
8135 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8136 while ( nodeIt->more() )
8137 this->insert( nodeIt->next() );
8140 const SMDS_MeshElement* Get() const
8143 void Set(const SMDS_MeshElement* e) const
8148 mutable const SMDS_MeshElement* myElem;
8151 //=======================================================================
8152 //function : FindEqualElements
8153 //purpose : Return list of group of elements built on the same nodes.
8154 // Search among theElements or in the whole mesh if theElements is empty
8155 //=======================================================================
8156 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
8157 TListOfListOfElementsID & theGroupsOfElementsID)
8159 myLastCreatedElems.Clear();
8160 myLastCreatedNodes.Clear();
8162 typedef set<const SMDS_MeshElement*> TElemsSet;
8163 typedef map< SortableElement, int > TMapOfNodeSet;
8164 typedef list<int> TGroupOfElems;
8167 if ( theElements.empty() )
8168 { // get all elements in the mesh
8169 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8170 while ( eIt->more() )
8171 elems.insert( elems.end(), eIt->next());
8174 elems = theElements;
8176 vector< TGroupOfElems > arrayOfGroups;
8177 TGroupOfElems groupOfElems;
8178 TMapOfNodeSet mapOfNodeSet;
8180 TElemsSet::iterator elemIt = elems.begin();
8181 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
8182 const SMDS_MeshElement* curElem = *elemIt;
8183 SortableElement SE(curElem);
8186 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8187 if( !(pp.second) ) {
8188 TMapOfNodeSet::iterator& itSE = pp.first;
8189 ind = (*itSE).second;
8190 arrayOfGroups[ind].push_back(curElem->GetID());
8193 groupOfElems.clear();
8194 groupOfElems.push_back(curElem->GetID());
8195 arrayOfGroups.push_back(groupOfElems);
8200 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8201 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8202 groupOfElems = *groupIt;
8203 if ( groupOfElems.size() > 1 ) {
8204 groupOfElems.sort();
8205 theGroupsOfElementsID.push_back(groupOfElems);
8210 //=======================================================================
8211 //function : MergeElements
8212 //purpose : In each given group, substitute all elements by the first one.
8213 //=======================================================================
8215 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8217 myLastCreatedElems.Clear();
8218 myLastCreatedNodes.Clear();
8220 typedef list<int> TListOfIDs;
8221 TListOfIDs rmElemIds; // IDs of elems to remove
8223 SMESHDS_Mesh* aMesh = GetMeshDS();
8225 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8226 while ( groupsIt != theGroupsOfElementsID.end() ) {
8227 TListOfIDs& aGroupOfElemID = *groupsIt;
8228 aGroupOfElemID.sort();
8229 int elemIDToKeep = aGroupOfElemID.front();
8230 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8231 aGroupOfElemID.pop_front();
8232 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8233 while ( idIt != aGroupOfElemID.end() ) {
8234 int elemIDToRemove = *idIt;
8235 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8236 // add the kept element in groups of removed one (PAL15188)
8237 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8238 rmElemIds.push_back( elemIDToRemove );
8244 Remove( rmElemIds, false );
8247 //=======================================================================
8248 //function : MergeEqualElements
8249 //purpose : Remove all but one of elements built on the same nodes.
8250 //=======================================================================
8252 void SMESH_MeshEditor::MergeEqualElements()
8254 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
8255 to merge equal elements in the whole mesh */
8256 TListOfListOfElementsID aGroupsOfElementsID;
8257 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8258 MergeElements(aGroupsOfElementsID);
8261 //=======================================================================
8262 //function : FindFaceInSet
8263 //purpose : Return a face having linked nodes n1 and n2 and which is
8264 // - not in avoidSet,
8265 // - in elemSet provided that !elemSet.empty()
8266 // i1 and i2 optionally returns indices of n1 and n2
8267 //=======================================================================
8269 const SMDS_MeshElement*
8270 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8271 const SMDS_MeshNode* n2,
8272 const TIDSortedElemSet& elemSet,
8273 const TIDSortedElemSet& avoidSet,
8279 const SMDS_MeshElement* face = 0;
8281 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8282 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8283 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8285 //MESSAGE("in while ( invElemIt->more() && !face )");
8286 const SMDS_MeshElement* elem = invElemIt->next();
8287 if (avoidSet.count( elem ))
8289 if ( !elemSet.empty() && !elemSet.count( elem ))
8292 i1 = elem->GetNodeIndex( n1 );
8293 // find a n2 linked to n1
8294 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8295 for ( int di = -1; di < 2 && !face; di += 2 )
8297 i2 = (i1+di+nbN) % nbN;
8298 if ( elem->GetNode( i2 ) == n2 )
8301 if ( !face && elem->IsQuadratic())
8303 // analysis for quadratic elements using all nodes
8304 const SMDS_VtkFace* F =
8305 dynamic_cast<const SMDS_VtkFace*>(elem);
8306 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8307 // use special nodes iterator
8308 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8309 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8310 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8312 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8313 if ( n1 == prevN && n2 == n )
8317 else if ( n2 == prevN && n1 == n )
8319 face = elem; swap( i1, i2 );
8325 if ( n1ind ) *n1ind = i1;
8326 if ( n2ind ) *n2ind = i2;
8330 //=======================================================================
8331 //function : findAdjacentFace
8333 //=======================================================================
8335 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8336 const SMDS_MeshNode* n2,
8337 const SMDS_MeshElement* elem)
8339 TIDSortedElemSet elemSet, avoidSet;
8341 avoidSet.insert ( elem );
8342 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8345 //=======================================================================
8346 //function : FindFreeBorder
8348 //=======================================================================
8350 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8352 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8353 const SMDS_MeshNode* theSecondNode,
8354 const SMDS_MeshNode* theLastNode,
8355 list< const SMDS_MeshNode* > & theNodes,
8356 list< const SMDS_MeshElement* >& theFaces)
8358 if ( !theFirstNode || !theSecondNode )
8360 // find border face between theFirstNode and theSecondNode
8361 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8365 theFaces.push_back( curElem );
8366 theNodes.push_back( theFirstNode );
8367 theNodes.push_back( theSecondNode );
8369 //vector<const SMDS_MeshNode*> nodes;
8370 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8371 TIDSortedElemSet foundElems;
8372 bool needTheLast = ( theLastNode != 0 );
8374 while ( nStart != theLastNode ) {
8375 if ( nStart == theFirstNode )
8376 return !needTheLast;
8378 // find all free border faces sharing form nStart
8380 list< const SMDS_MeshElement* > curElemList;
8381 list< const SMDS_MeshNode* > nStartList;
8382 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8383 while ( invElemIt->more() ) {
8384 const SMDS_MeshElement* e = invElemIt->next();
8385 if ( e == curElem || foundElems.insert( e ).second ) {
8387 int iNode = 0, nbNodes = e->NbNodes();
8388 //const SMDS_MeshNode* nodes[nbNodes+1];
8389 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8391 if(e->IsQuadratic()) {
8392 const SMDS_VtkFace* F =
8393 dynamic_cast<const SMDS_VtkFace*>(e);
8394 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8395 // use special nodes iterator
8396 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8397 while( anIter->more() ) {
8398 nodes[ iNode++ ] = cast2Node(anIter->next());
8402 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8403 while ( nIt->more() )
8404 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8406 nodes[ iNode ] = nodes[ 0 ];
8408 for ( iNode = 0; iNode < nbNodes; iNode++ )
8409 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8410 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8411 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8413 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8414 curElemList.push_back( e );
8418 // analyse the found
8420 int nbNewBorders = curElemList.size();
8421 if ( nbNewBorders == 0 ) {
8422 // no free border furthermore
8423 return !needTheLast;
8425 else if ( nbNewBorders == 1 ) {
8426 // one more element found
8428 nStart = nStartList.front();
8429 curElem = curElemList.front();
8430 theFaces.push_back( curElem );
8431 theNodes.push_back( nStart );
8434 // several continuations found
8435 list< const SMDS_MeshElement* >::iterator curElemIt;
8436 list< const SMDS_MeshNode* >::iterator nStartIt;
8437 // check if one of them reached the last node
8438 if ( needTheLast ) {
8439 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8440 curElemIt!= curElemList.end();
8441 curElemIt++, nStartIt++ )
8442 if ( *nStartIt == theLastNode ) {
8443 theFaces.push_back( *curElemIt );
8444 theNodes.push_back( *nStartIt );
8448 // find the best free border by the continuations
8449 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8450 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8451 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8452 curElemIt!= curElemList.end();
8453 curElemIt++, nStartIt++ )
8455 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8456 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8457 // find one more free border
8458 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8462 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8463 // choice: clear a worse one
8464 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8465 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8466 contNodes[ iWorse ].clear();
8467 contFaces[ iWorse ].clear();
8470 if ( contNodes[0].empty() && contNodes[1].empty() )
8473 // append the best free border
8474 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8475 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8476 theNodes.pop_back(); // remove nIgnore
8477 theNodes.pop_back(); // remove nStart
8478 theFaces.pop_back(); // remove curElem
8479 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8480 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8481 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8482 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8485 } // several continuations found
8486 } // while ( nStart != theLastNode )
8491 //=======================================================================
8492 //function : CheckFreeBorderNodes
8493 //purpose : Return true if the tree nodes are on a free border
8494 //=======================================================================
8496 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8497 const SMDS_MeshNode* theNode2,
8498 const SMDS_MeshNode* theNode3)
8500 list< const SMDS_MeshNode* > nodes;
8501 list< const SMDS_MeshElement* > faces;
8502 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8505 //=======================================================================
8506 //function : SewFreeBorder
8508 //=======================================================================
8510 SMESH_MeshEditor::Sew_Error
8511 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8512 const SMDS_MeshNode* theBordSecondNode,
8513 const SMDS_MeshNode* theBordLastNode,
8514 const SMDS_MeshNode* theSideFirstNode,
8515 const SMDS_MeshNode* theSideSecondNode,
8516 const SMDS_MeshNode* theSideThirdNode,
8517 const bool theSideIsFreeBorder,
8518 const bool toCreatePolygons,
8519 const bool toCreatePolyedrs)
8521 myLastCreatedElems.Clear();
8522 myLastCreatedNodes.Clear();
8524 MESSAGE("::SewFreeBorder()");
8525 Sew_Error aResult = SEW_OK;
8527 // ====================================
8528 // find side nodes and elements
8529 // ====================================
8531 list< const SMDS_MeshNode* > nSide[ 2 ];
8532 list< const SMDS_MeshElement* > eSide[ 2 ];
8533 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8534 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8538 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8539 nSide[0], eSide[0])) {
8540 MESSAGE(" Free Border 1 not found " );
8541 aResult = SEW_BORDER1_NOT_FOUND;
8543 if (theSideIsFreeBorder) {
8546 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8547 nSide[1], eSide[1])) {
8548 MESSAGE(" Free Border 2 not found " );
8549 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8552 if ( aResult != SEW_OK )
8555 if (!theSideIsFreeBorder) {
8559 // -------------------------------------------------------------------------
8561 // 1. If nodes to merge are not coincident, move nodes of the free border
8562 // from the coord sys defined by the direction from the first to last
8563 // nodes of the border to the correspondent sys of the side 2
8564 // 2. On the side 2, find the links most co-directed with the correspondent
8565 // links of the free border
8566 // -------------------------------------------------------------------------
8568 // 1. Since sewing may break if there are volumes to split on the side 2,
8569 // we wont move nodes but just compute new coordinates for them
8570 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8571 TNodeXYZMap nBordXYZ;
8572 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8573 list< const SMDS_MeshNode* >::iterator nBordIt;
8575 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8576 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8577 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8578 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8579 double tol2 = 1.e-8;
8580 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8581 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8582 // Need node movement.
8584 // find X and Z axes to create trsf
8585 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8587 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8589 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8592 gp_Ax3 toBordAx( Pb1, Zb, X );
8593 gp_Ax3 fromSideAx( Ps1, Zs, X );
8594 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8596 gp_Trsf toBordSys, fromSide2Sys;
8597 toBordSys.SetTransformation( toBordAx );
8598 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8599 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8602 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8603 const SMDS_MeshNode* n = *nBordIt;
8604 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8605 toBordSys.Transforms( xyz );
8606 fromSide2Sys.Transforms( xyz );
8607 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8611 // just insert nodes XYZ in the nBordXYZ map
8612 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8613 const SMDS_MeshNode* n = *nBordIt;
8614 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8618 // 2. On the side 2, find the links most co-directed with the correspondent
8619 // links of the free border
8621 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8622 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8623 sideNodes.push_back( theSideFirstNode );
8625 bool hasVolumes = false;
8626 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8627 set<long> foundSideLinkIDs, checkedLinkIDs;
8628 SMDS_VolumeTool volume;
8629 //const SMDS_MeshNode* faceNodes[ 4 ];
8631 const SMDS_MeshNode* sideNode;
8632 const SMDS_MeshElement* sideElem;
8633 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8634 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8635 nBordIt = bordNodes.begin();
8637 // border node position and border link direction to compare with
8638 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8639 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8640 // choose next side node by link direction or by closeness to
8641 // the current border node:
8642 bool searchByDir = ( *nBordIt != theBordLastNode );
8644 // find the next node on the Side 2
8646 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8648 checkedLinkIDs.clear();
8649 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8651 // loop on inverse elements of current node (prevSideNode) on the Side 2
8652 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8653 while ( invElemIt->more() )
8655 const SMDS_MeshElement* elem = invElemIt->next();
8656 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8657 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8658 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8659 bool isVolume = volume.Set( elem );
8660 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8661 if ( isVolume ) // --volume
8663 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8664 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8665 if(elem->IsQuadratic()) {
8666 const SMDS_VtkFace* F =
8667 dynamic_cast<const SMDS_VtkFace*>(elem);
8668 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8669 // use special nodes iterator
8670 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8671 while( anIter->more() ) {
8672 nodes[ iNode ] = cast2Node(anIter->next());
8673 if ( nodes[ iNode++ ] == prevSideNode )
8674 iPrevNode = iNode - 1;
8678 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8679 while ( nIt->more() ) {
8680 nodes[ iNode ] = cast2Node( nIt->next() );
8681 if ( nodes[ iNode++ ] == prevSideNode )
8682 iPrevNode = iNode - 1;
8685 // there are 2 links to check
8690 // loop on links, to be precise, on the second node of links
8691 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8692 const SMDS_MeshNode* n = nodes[ iNode ];
8694 if ( !volume.IsLinked( n, prevSideNode ))
8698 if ( iNode ) // a node before prevSideNode
8699 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8700 else // a node after prevSideNode
8701 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8703 // check if this link was already used
8704 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8705 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8706 if (!isJustChecked &&
8707 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8709 // test a link geometrically
8710 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8711 bool linkIsBetter = false;
8712 double dot = 0.0, dist = 0.0;
8713 if ( searchByDir ) { // choose most co-directed link
8714 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8715 linkIsBetter = ( dot > maxDot );
8717 else { // choose link with the node closest to bordPos
8718 dist = ( nextXYZ - bordPos ).SquareModulus();
8719 linkIsBetter = ( dist < minDist );
8721 if ( linkIsBetter ) {
8730 } // loop on inverse elements of prevSideNode
8733 MESSAGE(" Cant find path by links of the Side 2 ");
8734 return SEW_BAD_SIDE_NODES;
8736 sideNodes.push_back( sideNode );
8737 sideElems.push_back( sideElem );
8738 foundSideLinkIDs.insert ( linkID );
8739 prevSideNode = sideNode;
8741 if ( *nBordIt == theBordLastNode )
8742 searchByDir = false;
8744 // find the next border link to compare with
8745 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8746 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8747 // move to next border node if sideNode is before forward border node (bordPos)
8748 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8749 prevBordNode = *nBordIt;
8751 bordPos = nBordXYZ[ *nBordIt ];
8752 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8753 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8757 while ( sideNode != theSideSecondNode );
8759 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8760 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8761 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8763 } // end nodes search on the side 2
8765 // ============================
8766 // sew the border to the side 2
8767 // ============================
8769 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8770 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8772 TListOfListOfNodes nodeGroupsToMerge;
8773 if ( nbNodes[0] == nbNodes[1] ||
8774 ( theSideIsFreeBorder && !theSideThirdNode)) {
8776 // all nodes are to be merged
8778 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8779 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8780 nIt[0]++, nIt[1]++ )
8782 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8783 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8784 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8789 // insert new nodes into the border and the side to get equal nb of segments
8791 // get normalized parameters of nodes on the borders
8792 //double param[ 2 ][ maxNbNodes ];
8794 param[0] = new double [ maxNbNodes ];
8795 param[1] = new double [ maxNbNodes ];
8797 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8798 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8799 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8800 const SMDS_MeshNode* nPrev = *nIt;
8801 double bordLength = 0;
8802 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8803 const SMDS_MeshNode* nCur = *nIt;
8804 gp_XYZ segment (nCur->X() - nPrev->X(),
8805 nCur->Y() - nPrev->Y(),
8806 nCur->Z() - nPrev->Z());
8807 double segmentLen = segment.Modulus();
8808 bordLength += segmentLen;
8809 param[ iBord ][ iNode ] = bordLength;
8812 // normalize within [0,1]
8813 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8814 param[ iBord ][ iNode ] /= bordLength;
8818 // loop on border segments
8819 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8820 int i[ 2 ] = { 0, 0 };
8821 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8822 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8824 TElemOfNodeListMap insertMap;
8825 TElemOfNodeListMap::iterator insertMapIt;
8827 // key: elem to insert nodes into
8828 // value: 2 nodes to insert between + nodes to be inserted
8830 bool next[ 2 ] = { false, false };
8832 // find min adjacent segment length after sewing
8833 double nextParam = 10., prevParam = 0;
8834 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8835 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8836 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8837 if ( i[ iBord ] > 0 )
8838 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8840 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8841 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8842 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8844 // choose to insert or to merge nodes
8845 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8846 if ( Abs( du ) <= minSegLen * 0.2 ) {
8849 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8850 const SMDS_MeshNode* n0 = *nIt[0];
8851 const SMDS_MeshNode* n1 = *nIt[1];
8852 nodeGroupsToMerge.back().push_back( n1 );
8853 nodeGroupsToMerge.back().push_back( n0 );
8854 // position of node of the border changes due to merge
8855 param[ 0 ][ i[0] ] += du;
8856 // move n1 for the sake of elem shape evaluation during insertion.
8857 // n1 will be removed by MergeNodes() anyway
8858 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8859 next[0] = next[1] = true;
8864 int intoBord = ( du < 0 ) ? 0 : 1;
8865 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8866 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8867 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8868 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8869 if ( intoBord == 1 ) {
8870 // move node of the border to be on a link of elem of the side
8871 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8872 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8873 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8874 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8875 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8877 insertMapIt = insertMap.find( elem );
8878 bool notFound = ( insertMapIt == insertMap.end() );
8879 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8881 // insert into another link of the same element:
8882 // 1. perform insertion into the other link of the elem
8883 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8884 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8885 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8886 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8887 // 2. perform insertion into the link of adjacent faces
8889 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8891 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8895 if (toCreatePolyedrs) {
8896 // perform insertion into the links of adjacent volumes
8897 UpdateVolumes(n12, n22, nodeList);
8899 // 3. find an element appeared on n1 and n2 after the insertion
8900 insertMap.erase( elem );
8901 elem = findAdjacentFace( n1, n2, 0 );
8903 if ( notFound || otherLink ) {
8904 // add element and nodes of the side into the insertMap
8905 insertMapIt = insertMap.insert
8906 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8907 (*insertMapIt).second.push_back( n1 );
8908 (*insertMapIt).second.push_back( n2 );
8910 // add node to be inserted into elem
8911 (*insertMapIt).second.push_back( nIns );
8912 next[ 1 - intoBord ] = true;
8915 // go to the next segment
8916 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8917 if ( next[ iBord ] ) {
8918 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8920 nPrev[ iBord ] = *nIt[ iBord ];
8921 nIt[ iBord ]++; i[ iBord ]++;
8925 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8927 // perform insertion of nodes into elements
8929 for (insertMapIt = insertMap.begin();
8930 insertMapIt != insertMap.end();
8933 const SMDS_MeshElement* elem = (*insertMapIt).first;
8934 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8935 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8936 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8938 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8940 if ( !theSideIsFreeBorder ) {
8941 // look for and insert nodes into the faces adjacent to elem
8943 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8945 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8950 if (toCreatePolyedrs) {
8951 // perform insertion into the links of adjacent volumes
8952 UpdateVolumes(n1, n2, nodeList);
8958 } // end: insert new nodes
8960 MergeNodes ( nodeGroupsToMerge );
8965 //=======================================================================
8966 //function : InsertNodesIntoLink
8967 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8968 // and theBetweenNode2 and split theElement
8969 //=======================================================================
8971 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8972 const SMDS_MeshNode* theBetweenNode1,
8973 const SMDS_MeshNode* theBetweenNode2,
8974 list<const SMDS_MeshNode*>& theNodesToInsert,
8975 const bool toCreatePoly)
8977 if ( theFace->GetType() != SMDSAbs_Face ) return;
8979 // find indices of 2 link nodes and of the rest nodes
8980 int iNode = 0, il1, il2, i3, i4;
8981 il1 = il2 = i3 = i4 = -1;
8982 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8983 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8985 if(theFace->IsQuadratic()) {
8986 const SMDS_VtkFace* F =
8987 dynamic_cast<const SMDS_VtkFace*>(theFace);
8988 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8989 // use special nodes iterator
8990 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8991 while( anIter->more() ) {
8992 const SMDS_MeshNode* n = cast2Node(anIter->next());
8993 if ( n == theBetweenNode1 )
8995 else if ( n == theBetweenNode2 )
9001 nodes[ iNode++ ] = n;
9005 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9006 while ( nodeIt->more() ) {
9007 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9008 if ( n == theBetweenNode1 )
9010 else if ( n == theBetweenNode2 )
9016 nodes[ iNode++ ] = n;
9019 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9022 // arrange link nodes to go one after another regarding the face orientation
9023 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9024 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9029 aNodesToInsert.reverse();
9031 // check that not link nodes of a quadrangles are in good order
9032 int nbFaceNodes = theFace->NbNodes();
9033 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9039 if (toCreatePoly || theFace->IsPoly()) {
9042 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9044 // add nodes of face up to first node of link
9047 if(theFace->IsQuadratic()) {
9048 const SMDS_VtkFace* F =
9049 dynamic_cast<const SMDS_VtkFace*>(theFace);
9050 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9051 // use special nodes iterator
9052 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9053 while( anIter->more() && !isFLN ) {
9054 const SMDS_MeshNode* n = cast2Node(anIter->next());
9055 poly_nodes[iNode++] = n;
9056 if (n == nodes[il1]) {
9060 // add nodes to insert
9061 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9062 for (; nIt != aNodesToInsert.end(); nIt++) {
9063 poly_nodes[iNode++] = *nIt;
9065 // add nodes of face starting from last node of link
9066 while ( anIter->more() ) {
9067 poly_nodes[iNode++] = cast2Node(anIter->next());
9071 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9072 while ( nodeIt->more() && !isFLN ) {
9073 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9074 poly_nodes[iNode++] = n;
9075 if (n == nodes[il1]) {
9079 // add nodes to insert
9080 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9081 for (; nIt != aNodesToInsert.end(); nIt++) {
9082 poly_nodes[iNode++] = *nIt;
9084 // add nodes of face starting from last node of link
9085 while ( nodeIt->more() ) {
9086 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9087 poly_nodes[iNode++] = n;
9091 // edit or replace the face
9092 SMESHDS_Mesh *aMesh = GetMeshDS();
9094 if (theFace->IsPoly()) {
9095 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9098 int aShapeId = FindShape( theFace );
9100 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9101 myLastCreatedElems.Append(newElem);
9102 if ( aShapeId && newElem )
9103 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9105 aMesh->RemoveElement(theFace);
9110 SMESHDS_Mesh *aMesh = GetMeshDS();
9111 if( !theFace->IsQuadratic() ) {
9113 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9114 int nbLinkNodes = 2 + aNodesToInsert.size();
9115 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9116 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9117 linkNodes[ 0 ] = nodes[ il1 ];
9118 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9119 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9120 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9121 linkNodes[ iNode++ ] = *nIt;
9123 // decide how to split a quadrangle: compare possible variants
9124 // and choose which of splits to be a quadrangle
9125 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9126 if ( nbFaceNodes == 3 ) {
9127 iBestQuad = nbSplits;
9130 else if ( nbFaceNodes == 4 ) {
9131 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9132 double aBestRate = DBL_MAX;
9133 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9135 double aBadRate = 0;
9136 // evaluate elements quality
9137 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9138 if ( iSplit == iQuad ) {
9139 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9143 aBadRate += getBadRate( &quad, aCrit );
9146 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9148 nodes[ iSplit < iQuad ? i4 : i3 ]);
9149 aBadRate += getBadRate( &tria, aCrit );
9153 if ( aBadRate < aBestRate ) {
9155 aBestRate = aBadRate;
9160 // create new elements
9161 int aShapeId = FindShape( theFace );
9164 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9165 SMDS_MeshElement* newElem = 0;
9166 if ( iSplit == iBestQuad )
9167 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9172 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9174 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9175 myLastCreatedElems.Append(newElem);
9176 if ( aShapeId && newElem )
9177 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9180 // change nodes of theFace
9181 const SMDS_MeshNode* newNodes[ 4 ];
9182 newNodes[ 0 ] = linkNodes[ i1 ];
9183 newNodes[ 1 ] = linkNodes[ i2 ];
9184 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9185 newNodes[ 3 ] = nodes[ i4 ];
9186 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9187 const SMDS_MeshElement* newElem = 0;
9188 if (iSplit == iBestQuad)
9189 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9191 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9192 myLastCreatedElems.Append(newElem);
9193 if ( aShapeId && newElem )
9194 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9195 } // end if(!theFace->IsQuadratic())
9196 else { // theFace is quadratic
9197 // we have to split theFace on simple triangles and one simple quadrangle
9199 int nbshift = tmp*2;
9200 // shift nodes in nodes[] by nbshift
9202 for(i=0; i<nbshift; i++) {
9203 const SMDS_MeshNode* n = nodes[0];
9204 for(j=0; j<nbFaceNodes-1; j++) {
9205 nodes[j] = nodes[j+1];
9207 nodes[nbFaceNodes-1] = n;
9209 il1 = il1 - nbshift;
9210 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9211 // n0 n1 n2 n0 n1 n2
9212 // +-----+-----+ +-----+-----+
9221 // create new elements
9222 int aShapeId = FindShape( theFace );
9225 if(nbFaceNodes==6) { // quadratic triangle
9226 SMDS_MeshElement* newElem =
9227 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9228 myLastCreatedElems.Append(newElem);
9229 if ( aShapeId && newElem )
9230 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9231 if(theFace->IsMediumNode(nodes[il1])) {
9232 // create quadrangle
9233 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9234 myLastCreatedElems.Append(newElem);
9235 if ( aShapeId && newElem )
9236 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9242 // create quadrangle
9243 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9244 myLastCreatedElems.Append(newElem);
9245 if ( aShapeId && newElem )
9246 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9252 else { // nbFaceNodes==8 - quadratic quadrangle
9253 SMDS_MeshElement* newElem =
9254 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9255 myLastCreatedElems.Append(newElem);
9256 if ( aShapeId && newElem )
9257 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9258 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9259 myLastCreatedElems.Append(newElem);
9260 if ( aShapeId && newElem )
9261 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9262 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9263 myLastCreatedElems.Append(newElem);
9264 if ( aShapeId && newElem )
9265 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9266 if(theFace->IsMediumNode(nodes[il1])) {
9267 // create quadrangle
9268 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9269 myLastCreatedElems.Append(newElem);
9270 if ( aShapeId && newElem )
9271 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9277 // create quadrangle
9278 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9279 myLastCreatedElems.Append(newElem);
9280 if ( aShapeId && newElem )
9281 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9287 // create needed triangles using n1,n2,n3 and inserted nodes
9288 int nbn = 2 + aNodesToInsert.size();
9289 //const SMDS_MeshNode* aNodes[nbn];
9290 vector<const SMDS_MeshNode*> aNodes(nbn);
9291 aNodes[0] = nodes[n1];
9292 aNodes[nbn-1] = nodes[n2];
9293 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9294 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9295 aNodes[iNode++] = *nIt;
9297 for(i=1; i<nbn; i++) {
9298 SMDS_MeshElement* newElem =
9299 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9300 myLastCreatedElems.Append(newElem);
9301 if ( aShapeId && newElem )
9302 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9306 aMesh->RemoveElement(theFace);
9309 //=======================================================================
9310 //function : UpdateVolumes
9312 //=======================================================================
9313 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9314 const SMDS_MeshNode* theBetweenNode2,
9315 list<const SMDS_MeshNode*>& theNodesToInsert)
9317 myLastCreatedElems.Clear();
9318 myLastCreatedNodes.Clear();
9320 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9321 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9322 const SMDS_MeshElement* elem = invElemIt->next();
9324 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9325 SMDS_VolumeTool aVolume (elem);
9326 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9329 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9330 int iface, nbFaces = aVolume.NbFaces();
9331 vector<const SMDS_MeshNode *> poly_nodes;
9332 vector<int> quantities (nbFaces);
9334 for (iface = 0; iface < nbFaces; iface++) {
9335 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9336 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9337 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9339 for (int inode = 0; inode < nbFaceNodes; inode++) {
9340 poly_nodes.push_back(faceNodes[inode]);
9342 if (nbInserted == 0) {
9343 if (faceNodes[inode] == theBetweenNode1) {
9344 if (faceNodes[inode + 1] == theBetweenNode2) {
9345 nbInserted = theNodesToInsert.size();
9347 // add nodes to insert
9348 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9349 for (; nIt != theNodesToInsert.end(); nIt++) {
9350 poly_nodes.push_back(*nIt);
9354 else if (faceNodes[inode] == theBetweenNode2) {
9355 if (faceNodes[inode + 1] == theBetweenNode1) {
9356 nbInserted = theNodesToInsert.size();
9358 // add nodes to insert in reversed order
9359 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9361 for (; nIt != theNodesToInsert.begin(); nIt--) {
9362 poly_nodes.push_back(*nIt);
9364 poly_nodes.push_back(*nIt);
9371 quantities[iface] = nbFaceNodes + nbInserted;
9374 // Replace or update the volume
9375 SMESHDS_Mesh *aMesh = GetMeshDS();
9377 if (elem->IsPoly()) {
9378 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9382 int aShapeId = FindShape( elem );
9384 SMDS_MeshElement* newElem =
9385 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9386 myLastCreatedElems.Append(newElem);
9387 if (aShapeId && newElem)
9388 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9390 aMesh->RemoveElement(elem);
9397 //================================================================================
9399 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9401 //================================================================================
9403 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9404 vector<const SMDS_MeshNode *> & nodes,
9405 vector<int> & nbNodeInFaces )
9408 nbNodeInFaces.clear();
9409 SMDS_VolumeTool vTool ( elem );
9410 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9412 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9413 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9414 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9419 //=======================================================================
9421 * \brief Convert elements contained in a submesh to quadratic
9422 * \return int - nb of checked elements
9424 //=======================================================================
9426 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9427 SMESH_MesherHelper& theHelper,
9428 const bool theForce3d)
9431 if( !theSm ) return nbElem;
9433 vector<int> nbNodeInFaces;
9434 vector<const SMDS_MeshNode *> nodes;
9435 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9436 while(ElemItr->more())
9439 const SMDS_MeshElement* elem = ElemItr->next();
9440 if( !elem || elem->IsQuadratic() ) continue;
9442 // get elem data needed to re-create it
9444 const int id = elem->GetID();
9445 const int nbNodes = elem->NbNodes();
9446 const SMDSAbs_ElementType aType = elem->GetType();
9447 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9448 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9449 if ( aGeomType == SMDSEntity_Polyhedra )
9450 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9451 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9452 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9454 // remove a linear element
9455 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9457 const SMDS_MeshElement* NewElem = 0;
9463 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9471 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9474 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9477 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9482 case SMDSAbs_Volume :
9486 case SMDSEntity_Tetra:
9487 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9489 case SMDSEntity_Pyramid:
9490 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9492 case SMDSEntity_Penta:
9493 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9495 case SMDSEntity_Hexa:
9496 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9497 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9499 case SMDSEntity_Hexagonal_Prism:
9501 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9508 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9510 theSm->AddElement( NewElem );
9515 //=======================================================================
9516 //function : ConvertToQuadratic
9518 //=======================================================================
9520 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9522 SMESHDS_Mesh* meshDS = GetMeshDS();
9524 SMESH_MesherHelper aHelper(*myMesh);
9525 aHelper.SetIsQuadratic( true );
9527 int nbCheckedElems = 0;
9528 if ( myMesh->HasShapeToMesh() )
9530 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9532 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9533 while ( smIt->more() ) {
9534 SMESH_subMesh* sm = smIt->next();
9535 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9536 aHelper.SetSubShape( sm->GetSubShape() );
9537 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9542 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9543 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9545 SMESHDS_SubMesh *smDS = 0;
9546 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9547 while(aEdgeItr->more())
9549 const SMDS_MeshEdge* edge = aEdgeItr->next();
9550 if(edge && !edge->IsQuadratic())
9552 int id = edge->GetID();
9553 //MESSAGE("edge->GetID() " << id);
9554 const SMDS_MeshNode* n1 = edge->GetNode(0);
9555 const SMDS_MeshNode* n2 = edge->GetNode(1);
9557 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9559 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9560 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9563 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9564 while(aFaceItr->more())
9566 const SMDS_MeshFace* face = aFaceItr->next();
9567 if(!face || face->IsQuadratic() ) continue;
9569 const int id = face->GetID();
9570 const SMDSAbs_EntityType type = face->GetEntityType();
9571 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9573 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9575 SMDS_MeshFace * NewFace = 0;
9578 case SMDSEntity_Triangle:
9579 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9581 case SMDSEntity_Quadrangle:
9582 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9585 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9587 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9589 vector<int> nbNodeInFaces;
9590 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9591 while(aVolumeItr->more())
9593 const SMDS_MeshVolume* volume = aVolumeItr->next();
9594 if(!volume || volume->IsQuadratic() ) continue;
9596 const int id = volume->GetID();
9597 const SMDSAbs_EntityType type = volume->GetEntityType();
9598 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9599 if ( type == SMDSEntity_Polyhedra )
9600 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9601 else if ( type == SMDSEntity_Hexagonal_Prism )
9602 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9604 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9606 SMDS_MeshVolume * NewVolume = 0;
9609 case SMDSEntity_Tetra:
9610 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9612 case SMDSEntity_Hexa:
9613 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9614 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9616 case SMDSEntity_Pyramid:
9617 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9618 nodes[3], nodes[4], id, theForce3d);
9620 case SMDSEntity_Penta:
9621 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9622 nodes[3], nodes[4], nodes[5], id, theForce3d);
9624 case SMDSEntity_Hexagonal_Prism:
9626 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9628 ReplaceElemInGroups(volume, NewVolume, meshDS);
9633 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9634 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9635 aHelper.FixQuadraticElements();
9639 //================================================================================
9641 * \brief Makes given elements quadratic
9642 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9643 * \param theElements - elements to make quadratic
9645 //================================================================================
9647 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9648 TIDSortedElemSet& theElements)
9650 if ( theElements.empty() ) return;
9652 // we believe that all theElements are of the same type
9653 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9655 // get all nodes shared by theElements
9656 TIDSortedNodeSet allNodes;
9657 TIDSortedElemSet::iterator eIt = theElements.begin();
9658 for ( ; eIt != theElements.end(); ++eIt )
9659 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9661 // complete theElements with elements of lower dim whose all nodes are in allNodes
9663 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9664 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9665 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9666 for ( ; nIt != allNodes.end(); ++nIt )
9668 const SMDS_MeshNode* n = *nIt;
9669 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9670 while ( invIt->more() )
9672 const SMDS_MeshElement* e = invIt->next();
9673 if ( e->IsQuadratic() )
9675 quadAdjacentElems[ e->GetType() ].insert( e );
9678 if ( e->GetType() >= elemType )
9680 continue; // same type of more complex linear element
9683 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9684 continue; // e is already checked
9688 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9689 while ( nodeIt->more() && allIn )
9690 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9692 theElements.insert(e );
9696 SMESH_MesherHelper helper(*myMesh);
9697 helper.SetIsQuadratic( true );
9699 // add links of quadratic adjacent elements to the helper
9701 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9702 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9703 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9705 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9707 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9708 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9709 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9711 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9713 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9714 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9715 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9717 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9720 // make quadratic elements instead of linear ones
9722 SMESHDS_Mesh* meshDS = GetMeshDS();
9723 SMESHDS_SubMesh* smDS = 0;
9724 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9726 const SMDS_MeshElement* elem = *eIt;
9727 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9730 const int id = elem->GetID();
9731 const SMDSAbs_ElementType type = elem->GetType();
9732 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9734 if ( !smDS || !smDS->Contains( elem ))
9735 smDS = meshDS->MeshElements( elem->getshapeId() );
9736 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9738 SMDS_MeshElement * newElem = 0;
9739 switch( nodes.size() )
9741 case 4: // cases for most frequently used element types go first (for optimization)
9742 if ( type == SMDSAbs_Volume )
9743 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9745 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9748 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9749 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9752 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9755 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9758 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9759 nodes[4], id, theForce3d);
9762 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9763 nodes[4], nodes[5], id, theForce3d);
9767 ReplaceElemInGroups( elem, newElem, meshDS);
9768 if( newElem && smDS )
9769 smDS->AddElement( newElem );
9772 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9773 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9774 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9775 helper.FixQuadraticElements();
9779 //=======================================================================
9781 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9782 * \return int - nb of checked elements
9784 //=======================================================================
9786 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9787 SMDS_ElemIteratorPtr theItr,
9788 const int theShapeID)
9791 SMESHDS_Mesh* meshDS = GetMeshDS();
9793 while( theItr->more() )
9795 const SMDS_MeshElement* elem = theItr->next();
9797 if( elem && elem->IsQuadratic())
9799 int id = elem->GetID();
9800 int nbCornerNodes = elem->NbCornerNodes();
9801 SMDSAbs_ElementType aType = elem->GetType();
9803 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9805 //remove a quadratic element
9806 if ( !theSm || !theSm->Contains( elem ))
9807 theSm = meshDS->MeshElements( elem->getshapeId() );
9808 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9810 // remove medium nodes
9811 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9812 if ( nodes[i]->NbInverseElements() == 0 )
9813 meshDS->RemoveFreeNode( nodes[i], theSm );
9815 // add a linear element
9816 nodes.resize( nbCornerNodes );
9817 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9818 ReplaceElemInGroups(elem, newElem, meshDS);
9819 if( theSm && newElem )
9820 theSm->AddElement( newElem );
9826 //=======================================================================
9827 //function : ConvertFromQuadratic
9829 //=======================================================================
9831 bool SMESH_MeshEditor::ConvertFromQuadratic()
9833 int nbCheckedElems = 0;
9834 if ( myMesh->HasShapeToMesh() )
9836 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9838 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9839 while ( smIt->more() ) {
9840 SMESH_subMesh* sm = smIt->next();
9841 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9842 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9848 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9849 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9851 SMESHDS_SubMesh *aSM = 0;
9852 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9860 //================================================================================
9862 * \brief Return true if all medium nodes of the element are in the node set
9864 //================================================================================
9866 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9868 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9869 if ( !nodeSet.count( elem->GetNode(i) ))
9875 //================================================================================
9877 * \brief Makes given elements linear
9879 //================================================================================
9881 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9883 if ( theElements.empty() ) return;
9885 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9886 set<int> mediumNodeIDs;
9887 TIDSortedElemSet::iterator eIt = theElements.begin();
9888 for ( ; eIt != theElements.end(); ++eIt )
9890 const SMDS_MeshElement* e = *eIt;
9891 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9892 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9895 // replace given elements by linear ones
9896 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9897 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9898 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9900 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9901 // except those elements sharing medium nodes of quadratic element whose medium nodes
9902 // are not all in mediumNodeIDs
9904 // get remaining medium nodes
9905 TIDSortedNodeSet mediumNodes;
9906 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9907 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9908 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9909 mediumNodes.insert( mediumNodes.end(), n );
9911 // find more quadratic elements to convert
9912 TIDSortedElemSet moreElemsToConvert;
9913 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9914 for ( ; nIt != mediumNodes.end(); ++nIt )
9916 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9917 while ( invIt->more() )
9919 const SMDS_MeshElement* e = invIt->next();
9920 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9922 // find a more complex element including e and
9923 // whose medium nodes are not in mediumNodes
9924 bool complexFound = false;
9925 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9927 SMDS_ElemIteratorPtr invIt2 =
9928 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9929 while ( invIt2->more() )
9931 const SMDS_MeshElement* eComplex = invIt2->next();
9932 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9934 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9935 if ( nbCommonNodes == e->NbNodes())
9937 complexFound = true;
9938 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9944 if ( !complexFound )
9945 moreElemsToConvert.insert( e );
9949 elemIt = SMDS_ElemIteratorPtr
9950 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9951 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9954 //=======================================================================
9955 //function : SewSideElements
9957 //=======================================================================
9959 SMESH_MeshEditor::Sew_Error
9960 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9961 TIDSortedElemSet& theSide2,
9962 const SMDS_MeshNode* theFirstNode1,
9963 const SMDS_MeshNode* theFirstNode2,
9964 const SMDS_MeshNode* theSecondNode1,
9965 const SMDS_MeshNode* theSecondNode2)
9967 myLastCreatedElems.Clear();
9968 myLastCreatedNodes.Clear();
9970 MESSAGE ("::::SewSideElements()");
9971 if ( theSide1.size() != theSide2.size() )
9972 return SEW_DIFF_NB_OF_ELEMENTS;
9974 Sew_Error aResult = SEW_OK;
9976 // 1. Build set of faces representing each side
9977 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9978 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9980 // =======================================================================
9981 // 1. Build set of faces representing each side:
9982 // =======================================================================
9983 // a. build set of nodes belonging to faces
9984 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9985 // c. create temporary faces representing side of volumes if correspondent
9986 // face does not exist
9988 SMESHDS_Mesh* aMesh = GetMeshDS();
9989 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9990 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9991 TIDSortedElemSet faceSet1, faceSet2;
9992 set<const SMDS_MeshElement*> volSet1, volSet2;
9993 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9994 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9995 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9996 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9997 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9998 int iSide, iFace, iNode;
10000 list<const SMDS_MeshElement* > tempFaceList;
10001 for ( iSide = 0; iSide < 2; iSide++ ) {
10002 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10003 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10004 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10005 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10006 set<const SMDS_MeshElement*>::iterator vIt;
10007 TIDSortedElemSet::iterator eIt;
10008 set<const SMDS_MeshNode*>::iterator nIt;
10010 // check that given nodes belong to given elements
10011 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10012 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10013 int firstIndex = -1, secondIndex = -1;
10014 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10015 const SMDS_MeshElement* elem = *eIt;
10016 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10017 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10018 if ( firstIndex > -1 && secondIndex > -1 ) break;
10020 if ( firstIndex < 0 || secondIndex < 0 ) {
10021 // we can simply return until temporary faces created
10022 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10025 // -----------------------------------------------------------
10026 // 1a. Collect nodes of existing faces
10027 // and build set of face nodes in order to detect missing
10028 // faces corresponding to sides of volumes
10029 // -----------------------------------------------------------
10031 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10033 // loop on the given element of a side
10034 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10035 //const SMDS_MeshElement* elem = *eIt;
10036 const SMDS_MeshElement* elem = *eIt;
10037 if ( elem->GetType() == SMDSAbs_Face ) {
10038 faceSet->insert( elem );
10039 set <const SMDS_MeshNode*> faceNodeSet;
10040 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10041 while ( nodeIt->more() ) {
10042 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10043 nodeSet->insert( n );
10044 faceNodeSet.insert( n );
10046 setOfFaceNodeSet.insert( faceNodeSet );
10048 else if ( elem->GetType() == SMDSAbs_Volume )
10049 volSet->insert( elem );
10051 // ------------------------------------------------------------------------------
10052 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10053 // ------------------------------------------------------------------------------
10055 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10056 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10057 while ( fIt->more() ) { // loop on faces sharing a node
10058 const SMDS_MeshElement* f = fIt->next();
10059 if ( faceSet->find( f ) == faceSet->end() ) {
10060 // check if all nodes are in nodeSet and
10061 // complete setOfFaceNodeSet if they are
10062 set <const SMDS_MeshNode*> faceNodeSet;
10063 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10064 bool allInSet = true;
10065 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10066 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10067 if ( nodeSet->find( n ) == nodeSet->end() )
10070 faceNodeSet.insert( n );
10073 faceSet->insert( f );
10074 setOfFaceNodeSet.insert( faceNodeSet );
10080 // -------------------------------------------------------------------------
10081 // 1c. Create temporary faces representing sides of volumes if correspondent
10082 // face does not exist
10083 // -------------------------------------------------------------------------
10085 if ( !volSet->empty() ) {
10086 //int nodeSetSize = nodeSet->size();
10088 // loop on given volumes
10089 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10090 SMDS_VolumeTool vol (*vIt);
10091 // loop on volume faces: find free faces
10092 // --------------------------------------
10093 list<const SMDS_MeshElement* > freeFaceList;
10094 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10095 if ( !vol.IsFreeFace( iFace ))
10097 // check if there is already a face with same nodes in a face set
10098 const SMDS_MeshElement* aFreeFace = 0;
10099 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10100 int nbNodes = vol.NbFaceNodes( iFace );
10101 set <const SMDS_MeshNode*> faceNodeSet;
10102 vol.GetFaceNodes( iFace, faceNodeSet );
10103 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10105 // no such a face is given but it still can exist, check it
10106 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10107 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10109 if ( !aFreeFace ) {
10110 // create a temporary face
10111 if ( nbNodes == 3 ) {
10112 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10113 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10115 else if ( nbNodes == 4 ) {
10116 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10117 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10120 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10121 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10122 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10125 tempFaceList.push_back( aFreeFace );
10129 freeFaceList.push_back( aFreeFace );
10131 } // loop on faces of a volume
10133 // choose one of several free faces of a volume
10134 // --------------------------------------------
10135 if ( freeFaceList.size() > 1 ) {
10136 // choose a face having max nb of nodes shared by other elems of a side
10137 int maxNbNodes = -1;
10138 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10139 while ( fIt != freeFaceList.end() ) { // loop on free faces
10140 int nbSharedNodes = 0;
10141 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10142 while ( nodeIt->more() ) { // loop on free face nodes
10143 const SMDS_MeshNode* n =
10144 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10145 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10146 while ( invElemIt->more() ) {
10147 const SMDS_MeshElement* e = invElemIt->next();
10148 nbSharedNodes += faceSet->count( e );
10149 nbSharedNodes += elemSet->count( e );
10152 if ( nbSharedNodes > maxNbNodes ) {
10153 maxNbNodes = nbSharedNodes;
10154 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10156 else if ( nbSharedNodes == maxNbNodes ) {
10160 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10163 if ( freeFaceList.size() > 1 )
10165 // could not choose one face, use another way
10166 // choose a face most close to the bary center of the opposite side
10167 gp_XYZ aBC( 0., 0., 0. );
10168 set <const SMDS_MeshNode*> addedNodes;
10169 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10170 eIt = elemSet2->begin();
10171 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10172 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10173 while ( nodeIt->more() ) { // loop on free face nodes
10174 const SMDS_MeshNode* n =
10175 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10176 if ( addedNodes.insert( n ).second )
10177 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10180 aBC /= addedNodes.size();
10181 double minDist = DBL_MAX;
10182 fIt = freeFaceList.begin();
10183 while ( fIt != freeFaceList.end() ) { // loop on free faces
10185 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10186 while ( nodeIt->more() ) { // loop on free face nodes
10187 const SMDS_MeshNode* n =
10188 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10189 gp_XYZ p( n->X(),n->Y(),n->Z() );
10190 dist += ( aBC - p ).SquareModulus();
10192 if ( dist < minDist ) {
10194 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10197 fIt = freeFaceList.erase( fIt++ );
10200 } // choose one of several free faces of a volume
10202 if ( freeFaceList.size() == 1 ) {
10203 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10204 faceSet->insert( aFreeFace );
10205 // complete a node set with nodes of a found free face
10206 // for ( iNode = 0; iNode < ; iNode++ )
10207 // nodeSet->insert( fNodes[ iNode ] );
10210 } // loop on volumes of a side
10212 // // complete a set of faces if new nodes in a nodeSet appeared
10213 // // ----------------------------------------------------------
10214 // if ( nodeSetSize != nodeSet->size() ) {
10215 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10216 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10217 // while ( fIt->more() ) { // loop on faces sharing a node
10218 // const SMDS_MeshElement* f = fIt->next();
10219 // if ( faceSet->find( f ) == faceSet->end() ) {
10220 // // check if all nodes are in nodeSet and
10221 // // complete setOfFaceNodeSet if they are
10222 // set <const SMDS_MeshNode*> faceNodeSet;
10223 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10224 // bool allInSet = true;
10225 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10226 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10227 // if ( nodeSet->find( n ) == nodeSet->end() )
10228 // allInSet = false;
10230 // faceNodeSet.insert( n );
10232 // if ( allInSet ) {
10233 // faceSet->insert( f );
10234 // setOfFaceNodeSet.insert( faceNodeSet );
10240 } // Create temporary faces, if there are volumes given
10243 if ( faceSet1.size() != faceSet2.size() ) {
10244 // delete temporary faces: they are in reverseElements of actual nodes
10245 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10246 // while ( tmpFaceIt->more() )
10247 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10248 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10249 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10250 // aMesh->RemoveElement(*tmpFaceIt);
10251 MESSAGE("Diff nb of faces");
10252 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10255 // ============================================================
10256 // 2. Find nodes to merge:
10257 // bind a node to remove to a node to put instead
10258 // ============================================================
10260 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10261 if ( theFirstNode1 != theFirstNode2 )
10262 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10263 if ( theSecondNode1 != theSecondNode2 )
10264 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10266 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10267 set< long > linkIdSet; // links to process
10268 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10270 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10271 list< NLink > linkList[2];
10272 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10273 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10274 // loop on links in linkList; find faces by links and append links
10275 // of the found faces to linkList
10276 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10277 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10279 NLink link[] = { *linkIt[0], *linkIt[1] };
10280 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10281 if ( !linkIdSet.count( linkID ) )
10284 // by links, find faces in the face sets,
10285 // and find indices of link nodes in the found faces;
10286 // in a face set, there is only one or no face sharing a link
10287 // ---------------------------------------------------------------
10289 const SMDS_MeshElement* face[] = { 0, 0 };
10290 vector<const SMDS_MeshNode*> fnodes[2];
10291 int iLinkNode[2][2];
10292 TIDSortedElemSet avoidSet;
10293 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10294 const SMDS_MeshNode* n1 = link[iSide].first;
10295 const SMDS_MeshNode* n2 = link[iSide].second;
10296 //cout << "Side " << iSide << " ";
10297 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10298 // find a face by two link nodes
10299 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10300 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10301 if ( face[ iSide ])
10303 //cout << " F " << face[ iSide]->GetID() <<endl;
10304 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10305 // put face nodes to fnodes
10306 if ( face[ iSide ]->IsQuadratic() )
10308 // use interlaced nodes iterator
10309 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10310 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10311 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10312 while ( nIter->more() )
10313 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10317 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10318 face[ iSide ]->end_nodes() );
10320 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10324 // check similarity of elements of the sides
10325 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10326 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10327 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10328 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10331 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10333 break; // do not return because it's necessary to remove tmp faces
10336 // set nodes to merge
10337 // -------------------
10339 if ( face[0] && face[1] ) {
10340 const int nbNodes = face[0]->NbNodes();
10341 if ( nbNodes != face[1]->NbNodes() ) {
10342 MESSAGE("Diff nb of face nodes");
10343 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10344 break; // do not return because it s necessary to remove tmp faces
10346 bool reverse[] = { false, false }; // order of nodes in the link
10347 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10348 // analyse link orientation in faces
10349 int i1 = iLinkNode[ iSide ][ 0 ];
10350 int i2 = iLinkNode[ iSide ][ 1 ];
10351 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10353 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10354 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10355 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10357 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10358 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10361 // add other links of the faces to linkList
10362 // -----------------------------------------
10364 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10365 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10366 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10367 if ( !iter_isnew.second ) { // already in a set: no need to process
10368 linkIdSet.erase( iter_isnew.first );
10370 else // new in set == encountered for the first time: add
10372 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10373 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10374 linkList[0].push_back ( NLink( n1, n2 ));
10375 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10380 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10383 } // loop on link lists
10385 if ( aResult == SEW_OK &&
10386 ( //linkIt[0] != linkList[0].end() ||
10387 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10388 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10389 " " << (faceSetPtr[1]->empty()));
10390 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10393 // ====================================================================
10394 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10395 // ====================================================================
10397 // delete temporary faces
10398 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10399 // while ( tmpFaceIt->more() )
10400 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10401 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10402 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10403 aMesh->RemoveElement(*tmpFaceIt);
10405 if ( aResult != SEW_OK)
10408 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10409 // loop on nodes replacement map
10410 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10411 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10412 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10413 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10414 nodeIDsToRemove.push_back( nToRemove->GetID() );
10415 // loop on elements sharing nToRemove
10416 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10417 while ( invElemIt->more() ) {
10418 const SMDS_MeshElement* e = invElemIt->next();
10419 // get a new suite of nodes: make replacement
10420 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10421 vector< const SMDS_MeshNode*> nodes( nbNodes );
10422 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10423 while ( nIt->more() ) {
10424 const SMDS_MeshNode* n =
10425 static_cast<const SMDS_MeshNode*>( nIt->next() );
10426 nnIt = nReplaceMap.find( n );
10427 if ( nnIt != nReplaceMap.end() ) {
10429 n = (*nnIt).second;
10433 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10434 // elemIDsToRemove.push_back( e->GetID() );
10438 SMDSAbs_ElementType etyp = e->GetType();
10439 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10442 myLastCreatedElems.Append(newElem);
10443 AddToSameGroups(newElem, e, aMesh);
10444 int aShapeId = e->getshapeId();
10447 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10450 aMesh->RemoveElement(e);
10455 Remove( nodeIDsToRemove, true );
10460 //================================================================================
10462 * \brief Find corresponding nodes in two sets of faces
10463 * \param theSide1 - first face set
10464 * \param theSide2 - second first face
10465 * \param theFirstNode1 - a boundary node of set 1
10466 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10467 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10468 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10469 * \param nReplaceMap - output map of corresponding nodes
10470 * \return bool - is a success or not
10472 //================================================================================
10475 //#define DEBUG_MATCHING_NODES
10478 SMESH_MeshEditor::Sew_Error
10479 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10480 set<const SMDS_MeshElement*>& theSide2,
10481 const SMDS_MeshNode* theFirstNode1,
10482 const SMDS_MeshNode* theFirstNode2,
10483 const SMDS_MeshNode* theSecondNode1,
10484 const SMDS_MeshNode* theSecondNode2,
10485 TNodeNodeMap & nReplaceMap)
10487 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10489 nReplaceMap.clear();
10490 if ( theFirstNode1 != theFirstNode2 )
10491 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10492 if ( theSecondNode1 != theSecondNode2 )
10493 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10495 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10496 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10498 list< NLink > linkList[2];
10499 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10500 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10502 // loop on links in linkList; find faces by links and append links
10503 // of the found faces to linkList
10504 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10505 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10506 NLink link[] = { *linkIt[0], *linkIt[1] };
10507 if ( linkSet.find( link[0] ) == linkSet.end() )
10510 // by links, find faces in the face sets,
10511 // and find indices of link nodes in the found faces;
10512 // in a face set, there is only one or no face sharing a link
10513 // ---------------------------------------------------------------
10515 const SMDS_MeshElement* face[] = { 0, 0 };
10516 list<const SMDS_MeshNode*> notLinkNodes[2];
10517 //bool reverse[] = { false, false }; // order of notLinkNodes
10519 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10521 const SMDS_MeshNode* n1 = link[iSide].first;
10522 const SMDS_MeshNode* n2 = link[iSide].second;
10523 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10524 set< const SMDS_MeshElement* > facesOfNode1;
10525 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10527 // during a loop of the first node, we find all faces around n1,
10528 // during a loop of the second node, we find one face sharing both n1 and n2
10529 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10530 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10531 while ( fIt->more() ) { // loop on faces sharing a node
10532 const SMDS_MeshElement* f = fIt->next();
10533 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10534 ! facesOfNode1.insert( f ).second ) // f encounters twice
10536 if ( face[ iSide ] ) {
10537 MESSAGE( "2 faces per link " );
10538 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10541 faceSet->erase( f );
10543 // get not link nodes
10544 int nbN = f->NbNodes();
10545 if ( f->IsQuadratic() )
10547 nbNodes[ iSide ] = nbN;
10548 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10549 int i1 = f->GetNodeIndex( n1 );
10550 int i2 = f->GetNodeIndex( n2 );
10551 int iEnd = nbN, iBeg = -1, iDelta = 1;
10552 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10554 std::swap( iEnd, iBeg ); iDelta = -1;
10559 if ( i == iEnd ) i = iBeg + iDelta;
10560 if ( i == i1 ) break;
10561 nodes.push_back ( f->GetNode( i ) );
10567 // check similarity of elements of the sides
10568 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10569 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10570 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10571 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10574 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10578 // set nodes to merge
10579 // -------------------
10581 if ( face[0] && face[1] ) {
10582 if ( nbNodes[0] != nbNodes[1] ) {
10583 MESSAGE("Diff nb of face nodes");
10584 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10586 #ifdef DEBUG_MATCHING_NODES
10587 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10588 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10589 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10591 int nbN = nbNodes[0];
10593 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10594 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10595 for ( int i = 0 ; i < nbN - 2; ++i ) {
10596 #ifdef DEBUG_MATCHING_NODES
10597 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10599 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10603 // add other links of the face 1 to linkList
10604 // -----------------------------------------
10606 const SMDS_MeshElement* f0 = face[0];
10607 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10608 for ( int i = 0; i < nbN; i++ )
10610 const SMDS_MeshNode* n2 = f0->GetNode( i );
10611 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10612 linkSet.insert( SMESH_TLink( n1, n2 ));
10613 if ( !iter_isnew.second ) { // already in a set: no need to process
10614 linkSet.erase( iter_isnew.first );
10616 else // new in set == encountered for the first time: add
10618 #ifdef DEBUG_MATCHING_NODES
10619 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10620 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10622 linkList[0].push_back ( NLink( n1, n2 ));
10623 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10628 } // loop on link lists
10633 //================================================================================
10635 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10636 \param theElems - the list of elements (edges or faces) to be replicated
10637 The nodes for duplication could be found from these elements
10638 \param theNodesNot - list of nodes to NOT replicate
10639 \param theAffectedElems - the list of elements (cells and edges) to which the
10640 replicated nodes should be associated to.
10641 \return TRUE if operation has been completed successfully, FALSE otherwise
10643 //================================================================================
10645 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10646 const TIDSortedElemSet& theNodesNot,
10647 const TIDSortedElemSet& theAffectedElems )
10649 myLastCreatedElems.Clear();
10650 myLastCreatedNodes.Clear();
10652 if ( theElems.size() == 0 )
10655 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10660 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10661 // duplicate elements and nodes
10662 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10663 // replce nodes by duplications
10664 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10668 //================================================================================
10670 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10671 \param theMeshDS - mesh instance
10672 \param theElems - the elements replicated or modified (nodes should be changed)
10673 \param theNodesNot - nodes to NOT replicate
10674 \param theNodeNodeMap - relation of old node to new created node
10675 \param theIsDoubleElem - flag os to replicate element or modify
10676 \return TRUE if operation has been completed successfully, FALSE otherwise
10678 //================================================================================
10680 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10681 const TIDSortedElemSet& theElems,
10682 const TIDSortedElemSet& theNodesNot,
10683 std::map< const SMDS_MeshNode*,
10684 const SMDS_MeshNode* >& theNodeNodeMap,
10685 const bool theIsDoubleElem )
10687 MESSAGE("doubleNodes");
10688 // iterate on through element and duplicate them (by nodes duplication)
10690 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10691 for ( ; elemItr != theElems.end(); ++elemItr )
10693 const SMDS_MeshElement* anElem = *elemItr;
10697 bool isDuplicate = false;
10698 // duplicate nodes to duplicate element
10699 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10700 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10702 while ( anIter->more() )
10705 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10706 SMDS_MeshNode* aNewNode = aCurrNode;
10707 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10708 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10709 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10712 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10713 theNodeNodeMap[ aCurrNode ] = aNewNode;
10714 myLastCreatedNodes.Append( aNewNode );
10716 isDuplicate |= (aCurrNode != aNewNode);
10717 newNodes[ ind++ ] = aNewNode;
10719 if ( !isDuplicate )
10722 if ( theIsDoubleElem )
10723 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10726 MESSAGE("ChangeElementNodes");
10727 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10734 //================================================================================
10736 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10737 \param theNodes - identifiers of nodes to be doubled
10738 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10739 nodes. If list of element identifiers is empty then nodes are doubled but
10740 they not assigned to elements
10741 \return TRUE if operation has been completed successfully, FALSE otherwise
10743 //================================================================================
10745 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10746 const std::list< int >& theListOfModifiedElems )
10748 MESSAGE("DoubleNodes");
10749 myLastCreatedElems.Clear();
10750 myLastCreatedNodes.Clear();
10752 if ( theListOfNodes.size() == 0 )
10755 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10759 // iterate through nodes and duplicate them
10761 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10763 std::list< int >::const_iterator aNodeIter;
10764 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10766 int aCurr = *aNodeIter;
10767 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10773 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10776 anOldNodeToNewNode[ aNode ] = aNewNode;
10777 myLastCreatedNodes.Append( aNewNode );
10781 // Create map of new nodes for modified elements
10783 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10785 std::list< int >::const_iterator anElemIter;
10786 for ( anElemIter = theListOfModifiedElems.begin();
10787 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10789 int aCurr = *anElemIter;
10790 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10794 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10796 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10798 while ( anIter->more() )
10800 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10801 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10803 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10804 aNodeArr[ ind++ ] = aNewNode;
10807 aNodeArr[ ind++ ] = aCurrNode;
10809 anElemToNodes[ anElem ] = aNodeArr;
10812 // Change nodes of elements
10814 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10815 anElemToNodesIter = anElemToNodes.begin();
10816 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10818 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10819 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10822 MESSAGE("ChangeElementNodes");
10823 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10832 //================================================================================
10834 \brief Check if element located inside shape
10835 \return TRUE if IN or ON shape, FALSE otherwise
10837 //================================================================================
10839 template<class Classifier>
10840 bool isInside(const SMDS_MeshElement* theElem,
10841 Classifier& theClassifier,
10842 const double theTol)
10844 gp_XYZ centerXYZ (0, 0, 0);
10845 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10846 while (aNodeItr->more())
10847 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10849 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10850 theClassifier.Perform(aPnt, theTol);
10851 TopAbs_State aState = theClassifier.State();
10852 return (aState == TopAbs_IN || aState == TopAbs_ON );
10855 //================================================================================
10857 * \brief Classifier of the 3D point on the TopoDS_Face
10858 * with interaface suitable for isInside()
10860 //================================================================================
10862 struct _FaceClassifier
10864 Extrema_ExtPS _extremum;
10865 BRepAdaptor_Surface _surface;
10866 TopAbs_State _state;
10868 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10870 _extremum.Initialize( _surface,
10871 _surface.FirstUParameter(), _surface.LastUParameter(),
10872 _surface.FirstVParameter(), _surface.LastVParameter(),
10873 _surface.Tolerance(), _surface.Tolerance() );
10875 void Perform(const gp_Pnt& aPnt, double theTol)
10877 _state = TopAbs_OUT;
10878 _extremum.Perform(aPnt);
10879 if ( _extremum.IsDone() )
10880 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10881 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10882 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10884 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10887 TopAbs_State State() const
10894 //================================================================================
10896 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10897 \param theElems - group of of elements (edges or faces) to be replicated
10898 \param theNodesNot - group of nodes not to replicate
10899 \param theShape - shape to detect affected elements (element which geometric center
10900 located on or inside shape).
10901 The replicated nodes should be associated to affected elements.
10902 \return TRUE if operation has been completed successfully, FALSE otherwise
10904 //================================================================================
10906 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10907 const TIDSortedElemSet& theNodesNot,
10908 const TopoDS_Shape& theShape )
10910 if ( theShape.IsNull() )
10913 const double aTol = Precision::Confusion();
10914 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10915 auto_ptr<_FaceClassifier> aFaceClassifier;
10916 if ( theShape.ShapeType() == TopAbs_SOLID )
10918 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10919 bsc3d->PerformInfinitePoint(aTol);
10921 else if (theShape.ShapeType() == TopAbs_FACE )
10923 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10926 // iterates on indicated elements and get elements by back references from their nodes
10927 TIDSortedElemSet anAffected;
10928 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10929 for ( ; elemItr != theElems.end(); ++elemItr )
10931 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10935 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10936 while ( nodeItr->more() )
10938 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10939 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10941 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10942 while ( backElemItr->more() )
10944 const SMDS_MeshElement* curElem = backElemItr->next();
10945 if ( curElem && theElems.find(curElem) == theElems.end() &&
10947 isInside( curElem, *bsc3d, aTol ) :
10948 isInside( curElem, *aFaceClassifier, aTol )))
10949 anAffected.insert( curElem );
10953 return DoubleNodes( theElems, theNodesNot, anAffected );
10957 * \brief compute an oriented angle between two planes defined by four points.
10958 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10959 * @param p0 base of the rotation axe
10960 * @param p1 extremity of the rotation axe
10961 * @param g1 belongs to the first plane
10962 * @param g2 belongs to the second plane
10964 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10966 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10967 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10968 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10969 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10970 gp_Vec vref(p0, p1);
10973 gp_Vec n1 = vref.Crossed(v1);
10974 gp_Vec n2 = vref.Crossed(v2);
10975 return n2.AngleWithRef(n1, vref);
10979 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10980 * The list of groups must describe a partition of the mesh volumes.
10981 * The nodes of the internal faces at the boundaries of the groups are doubled.
10982 * In option, the internal faces are replaced by flat elements.
10983 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10984 * The flat elements are stored in groups of volumes.
10985 * @param theElems - list of groups of volumes, where a group of volume is a set of
10986 * SMDS_MeshElements sorted by Id.
10987 * @param createJointElems - if TRUE, create the elements
10988 * @return TRUE if operation has been completed successfully, FALSE otherwise
10990 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10991 bool createJointElems)
10993 MESSAGE("----------------------------------------------");
10994 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10995 MESSAGE("----------------------------------------------");
10997 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10998 meshDS->BuildDownWardConnectivity(true);
11000 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11002 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11003 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11004 // build the list of nodes shared by 2 or more domains, with their domain indexes
11006 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11007 std::map<int,int>celldom; // cell vtkId --> domain
11008 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11009 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11010 faceDomains.clear();
11012 cellDomains.clear();
11013 nodeDomains.clear();
11014 std::map<int,int> emptyMap;
11015 std::set<int> emptySet;
11018 for (int idom = 0; idom < theElems.size(); idom++)
11021 // --- build a map (face to duplicate --> volume to modify)
11022 // with all the faces shared by 2 domains (group of elements)
11023 // and corresponding volume of this domain, for each shared face.
11024 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
11026 //MESSAGE("Domain " << idom);
11027 const TIDSortedElemSet& domain = theElems[idom];
11028 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11029 for (; elemItr != domain.end(); ++elemItr)
11031 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11034 int vtkId = anElem->getVtkId();
11035 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11036 int neighborsVtkIds[NBMAXNEIGHBORS];
11037 int downIds[NBMAXNEIGHBORS];
11038 unsigned char downTypes[NBMAXNEIGHBORS];
11039 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11040 for (int n = 0; n < nbNeighbors; n++)
11042 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11043 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11044 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11046 DownIdType face(downIds[n], downTypes[n]);
11047 if (!faceDomains.count(face))
11048 faceDomains[face] = emptyMap; // create an empty entry for face
11049 if (!faceDomains[face].count(idom))
11051 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11052 celldom[vtkId] = idom;
11053 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11060 //MESSAGE("Number of shared faces " << faceDomains.size());
11061 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11063 // --- explore the shared faces domain by domain,
11064 // explore the nodes of the face and see if they belong to a cell in the domain,
11065 // which has only a node or an edge on the border (not a shared face)
11067 for (int idomain = 0; idomain < theElems.size(); idomain++)
11069 //MESSAGE("Domain " << idomain);
11070 const TIDSortedElemSet& domain = theElems[idomain];
11071 itface = faceDomains.begin();
11072 for (; itface != faceDomains.end(); ++itface)
11074 std::map<int, int> domvol = itface->second;
11075 if (!domvol.count(idomain))
11077 DownIdType face = itface->first;
11078 //MESSAGE(" --- face " << face.cellId);
11079 std::set<int> oldNodes;
11081 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11082 std::set<int>::iterator itn = oldNodes.begin();
11083 for (; itn != oldNodes.end(); ++itn)
11086 //MESSAGE(" node " << oldId);
11087 std::set<int> cells;
11089 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11090 for (int i=0; i<l.ncells; i++)
11092 int vtkId = l.cells[i];
11093 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11094 if (!domain.count(anElem))
11096 int vtkType = grid->GetCellType(vtkId);
11097 int downId = grid->CellIdToDownId(vtkId);
11100 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11101 continue; // not OK at this stage of the algorithm:
11102 //no cells created after BuildDownWardConnectivity
11104 DownIdType aCell(downId, vtkType);
11105 if (celldom.count(vtkId))
11107 cellDomains[aCell][idomain] = vtkId;
11108 celldom[vtkId] = idomain;
11109 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11115 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11116 // for each shared face, get the nodes
11117 // for each node, for each domain of the face, create a clone of the node
11119 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11120 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11121 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11123 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11124 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11125 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11127 for (int idomain = 0; idomain < theElems.size(); idomain++)
11129 itface = faceDomains.begin();
11130 for (; itface != faceDomains.end(); ++itface)
11132 std::map<int, int> domvol = itface->second;
11133 if (!domvol.count(idomain))
11135 DownIdType face = itface->first;
11136 //MESSAGE(" --- face " << face.cellId);
11137 std::set<int> oldNodes;
11139 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11140 bool isMultipleDetected = false;
11141 std::set<int>::iterator itn = oldNodes.begin();
11142 for (; itn != oldNodes.end(); ++itn)
11145 //MESSAGE(" node " << oldId);
11146 if (!nodeDomains.count(oldId))
11147 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11148 if (nodeDomains[oldId].empty())
11149 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11150 std::map<int, int>::iterator itdom = domvol.begin();
11151 for (; itdom != domvol.end(); ++itdom)
11153 int idom = itdom->first;
11154 //MESSAGE(" domain " << idom);
11155 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11157 if (nodeDomains[oldId].size() >= 2) // a multiple node
11159 vector<int> orderedDoms;
11160 //MESSAGE("multiple node " << oldId);
11161 isMultipleDetected =true;
11162 if (mutipleNodes.count(oldId))
11163 orderedDoms = mutipleNodes[oldId];
11166 map<int,int>::iterator it = nodeDomains[oldId].begin();
11167 for (; it != nodeDomains[oldId].end(); ++it)
11168 orderedDoms.push_back(it->first);
11170 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11171 //stringstream txt;
11172 //for (int i=0; i<orderedDoms.size(); i++)
11173 // txt << orderedDoms[i] << " ";
11174 //MESSAGE("orderedDoms " << txt.str());
11175 mutipleNodes[oldId] = orderedDoms;
11177 double *coords = grid->GetPoint(oldId);
11178 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11179 int newId = newNode->getVtkId();
11180 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11181 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
11183 if (nodeDomains[oldId].size() >= 3)
11185 //MESSAGE("confirm multiple node " << oldId);
11186 isMultipleDetected =true;
11190 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
11192 //MESSAGE("multiple Nodes detected on a shared face");
11193 int downId = itface->first.cellId;
11194 unsigned char cellType = itface->first.cellType;
11195 // --- shared edge or shared face ?
11196 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11199 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11200 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11201 if (mutipleNodes.count(nodes[i]))
11202 if (!mutipleNodesToFace.count(nodes[i]))
11203 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11205 else // shared face (between two volumes)
11207 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11208 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11209 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11210 for (int ie =0; ie < nbEdges; ie++)
11213 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11214 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11216 vector<int> vn0 = mutipleNodes[nodes[0]];
11217 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11218 sort( vn0.begin(), vn0.end() );
11219 sort( vn1.begin(), vn1.end() );
11222 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11223 double *coords = grid->GetPoint(nodes[0]);
11224 gp_Pnt p0(coords[0], coords[1], coords[2]);
11225 coords = grid->GetPoint(nodes[nbNodes - 1]);
11226 gp_Pnt p1(coords[0], coords[1], coords[2]);
11228 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11229 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11230 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11231 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11232 for (int id=0; id < vn0.size(); id++)
11234 int idom = vn0[id];
11235 for (int ivol=0; ivol<nbvol; ivol++)
11237 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11238 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11239 if (theElems[idom].count(elem))
11241 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11242 domvol[idom] = svol;
11243 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11245 vtkIdType npts = 0;
11246 vtkIdType* pts = 0;
11247 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11248 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11251 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11252 angleDom[idom] = 0;
11256 gp_Pnt g(values[0], values[1], values[2]);
11257 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11258 //MESSAGE(" angle=" << angleDom[idom]);
11264 map<double, int> sortedDom; // sort domains by angle
11265 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11266 sortedDom[ia->second] = ia->first;
11267 vector<int> vnodes;
11269 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11271 vdom.push_back(ib->second);
11272 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11274 for (int ino = 0; ino < nbNodes; ino++)
11275 vnodes.push_back(nodes[ino]);
11276 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11285 // --- iterate on shared faces (volumes to modify, face to extrude)
11286 // get node id's of the face (id SMDS = id VTK)
11287 // create flat element with old and new nodes if requested
11289 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11290 // (domain1 X domain2) = domain1 + MAXINT*domain2
11292 std::map<int, std::map<long,int> > nodeQuadDomains;
11293 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11295 if (createJointElems)
11297 itface = faceDomains.begin();
11298 for (; itface != faceDomains.end(); ++itface)
11300 DownIdType face = itface->first;
11301 std::set<int> oldNodes;
11302 std::set<int>::iterator itn;
11304 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11306 std::map<int, int> domvol = itface->second;
11307 std::map<int, int>::iterator itdom = domvol.begin();
11308 int dom1 = itdom->first;
11309 int vtkVolId = itdom->second;
11311 int dom2 = itdom->first;
11312 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11314 stringstream grpname;
11317 grpname << dom1 << "_" << dom2;
11319 grpname << dom2 << "_" << dom1;
11321 string namegrp = grpname.str();
11322 if (!mapOfJunctionGroups.count(namegrp))
11323 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11324 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11326 sgrp->Add(vol->GetID());
11330 // --- create volumes on multiple domain intersection if requested
11331 // iterate on mutipleNodesToFace
11332 // iterate on edgesMultiDomains
11334 if (createJointElems)
11336 // --- iterate on mutipleNodesToFace
11338 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11339 for (; itn != mutipleNodesToFace.end(); ++itn)
11341 int node = itn->first;
11342 vector<int> orderDom = itn->second;
11343 vector<vtkIdType> orderedNodes;
11344 for (int idom = 0; idom <orderDom.size(); idom++)
11345 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11346 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11348 stringstream grpname;
11350 grpname << 0 << "_" << 0;
11352 string namegrp = grpname.str();
11353 if (!mapOfJunctionGroups.count(namegrp))
11354 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11355 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11357 sgrp->Add(face->GetID());
11360 // --- iterate on edgesMultiDomains
11362 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11363 for (; ite != edgesMultiDomains.end(); ++ite)
11365 vector<int> nodes = ite->first;
11366 vector<int> orderDom = ite->second;
11367 vector<vtkIdType> orderedNodes;
11368 if (nodes.size() == 2)
11370 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11371 for (int ino=0; ino < nodes.size(); ino++)
11372 if (orderDom.size() == 3)
11373 for (int idom = 0; idom <orderDom.size(); idom++)
11374 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11376 for (int idom = orderDom.size()-1; idom >=0; idom--)
11377 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11378 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11380 stringstream grpname;
11382 grpname << 0 << "_" << 0;
11384 string namegrp = grpname.str();
11385 if (!mapOfJunctionGroups.count(namegrp))
11386 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11387 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11389 sgrp->Add(vol->GetID());
11393 MESSAGE("Quadratic multiple joints not implemented");
11394 // TODO quadratic nodes
11399 // --- list the explicit faces and edges of the mesh that need to be modified,
11400 // i.e. faces and edges built with one or more duplicated nodes.
11401 // associate these faces or edges to their corresponding domain.
11402 // only the first domain found is kept when a face or edge is shared
11404 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11405 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11406 faceOrEdgeDom.clear();
11409 for (int idomain = 0; idomain < theElems.size(); idomain++)
11411 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11412 for (; itnod != nodeDomains.end(); ++itnod)
11414 int oldId = itnod->first;
11415 //MESSAGE(" node " << oldId);
11416 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11417 for (int i = 0; i < l.ncells; i++)
11419 int vtkId = l.cells[i];
11420 int vtkType = grid->GetCellType(vtkId);
11421 int downId = grid->CellIdToDownId(vtkId);
11423 continue; // new cells: not to be modified
11424 DownIdType aCell(downId, vtkType);
11425 int volParents[1000];
11426 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11427 for (int j = 0; j < nbvol; j++)
11428 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11429 if (!feDom.count(vtkId))
11431 feDom[vtkId] = idomain;
11432 faceOrEdgeDom[aCell] = emptyMap;
11433 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11434 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11435 // << " type " << vtkType << " downId " << downId);
11441 // --- iterate on shared faces (volumes to modify, face to extrude)
11442 // get node id's of the face
11443 // replace old nodes by new nodes in volumes, and update inverse connectivity
11445 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11446 for (int m=0; m<3; m++)
11448 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11449 itface = (*amap).begin();
11450 for (; itface != (*amap).end(); ++itface)
11452 DownIdType face = itface->first;
11453 std::set<int> oldNodes;
11454 std::set<int>::iterator itn;
11456 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11457 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11458 std::map<int, int> localClonedNodeIds;
11460 std::map<int, int> domvol = itface->second;
11461 std::map<int, int>::iterator itdom = domvol.begin();
11462 for (; itdom != domvol.end(); ++itdom)
11464 int idom = itdom->first;
11465 int vtkVolId = itdom->second;
11466 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11467 localClonedNodeIds.clear();
11468 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11471 if (nodeDomains[oldId].count(idom))
11473 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11474 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11477 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11482 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11483 grid->BuildLinks();
11491 * \brief Double nodes on some external faces and create flat elements.
11492 * Flat elements are mainly used by some types of mechanic calculations.
11494 * Each group of the list must be constituted of faces.
11495 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11496 * @param theElems - list of groups of faces, where a group of faces is a set of
11497 * SMDS_MeshElements sorted by Id.
11498 * @return TRUE if operation has been completed successfully, FALSE otherwise
11500 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11502 MESSAGE("-------------------------------------------------");
11503 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11504 MESSAGE("-------------------------------------------------");
11506 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11508 // --- For each group of faces
11509 // duplicate the nodes, create a flat element based on the face
11510 // replace the nodes of the faces by their clones
11512 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11513 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11514 clonedNodes.clear();
11515 intermediateNodes.clear();
11516 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11517 mapOfJunctionGroups.clear();
11519 for (int idom = 0; idom < theElems.size(); idom++)
11521 const TIDSortedElemSet& domain = theElems[idom];
11522 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11523 for (; elemItr != domain.end(); ++elemItr)
11525 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11526 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11529 // MESSAGE("aFace=" << aFace->GetID());
11530 bool isQuad = aFace->IsQuadratic();
11531 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11533 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11535 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11536 while (nodeIt->more())
11538 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11539 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11541 ln2.push_back(node);
11543 ln0.push_back(node);
11545 const SMDS_MeshNode* clone = 0;
11546 if (!clonedNodes.count(node))
11548 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11549 clonedNodes[node] = clone;
11552 clone = clonedNodes[node];
11555 ln3.push_back(clone);
11557 ln1.push_back(clone);
11559 const SMDS_MeshNode* inter = 0;
11560 if (isQuad && (!isMedium))
11562 if (!intermediateNodes.count(node))
11564 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11565 intermediateNodes[node] = inter;
11568 inter = intermediateNodes[node];
11569 ln4.push_back(inter);
11573 // --- extrude the face
11575 vector<const SMDS_MeshNode*> ln;
11576 SMDS_MeshVolume* vol = 0;
11577 vtkIdType aType = aFace->GetVtkType();
11581 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11582 // MESSAGE("vol prism " << vol->GetID());
11583 ln.push_back(ln1[0]);
11584 ln.push_back(ln1[1]);
11585 ln.push_back(ln1[2]);
11588 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11589 // MESSAGE("vol hexa " << vol->GetID());
11590 ln.push_back(ln1[0]);
11591 ln.push_back(ln1[1]);
11592 ln.push_back(ln1[2]);
11593 ln.push_back(ln1[3]);
11595 case VTK_QUADRATIC_TRIANGLE:
11596 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11597 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11598 // MESSAGE("vol quad prism " << vol->GetID());
11599 ln.push_back(ln1[0]);
11600 ln.push_back(ln1[1]);
11601 ln.push_back(ln1[2]);
11602 ln.push_back(ln3[0]);
11603 ln.push_back(ln3[1]);
11604 ln.push_back(ln3[2]);
11606 case VTK_QUADRATIC_QUAD:
11607 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11608 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11609 // ln4[0], ln4[1], ln4[2], ln4[3]);
11610 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11611 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11612 ln4[0], ln4[1], ln4[2], ln4[3]);
11613 // MESSAGE("vol quad hexa " << vol->GetID());
11614 ln.push_back(ln1[0]);
11615 ln.push_back(ln1[1]);
11616 ln.push_back(ln1[2]);
11617 ln.push_back(ln1[3]);
11618 ln.push_back(ln3[0]);
11619 ln.push_back(ln3[1]);
11620 ln.push_back(ln3[2]);
11621 ln.push_back(ln3[3]);
11631 stringstream grpname;
11635 string namegrp = grpname.str();
11636 if (!mapOfJunctionGroups.count(namegrp))
11637 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11638 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11640 sgrp->Add(vol->GetID());
11643 // --- modify the face
11645 aFace->ChangeNodes(&ln[0], ln.size());
11651 //================================================================================
11653 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11654 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11655 * \return TRUE if operation has been completed successfully, FALSE otherwise
11657 //================================================================================
11659 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11661 // iterates on volume elements and detect all free faces on them
11662 SMESHDS_Mesh* aMesh = GetMeshDS();
11665 //bool res = false;
11666 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11667 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11670 const SMDS_MeshVolume* volume = vIt->next();
11671 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11672 vTool.SetExternalNormal();
11673 //const bool isPoly = volume->IsPoly();
11674 const int iQuad = volume->IsQuadratic();
11675 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11677 if (!vTool.IsFreeFace(iface))
11680 vector<const SMDS_MeshNode *> nodes;
11681 int nbFaceNodes = vTool.NbFaceNodes(iface);
11682 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11684 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11685 nodes.push_back(faceNodes[inode]);
11686 if (iQuad) { // add medium nodes
11687 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11688 nodes.push_back(faceNodes[inode]);
11689 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11690 nodes.push_back(faceNodes[8]);
11692 // add new face based on volume nodes
11693 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11695 continue; // face already exsist
11697 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11701 return ( nbFree==(nbExisted+nbCreated) );
11706 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11708 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11710 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11713 //================================================================================
11715 * \brief Creates missing boundary elements
11716 * \param elements - elements whose boundary is to be checked
11717 * \param dimension - defines type of boundary elements to create
11718 * \param group - a group to store created boundary elements in
11719 * \param targetMesh - a mesh to store created boundary elements in
11720 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11721 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11722 * boundary elements will be copied into the targetMesh
11723 * \param toAddExistingBondary - if true, not only new but also pre-existing
11724 * boundary elements will be added into the new group
11725 * \param aroundElements - if true, elements will be created on boundary of given
11726 * elements else, on boundary of the whole mesh.
11727 * \return nb of added boundary elements
11729 //================================================================================
11731 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11732 Bnd_Dimension dimension,
11733 SMESH_Group* group/*=0*/,
11734 SMESH_Mesh* targetMesh/*=0*/,
11735 bool toCopyElements/*=false*/,
11736 bool toCopyExistingBoundary/*=false*/,
11737 bool toAddExistingBondary/*= false*/,
11738 bool aroundElements/*= false*/)
11740 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11741 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11742 // hope that all elements are of the same type, do not check them all
11743 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11744 throw SALOME_Exception(LOCALIZED("wrong element type"));
11747 toCopyElements = toCopyExistingBoundary = false;
11749 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11750 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11751 int nbAddedBnd = 0;
11753 // editor adding present bnd elements and optionally holding elements to add to the group
11754 SMESH_MeshEditor* presentEditor;
11755 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11756 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11758 SMESH_MesherHelper helper( *myMesh );
11759 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11760 SMDS_VolumeTool vTool;
11761 TIDSortedElemSet avoidSet;
11762 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11765 typedef vector<const SMDS_MeshNode*> TConnectivity;
11767 SMDS_ElemIteratorPtr eIt;
11768 if (elements.empty())
11769 eIt = aMesh->elementsIterator(elemType);
11771 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11773 while (eIt->more())
11775 const SMDS_MeshElement* elem = eIt->next();
11776 const int iQuad = elem->IsQuadratic();
11778 // ------------------------------------------------------------------------------------
11779 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11780 // ------------------------------------------------------------------------------------
11781 vector<const SMDS_MeshElement*> presentBndElems;
11782 vector<TConnectivity> missingBndElems;
11783 TConnectivity nodes;
11784 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11786 vTool.SetExternalNormal();
11787 const SMDS_MeshElement* otherVol = 0;
11788 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11790 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11791 ( !aroundElements || elements.count( otherVol )))
11793 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11794 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11795 if ( missType == SMDSAbs_Edge ) // boundary edges
11797 nodes.resize( 2+iQuad );
11798 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11800 for ( int j = 0; j < nodes.size(); ++j )
11802 if ( const SMDS_MeshElement* edge =
11803 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11804 presentBndElems.push_back( edge );
11806 missingBndElems.push_back( nodes );
11809 else // boundary face
11812 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11813 nodes.push_back( nn[inode] );
11814 if (iQuad) // add medium nodes
11815 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11816 nodes.push_back( nn[inode] );
11817 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11819 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11821 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11822 SMDSAbs_Face, /*noMedium=*/false ))
11823 presentBndElems.push_back( f );
11825 missingBndElems.push_back( nodes );
11827 if ( targetMesh != myMesh )
11829 // add 1D elements on face boundary to be added to a new mesh
11830 const SMDS_MeshElement* edge;
11831 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11834 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11836 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11837 if ( edge && avoidSet.insert( edge ).second )
11838 presentBndElems.push_back( edge );
11844 else // elem is a face ------------------------------------------
11846 avoidSet.clear(), avoidSet.insert( elem );
11847 int nbNodes = elem->NbCornerNodes();
11848 nodes.resize( 2 /*+ iQuad*/);
11849 for ( int i = 0; i < nbNodes; i++ )
11851 nodes[0] = elem->GetNode(i);
11852 nodes[1] = elem->GetNode((i+1)%nbNodes);
11853 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11854 continue; // not free link
11857 //nodes[2] = elem->GetNode( i + nbNodes );
11858 if ( const SMDS_MeshElement* edge =
11859 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11860 presentBndElems.push_back( edge );
11862 missingBndElems.push_back( nodes );
11866 // ---------------------------------
11867 // 2. Add missing boundary elements
11868 // ---------------------------------
11869 if ( targetMesh != myMesh )
11870 // instead of making a map of nodes in this mesh and targetMesh,
11871 // we create nodes with same IDs.
11872 for ( int i = 0; i < missingBndElems.size(); ++i )
11874 TConnectivity& srcNodes = missingBndElems[i];
11875 TConnectivity nodes( srcNodes.size() );
11876 for ( inode = 0; inode < nodes.size(); ++inode )
11877 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11878 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11880 /*noMedium=*/false))
11882 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11886 for ( int i = 0; i < missingBndElems.size(); ++i )
11888 TConnectivity& nodes = missingBndElems[i];
11889 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11891 /*noMedium=*/false))
11893 SMDS_MeshElement* elem =
11894 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11897 // try to set a new element to a shape
11898 if ( myMesh->HasShapeToMesh() )
11901 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11902 const int nbN = nodes.size() / (iQuad+1 );
11903 for ( inode = 0; inode < nbN && ok; ++inode )
11905 pair<int, TopAbs_ShapeEnum> i_stype =
11906 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11907 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11908 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11910 if ( ok && mediumShapes.size() > 1 )
11912 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11913 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11914 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11916 if (( ok = ( stype_i->first != stype_i_0.first )))
11917 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11918 aMesh->IndexToShape( stype_i_0.second ));
11921 if ( ok && mediumShapes.begin()->first == missShapeType )
11922 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11926 // ----------------------------------
11927 // 3. Copy present boundary elements
11928 // ----------------------------------
11929 if ( toCopyExistingBoundary )
11930 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11932 const SMDS_MeshElement* e = presentBndElems[i];
11933 TConnectivity nodes( e->NbNodes() );
11934 for ( inode = 0; inode < nodes.size(); ++inode )
11935 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11936 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11938 else // store present elements to add them to a group
11939 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11941 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11944 } // loop on given elements
11946 // ---------------------------------------------
11947 // 4. Fill group with boundary elements
11948 // ---------------------------------------------
11951 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11952 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11953 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11955 tgtEditor.myLastCreatedElems.Clear();
11956 tgtEditor2.myLastCreatedElems.Clear();
11958 // -----------------------
11959 // 5. Copy given elements
11960 // -----------------------
11961 if ( toCopyElements && targetMesh != myMesh )
11963 if (elements.empty())
11964 eIt = aMesh->elementsIterator(elemType);
11966 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11967 while (eIt->more())
11969 const SMDS_MeshElement* elem = eIt->next();
11970 TConnectivity nodes( elem->NbNodes() );
11971 for ( inode = 0; inode < nodes.size(); ++inode )
11972 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11973 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11975 tgtEditor.myLastCreatedElems.Clear();