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() );
5991 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
5998 //================================================================================
6000 * \brief Return list of group of nodes close to each other within theTolerance
6001 * Search among theNodes or in the whole mesh if theNodes is empty using
6002 * an Octree algorithm
6004 //================================================================================
6006 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6007 const double theTolerance,
6008 TListOfListOfNodes & theGroupsOfNodes)
6010 myLastCreatedElems.Clear();
6011 myLastCreatedNodes.Clear();
6013 if ( theNodes.empty() )
6014 { // get all nodes in the mesh
6015 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6016 while ( nIt->more() )
6017 theNodes.insert( theNodes.end(),nIt->next());
6020 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6024 //=======================================================================
6026 * \brief Implementation of search for the node closest to point
6028 //=======================================================================
6030 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6032 //---------------------------------------------------------------------
6034 * \brief Constructor
6036 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6038 myMesh = ( SMESHDS_Mesh* ) theMesh;
6040 TIDSortedNodeSet nodes;
6042 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6043 while ( nIt->more() )
6044 nodes.insert( nodes.end(), nIt->next() );
6046 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6048 // get max size of a leaf box
6049 SMESH_OctreeNode* tree = myOctreeNode;
6050 while ( !tree->isLeaf() )
6052 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6056 myHalfLeafSize = tree->maxSize() / 2.;
6059 //---------------------------------------------------------------------
6061 * \brief Move node and update myOctreeNode accordingly
6063 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6065 myOctreeNode->UpdateByMoveNode( node, toPnt );
6066 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6069 //---------------------------------------------------------------------
6071 * \brief Do it's job
6073 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6075 map<double, const SMDS_MeshNode*> dist2Nodes;
6076 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6077 if ( !dist2Nodes.empty() )
6078 return dist2Nodes.begin()->second;
6079 list<const SMDS_MeshNode*> nodes;
6080 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6082 double minSqDist = DBL_MAX;
6083 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6085 // sort leafs by their distance from thePnt
6086 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6087 TDistTreeMap treeMap;
6088 list< SMESH_OctreeNode* > treeList;
6089 list< SMESH_OctreeNode* >::iterator trIt;
6090 treeList.push_back( myOctreeNode );
6092 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6093 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6094 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6096 SMESH_OctreeNode* tree = *trIt;
6097 if ( !tree->isLeaf() ) // put children to the queue
6099 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6100 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6101 while ( cIt->more() )
6102 treeList.push_back( cIt->next() );
6104 else if ( tree->NbNodes() ) // put a tree to the treeMap
6106 const Bnd_B3d& box = tree->getBox();
6107 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6108 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6109 if ( !it_in.second ) // not unique distance to box center
6110 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6113 // find distance after which there is no sense to check tree's
6114 double sqLimit = DBL_MAX;
6115 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6116 if ( treeMap.size() > 5 ) {
6117 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6118 const Bnd_B3d& box = closestTree->getBox();
6119 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6120 sqLimit = limit * limit;
6122 // get all nodes from trees
6123 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6124 if ( sqDist_tree->first > sqLimit )
6126 SMESH_OctreeNode* tree = sqDist_tree->second;
6127 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6130 // find closest among nodes
6131 minSqDist = DBL_MAX;
6132 const SMDS_MeshNode* closestNode = 0;
6133 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6134 for ( ; nIt != nodes.end(); ++nIt ) {
6135 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6136 if ( minSqDist > sqDist ) {
6144 //---------------------------------------------------------------------
6148 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6150 //---------------------------------------------------------------------
6152 * \brief Return the node tree
6154 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6157 SMESH_OctreeNode* myOctreeNode;
6158 SMESHDS_Mesh* myMesh;
6159 double myHalfLeafSize; // max size of a leaf box
6162 //=======================================================================
6164 * \brief Return SMESH_NodeSearcher
6166 //=======================================================================
6168 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6170 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6173 // ========================================================================
6174 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6176 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6177 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6178 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6180 //=======================================================================
6182 * \brief Octal tree of bounding boxes of elements
6184 //=======================================================================
6186 class ElementBndBoxTree : public SMESH_Octree
6190 ElementBndBoxTree(const SMDS_Mesh& mesh,
6191 SMDSAbs_ElementType elemType,
6192 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6193 double tolerance = NodeRadius );
6194 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6195 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6196 void getElementsInSphere ( const gp_XYZ& center,
6197 const double radius, TIDSortedElemSet& foundElems);
6198 size_t getSize() { return std::max( _size, _elements.size() ); }
6199 ~ElementBndBoxTree();
6202 ElementBndBoxTree():_size(0) {}
6203 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6204 void buildChildrenData();
6205 Bnd_B3d* buildRootBox();
6207 //!< Bounding box of element
6208 struct ElementBox : public Bnd_B3d
6210 const SMDS_MeshElement* _element;
6211 int _refCount; // an ElementBox can be included in several tree branches
6212 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6214 vector< ElementBox* > _elements;
6218 //================================================================================
6220 * \brief ElementBndBoxTree creation
6222 //================================================================================
6224 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6225 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6227 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6228 _elements.reserve( nbElems );
6230 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6231 while ( elemIt->more() )
6232 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6237 //================================================================================
6241 //================================================================================
6243 ElementBndBoxTree::~ElementBndBoxTree()
6245 for ( int i = 0; i < _elements.size(); ++i )
6246 if ( --_elements[i]->_refCount <= 0 )
6247 delete _elements[i];
6250 //================================================================================
6252 * \brief Return the maximal box
6254 //================================================================================
6256 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6258 Bnd_B3d* box = new Bnd_B3d;
6259 for ( int i = 0; i < _elements.size(); ++i )
6260 box->Add( *_elements[i] );
6264 //================================================================================
6266 * \brief Redistrubute element boxes among children
6268 //================================================================================
6270 void ElementBndBoxTree::buildChildrenData()
6272 for ( int i = 0; i < _elements.size(); ++i )
6274 for (int j = 0; j < 8; j++)
6276 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6278 _elements[i]->_refCount++;
6279 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6282 _elements[i]->_refCount--;
6284 _size = _elements.size();
6285 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6287 for (int j = 0; j < 8; j++)
6289 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6290 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6291 child->myIsLeaf = true;
6293 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6294 SMESHUtils::CompactVector( child->_elements );
6298 //================================================================================
6300 * \brief Return elements which can include the point
6302 //================================================================================
6304 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6305 TIDSortedElemSet& foundElems)
6307 if ( getBox().IsOut( point.XYZ() ))
6312 for ( int i = 0; i < _elements.size(); ++i )
6313 if ( !_elements[i]->IsOut( point.XYZ() ))
6314 foundElems.insert( _elements[i]->_element );
6318 for (int i = 0; i < 8; i++)
6319 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6323 //================================================================================
6325 * \brief Return elements which can be intersected by the line
6327 //================================================================================
6329 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6330 TIDSortedElemSet& foundElems)
6332 if ( getBox().IsOut( line ))
6337 for ( int i = 0; i < _elements.size(); ++i )
6338 if ( !_elements[i]->IsOut( line ))
6339 foundElems.insert( _elements[i]->_element );
6343 for (int i = 0; i < 8; i++)
6344 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6348 //================================================================================
6350 * \brief Return elements from leaves intersecting the sphere
6352 //================================================================================
6354 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6355 const double radius,
6356 TIDSortedElemSet& foundElems)
6358 if ( getBox().IsOut( center, radius ))
6363 for ( int i = 0; i < _elements.size(); ++i )
6364 if ( !_elements[i]->IsOut( center, radius ))
6365 foundElems.insert( _elements[i]->_element );
6369 for (int i = 0; i < 8; i++)
6370 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6374 //================================================================================
6376 * \brief Construct the element box
6378 //================================================================================
6380 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6384 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6385 while ( nIt->more() )
6386 Add( SMESH_TNodeXYZ( nIt->next() ));
6387 Enlarge( tolerance );
6392 //=======================================================================
6394 * \brief Implementation of search for the elements by point and
6395 * of classification of point in 2D mesh
6397 //=======================================================================
6399 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6401 SMESHDS_Mesh* _mesh;
6402 SMDS_ElemIteratorPtr _meshPartIt;
6403 ElementBndBoxTree* _ebbTree;
6404 SMESH_NodeSearcherImpl* _nodeSearcher;
6405 SMDSAbs_ElementType _elementType;
6407 bool _outerFacesFound;
6408 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6410 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6411 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6412 ~SMESH_ElementSearcherImpl()
6414 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6415 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6417 virtual int FindElementsByPoint(const gp_Pnt& point,
6418 SMDSAbs_ElementType type,
6419 vector< const SMDS_MeshElement* >& foundElements);
6420 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6421 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6422 SMDSAbs_ElementType type );
6424 void GetElementsNearLine( const gp_Ax1& line,
6425 SMDSAbs_ElementType type,
6426 vector< const SMDS_MeshElement* >& foundElems);
6427 double getTolerance();
6428 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6429 const double tolerance, double & param);
6430 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6431 bool isOuterBoundary(const SMDS_MeshElement* face) const
6433 return _outerFaces.empty() || _outerFaces.count(face);
6435 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6437 const SMDS_MeshElement* _face;
6439 bool _coincides; //!< the line lays in face plane
6440 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6441 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6443 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6446 TIDSortedElemSet _faces;
6447 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6448 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6452 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6454 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6455 << ", _coincides="<<i._coincides << ")";
6458 //=======================================================================
6460 * \brief define tolerance for search
6462 //=======================================================================
6464 double SMESH_ElementSearcherImpl::getTolerance()
6466 if ( _tolerance < 0 )
6468 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6471 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6473 double boxSize = _nodeSearcher->getTree()->maxSize();
6474 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6476 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6478 double boxSize = _ebbTree->maxSize();
6479 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6481 if ( _tolerance == 0 )
6483 // define tolerance by size of a most complex element
6484 int complexType = SMDSAbs_Volume;
6485 while ( complexType > SMDSAbs_All &&
6486 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6488 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6490 if ( complexType == int( SMDSAbs_Node ))
6492 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6494 if ( meshInfo.NbNodes() > 2 )
6495 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6499 SMDS_ElemIteratorPtr elemIt =
6500 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6501 const SMDS_MeshElement* elem = elemIt->next();
6502 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6503 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6505 while ( nodeIt->more() )
6507 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6508 elemSize = max( dist, elemSize );
6511 _tolerance = 1e-4 * elemSize;
6517 //================================================================================
6519 * \brief Find intersection of the line and an edge of face and return parameter on line
6521 //================================================================================
6523 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6524 const SMDS_MeshElement* face,
6531 GeomAPI_ExtremaCurveCurve anExtCC;
6532 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6534 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6535 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6537 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6538 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6539 anExtCC.Init( lineCurve, edge);
6540 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6542 Quantity_Parameter pl, pe;
6543 anExtCC.LowerDistanceParameters( pl, pe );
6545 if ( ++nbInts == 2 )
6549 if ( nbInts > 0 ) param /= nbInts;
6552 //================================================================================
6554 * \brief Find all faces belonging to the outer boundary of mesh
6556 //================================================================================
6558 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6560 if ( _outerFacesFound ) return;
6562 // Collect all outer faces by passing from one outer face to another via their links
6563 // and BTW find out if there are internal faces at all.
6565 // checked links and links where outer boundary meets internal one
6566 set< SMESH_TLink > visitedLinks, seamLinks;
6568 // links to treat with already visited faces sharing them
6569 list < TFaceLink > startLinks;
6571 // load startLinks with the first outerFace
6572 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6573 _outerFaces.insert( outerFace );
6575 TIDSortedElemSet emptySet;
6576 while ( !startLinks.empty() )
6578 const SMESH_TLink& link = startLinks.front()._link;
6579 TIDSortedElemSet& faces = startLinks.front()._faces;
6581 outerFace = *faces.begin();
6582 // find other faces sharing the link
6583 const SMDS_MeshElement* f;
6584 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6587 // select another outer face among the found
6588 const SMDS_MeshElement* outerFace2 = 0;
6589 if ( faces.size() == 2 )
6591 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6593 else if ( faces.size() > 2 )
6595 seamLinks.insert( link );
6597 // link direction within the outerFace
6598 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6599 SMESH_TNodeXYZ( link.node2()));
6600 int i1 = outerFace->GetNodeIndex( link.node1() );
6601 int i2 = outerFace->GetNodeIndex( link.node2() );
6602 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6603 if ( rev ) n1n2.Reverse();
6605 gp_XYZ ofNorm, fNorm;
6606 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6608 // direction from the link inside outerFace
6609 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6610 // sort all other faces by angle with the dirInOF
6611 map< double, const SMDS_MeshElement* > angle2Face;
6612 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6613 for ( ; face != faces.end(); ++face )
6615 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6617 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6618 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6619 if ( angle < 0 ) angle += 2. * M_PI;
6620 angle2Face.insert( make_pair( angle, *face ));
6622 if ( !angle2Face.empty() )
6623 outerFace2 = angle2Face.begin()->second;
6626 // store the found outer face and add its links to continue seaching from
6629 _outerFaces.insert( outerFace );
6630 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6631 for ( int i = 0; i < nbNodes; ++i )
6633 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6634 if ( visitedLinks.insert( link2 ).second )
6635 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6638 startLinks.pop_front();
6640 _outerFacesFound = true;
6642 if ( !seamLinks.empty() )
6644 // There are internal boundaries touching the outher one,
6645 // find all faces of internal boundaries in order to find
6646 // faces of boundaries of holes, if any.
6651 _outerFaces.clear();
6655 //=======================================================================
6657 * \brief Find elements of given type where the given point is IN or ON.
6658 * Returns nb of found elements and elements them-selves.
6660 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6662 //=======================================================================
6664 int SMESH_ElementSearcherImpl::
6665 FindElementsByPoint(const gp_Pnt& point,
6666 SMDSAbs_ElementType type,
6667 vector< const SMDS_MeshElement* >& foundElements)
6669 foundElements.clear();
6671 double tolerance = getTolerance();
6673 // =================================================================================
6674 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6676 if ( !_nodeSearcher )
6677 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6679 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6680 if ( !closeNode ) return foundElements.size();
6682 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6683 return foundElements.size(); // to far from any node
6685 if ( type == SMDSAbs_Node )
6687 foundElements.push_back( closeNode );
6691 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6692 while ( elemIt->more() )
6693 foundElements.push_back( elemIt->next() );
6696 // =================================================================================
6697 else // elements more complex than 0D
6699 if ( !_ebbTree || _elementType != type )
6701 if ( _ebbTree ) delete _ebbTree;
6702 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6704 TIDSortedElemSet suspectElems;
6705 _ebbTree->getElementsNearPoint( point, suspectElems );
6706 TIDSortedElemSet::iterator elem = suspectElems.begin();
6707 for ( ; elem != suspectElems.end(); ++elem )
6708 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6709 foundElements.push_back( *elem );
6711 return foundElements.size();
6714 //=======================================================================
6716 * \brief Find an element of given type most close to the given point
6718 * WARNING: Only face search is implemeneted so far
6720 //=======================================================================
6722 const SMDS_MeshElement*
6723 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6724 SMDSAbs_ElementType type )
6726 const SMDS_MeshElement* closestElem = 0;
6728 if ( type == SMDSAbs_Face )
6730 if ( !_ebbTree || _elementType != type )
6732 if ( _ebbTree ) delete _ebbTree;
6733 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6735 TIDSortedElemSet suspectElems;
6736 _ebbTree->getElementsNearPoint( point, suspectElems );
6738 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6740 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox().CornerMin() +
6741 _ebbTree->getBox().CornerMax() );
6743 if ( _ebbTree->getBox().IsOut( point.XYZ() ))
6744 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6746 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6747 while ( suspectElems.empty() )
6749 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6753 double minDist = std::numeric_limits<double>::max();
6754 multimap< double, const SMDS_MeshElement* > dist2face;
6755 TIDSortedElemSet::iterator elem = suspectElems.begin();
6756 for ( ; elem != suspectElems.end(); ++elem )
6758 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6760 if ( dist < minDist + 1e-10)
6763 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6766 if ( !dist2face.empty() )
6768 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6769 closestElem = d2f->second;
6770 // if there are several elements at the same distance, select one
6771 // with GC closest to the point
6772 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6773 double minDistToGC = 0;
6774 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6776 if ( minDistToGC == 0 )
6779 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6780 TXyzIterator(), gc ) / closestElem->NbNodes();
6781 minDistToGC = point.SquareDistance( gc );
6784 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6785 TXyzIterator(), gc ) / d2f->second->NbNodes();
6786 double d = point.SquareDistance( gc );
6787 if ( d < minDistToGC )
6790 closestElem = d2f->second;
6793 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6794 // <<closestElem->GetID() << " DIST " << minDist << endl;
6799 // NOT IMPLEMENTED SO FAR
6805 //================================================================================
6807 * \brief Classify the given point in the closed 2D mesh
6809 //================================================================================
6811 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6813 double tolerance = getTolerance();
6814 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6816 if ( _ebbTree ) delete _ebbTree;
6817 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6819 // Algo: analyse transition of a line starting at the point through mesh boundary;
6820 // try three lines parallel to axis of the coordinate system and perform rough
6821 // analysis. If solution is not clear perform thorough analysis.
6823 const int nbAxes = 3;
6824 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6825 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6826 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6827 multimap< int, int > nbInt2Axis; // to find the simplest case
6828 for ( int axis = 0; axis < nbAxes; ++axis )
6830 gp_Ax1 lineAxis( point, axisDir[axis]);
6831 gp_Lin line ( lineAxis );
6833 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6834 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6836 // Intersect faces with the line
6838 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6839 TIDSortedElemSet::iterator face = suspectFaces.begin();
6840 for ( ; face != suspectFaces.end(); ++face )
6844 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6845 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6847 // perform intersection
6848 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6849 if ( !intersection.IsDone() )
6851 if ( intersection.IsInQuadric() )
6853 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6855 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6857 gp_Pnt intersectionPoint = intersection.Point(1);
6858 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6859 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6862 // Analyse intersections roughly
6864 int nbInter = u2inters.size();
6868 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6869 if ( nbInter == 1 ) // not closed mesh
6870 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6872 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6875 if ( (f<0) == (l<0) )
6878 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6879 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6880 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6883 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6885 if ( _outerFacesFound ) break; // pass to thorough analysis
6887 } // three attempts - loop on CS axes
6889 // Analyse intersections thoroughly.
6890 // We make two loops maximum, on the first one we only exclude touching intersections,
6891 // on the second, if situation is still unclear, we gather and use information on
6892 // position of faces (internal or outer). If faces position is already gathered,
6893 // we make the second loop right away.
6895 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6897 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6898 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6900 int axis = nb_axis->second;
6901 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6903 gp_Ax1 lineAxis( point, axisDir[axis]);
6904 gp_Lin line ( lineAxis );
6906 // add tangent intersections to u2inters
6908 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6909 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6910 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6911 u2inters.insert(make_pair( param, *tgtInt ));
6912 tangentInters[ axis ].clear();
6914 // Count intersections before and after the point excluding touching ones.
6915 // If hasPositionInfo we count intersections of outer boundary only
6917 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6918 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6919 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6920 bool ok = ! u_int1->second._coincides;
6921 while ( ok && u_int1 != u2inters.end() )
6923 double u = u_int1->first;
6924 bool touchingInt = false;
6925 if ( ++u_int2 != u2inters.end() )
6927 // skip intersections at the same point (if the line passes through edge or node)
6929 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6935 // skip tangent intersections
6937 const SMDS_MeshElement* prevFace = u_int1->second._face;
6938 while ( ok && u_int2->second._coincides )
6940 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6946 ok = ( u_int2 != u2inters.end() );
6951 // skip intersections at the same point after tangent intersections
6954 double u2 = u_int2->first;
6956 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6962 // decide if we skipped a touching intersection
6963 if ( nbSamePnt + nbTgt > 0 )
6965 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6966 map< double, TInters >::iterator u_int = u_int1;
6967 for ( ; u_int != u_int2; ++u_int )
6969 if ( u_int->second._coincides ) continue;
6970 double dot = u_int->second._faceNorm * line.Direction();
6971 if ( dot > maxDot ) maxDot = dot;
6972 if ( dot < minDot ) minDot = dot;
6974 touchingInt = ( minDot*maxDot < 0 );
6979 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6990 u_int1 = u_int2; // to next intersection
6992 } // loop on intersections with one line
6996 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6999 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
7002 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
7003 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
7005 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7008 if ( (f<0) == (l<0) )
7011 if ( hasPositionInfo )
7012 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7014 } // loop on intersections of the tree lines - thorough analysis
7016 if ( !hasPositionInfo )
7018 // gather info on faces position - is face in the outer boundary or not
7019 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7020 findOuterBoundary( u2inters.begin()->second._face );
7023 } // two attempts - with and w/o faces position info in the mesh
7025 return TopAbs_UNKNOWN;
7028 //=======================================================================
7030 * \brief Return elements possibly intersecting the line
7032 //=======================================================================
7034 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7035 SMDSAbs_ElementType type,
7036 vector< const SMDS_MeshElement* >& foundElems)
7038 if ( !_ebbTree || _elementType != type )
7040 if ( _ebbTree ) delete _ebbTree;
7041 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7043 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7044 _ebbTree->getElementsNearLine( line, suspectFaces );
7045 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7048 //=======================================================================
7050 * \brief Return SMESH_ElementSearcher
7052 //=======================================================================
7054 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7056 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7059 //=======================================================================
7061 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7063 //=======================================================================
7065 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7067 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7070 //=======================================================================
7072 * \brief Return true if the point is IN or ON of the element
7074 //=======================================================================
7076 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7078 if ( element->GetType() == SMDSAbs_Volume)
7080 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7083 // get ordered nodes
7085 vector< gp_XYZ > xyz;
7086 vector<const SMDS_MeshNode*> nodeList;
7088 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7089 if ( element->IsQuadratic() ) {
7090 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7091 nodeIt = f->interlacedNodesElemIterator();
7092 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7093 nodeIt = e->interlacedNodesElemIterator();
7095 while ( nodeIt->more() )
7097 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7098 xyz.push_back( SMESH_TNodeXYZ(node) );
7099 nodeList.push_back(node);
7102 int i, nbNodes = element->NbNodes();
7104 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7106 // compute face normal
7107 gp_Vec faceNorm(0,0,0);
7108 xyz.push_back( xyz.front() );
7109 nodeList.push_back( nodeList.front() );
7110 for ( i = 0; i < nbNodes; ++i )
7112 gp_Vec edge1( xyz[i+1], xyz[i]);
7113 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7114 faceNorm += edge1 ^ edge2;
7116 double normSize = faceNorm.Magnitude();
7117 if ( normSize <= tol )
7119 // degenerated face: point is out if it is out of all face edges
7120 for ( i = 0; i < nbNodes; ++i )
7122 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7123 if ( !IsOut( &edge, point, tol ))
7128 faceNorm /= normSize;
7130 // check if the point lays on face plane
7131 gp_Vec n2p( xyz[0], point );
7132 if ( fabs( n2p * faceNorm ) > tol )
7133 return true; // not on face plane
7135 // check if point is out of face boundary:
7136 // define it by closest transition of a ray point->infinity through face boundary
7137 // on the face plane.
7138 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7139 // to find intersections of the ray with the boundary.
7141 gp_Vec plnNorm = ray ^ faceNorm;
7142 normSize = plnNorm.Magnitude();
7143 if ( normSize <= tol ) return false; // point coincides with the first node
7144 plnNorm /= normSize;
7145 // for each node of the face, compute its signed distance to the plane
7146 vector<double> dist( nbNodes + 1);
7147 for ( i = 0; i < nbNodes; ++i )
7149 gp_Vec n2p( xyz[i], point );
7150 dist[i] = n2p * plnNorm;
7152 dist.back() = dist.front();
7153 // find the closest intersection
7155 double rClosest, distClosest = 1e100;;
7157 for ( i = 0; i < nbNodes; ++i )
7160 if ( fabs( dist[i]) < tol )
7162 else if ( fabs( dist[i+1]) < tol )
7164 else if ( dist[i] * dist[i+1] < 0 )
7165 r = dist[i] / ( dist[i] - dist[i+1] );
7167 continue; // no intersection
7168 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7169 gp_Vec p2int ( point, pInt);
7170 if ( p2int * ray > -tol ) // right half-space
7172 double intDist = p2int.SquareMagnitude();
7173 if ( intDist < distClosest )
7178 distClosest = intDist;
7183 return true; // no intesections - out
7185 // analyse transition
7186 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7187 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7188 gp_Vec p2int ( point, pClosest );
7189 bool out = (edgeNorm * p2int) < -tol;
7190 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7193 // ray pass through a face node; analyze transition through an adjacent edge
7194 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7195 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7196 gp_Vec edgeAdjacent( p1, p2 );
7197 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7198 bool out2 = (edgeNorm2 * p2int) < -tol;
7200 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7201 return covexCorner ? (out || out2) : (out && out2);
7203 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7205 // point is out of edge if it is NOT ON any straight part of edge
7206 // (we consider quadratic edge as being composed of two straight parts)
7207 for ( i = 1; i < nbNodes; ++i )
7209 gp_Vec edge( xyz[i-1], xyz[i]);
7210 gp_Vec n1p ( xyz[i-1], point);
7211 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7214 gp_Vec n2p( xyz[i], point );
7215 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7217 return false; // point is ON this part
7221 // Node or 0D element -------------------------------------------------------------------------
7223 gp_Vec n2p ( xyz[0], point );
7224 return n2p.Magnitude() <= tol;
7229 //=======================================================================
7233 // Position of a point relative to a segment
7237 // VERTEX 1 o----ON-----> VERTEX 2
7241 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7242 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7246 int _index; // index of vertex or segment
7248 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7249 bool operator < (const PointPos& other ) const
7251 if ( _name == other._name )
7252 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7253 return _name < other._name;
7257 //================================================================================
7259 * \brief Return of a point relative to a segment
7260 * \param point2D - the point to analyze position of
7261 * \param xyVec - end points of segments
7262 * \param index0 - 0-based index of the first point of segment
7263 * \param posToFindOut - flags of positions to detect
7264 * \retval PointPos - point position
7266 //================================================================================
7268 PointPos getPointPosition( const gp_XY& point2D,
7269 const gp_XY* segEnds,
7270 const int index0 = 0,
7271 const int posToFindOut = POS_ALL)
7273 const gp_XY& p1 = segEnds[ index0 ];
7274 const gp_XY& p2 = segEnds[ index0+1 ];
7275 const gp_XY grad = p2 - p1;
7277 if ( posToFindOut & POS_VERTEX )
7279 // check if the point2D is at "vertex 1" zone
7280 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7281 p1.Y() + grad.X() ) };
7282 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7283 return PointPos( POS_VERTEX, index0 );
7285 // check if the point2D is at "vertex 2" zone
7286 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7287 p2.Y() + grad.X() ) };
7288 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7289 return PointPos( POS_VERTEX, index0 + 1);
7291 double edgeEquation =
7292 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7293 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7297 //=======================================================================
7299 * \brief Return minimal distance from a point to a face
7301 * Currently we ignore non-planarity and 2nd order of face
7303 //=======================================================================
7305 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7306 const gp_Pnt& point )
7308 double badDistance = -1;
7309 if ( !face ) return badDistance;
7311 // coordinates of nodes (medium nodes, if any, ignored)
7312 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7313 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7314 xyz.resize( face->NbCornerNodes()+1 );
7316 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7317 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7319 gp_Vec OZ ( xyz[0], xyz[1] );
7320 gp_Vec OX ( xyz[0], xyz[2] );
7321 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7323 if ( xyz.size() < 4 ) return badDistance;
7324 OZ = gp_Vec ( xyz[0], xyz[2] );
7325 OX = gp_Vec ( xyz[0], xyz[3] );
7329 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7331 catch ( Standard_Failure ) {
7334 trsf.SetTransformation( tgtCS );
7336 // move all the nodes to 2D
7337 vector<gp_XY> xy( xyz.size() );
7338 for ( size_t i = 0;i < xyz.size()-1; ++i )
7340 gp_XYZ p3d = xyz[i];
7341 trsf.Transforms( p3d );
7342 xy[i].SetCoord( p3d.X(), p3d.Z() );
7344 xyz.back() = xyz.front();
7345 xy.back() = xy.front();
7347 // // move the point in 2D
7348 gp_XYZ tmpPnt = point.XYZ();
7349 trsf.Transforms( tmpPnt );
7350 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7352 // loop on segments of the face to analyze point position ralative to the face
7353 set< PointPos > pntPosSet;
7354 for ( size_t i = 1; i < xy.size(); ++i )
7356 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7357 pntPosSet.insert( pos );
7361 PointPos pos = *pntPosSet.begin();
7362 // cout << "Face " << face->GetID() << " DIST: ";
7363 switch ( pos._name )
7366 // point is most close to a segment
7367 gp_Vec p0p1( point, xyz[ pos._index ] );
7368 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7370 double projDist = p0p1 * p1p2; // distance projected to the segment
7371 gp_Vec projVec = p1p2 * projDist;
7372 gp_Vec distVec = p0p1 - projVec;
7373 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7374 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7375 return distVec.Magnitude();
7378 // point is inside the face
7379 double distToFacePlane = tmpPnt.Y();
7380 // cout << distToFacePlane << ", INSIDE " << endl;
7381 return Abs( distToFacePlane );
7384 // point is most close to a node
7385 gp_Vec distVec( point, xyz[ pos._index ]);
7386 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7387 return distVec.Magnitude();
7393 //=======================================================================
7394 //function : SimplifyFace
7396 //=======================================================================
7397 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7398 vector<const SMDS_MeshNode *>& poly_nodes,
7399 vector<int>& quantities) const
7401 int nbNodes = faceNodes.size();
7406 set<const SMDS_MeshNode*> nodeSet;
7408 // get simple seq of nodes
7409 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7410 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7411 int iSimple = 0, nbUnique = 0;
7413 simpleNodes[iSimple++] = faceNodes[0];
7415 for (int iCur = 1; iCur < nbNodes; iCur++) {
7416 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7417 simpleNodes[iSimple++] = faceNodes[iCur];
7418 if (nodeSet.insert( faceNodes[iCur] ).second)
7422 int nbSimple = iSimple;
7423 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7433 bool foundLoop = (nbSimple > nbUnique);
7436 set<const SMDS_MeshNode*> loopSet;
7437 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7438 const SMDS_MeshNode* n = simpleNodes[iSimple];
7439 if (!loopSet.insert( n ).second) {
7443 int iC = 0, curLast = iSimple;
7444 for (; iC < curLast; iC++) {
7445 if (simpleNodes[iC] == n) break;
7447 int loopLen = curLast - iC;
7449 // create sub-element
7451 quantities.push_back(loopLen);
7452 for (; iC < curLast; iC++) {
7453 poly_nodes.push_back(simpleNodes[iC]);
7456 // shift the rest nodes (place from the first loop position)
7457 for (iC = curLast + 1; iC < nbSimple; iC++) {
7458 simpleNodes[iC - loopLen] = simpleNodes[iC];
7460 nbSimple -= loopLen;
7463 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7464 } // while (foundLoop)
7468 quantities.push_back(iSimple);
7469 for (int i = 0; i < iSimple; i++)
7470 poly_nodes.push_back(simpleNodes[i]);
7476 //=======================================================================
7477 //function : MergeNodes
7478 //purpose : In each group, the cdr of nodes are substituted by the first one
7480 //=======================================================================
7482 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7484 MESSAGE("MergeNodes");
7485 myLastCreatedElems.Clear();
7486 myLastCreatedNodes.Clear();
7488 SMESHDS_Mesh* aMesh = GetMeshDS();
7490 TNodeNodeMap nodeNodeMap; // node to replace - new node
7491 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7492 list< int > rmElemIds, rmNodeIds;
7494 // Fill nodeNodeMap and elems
7496 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7497 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7498 list<const SMDS_MeshNode*>& nodes = *grIt;
7499 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7500 const SMDS_MeshNode* nToKeep = *nIt;
7501 //MESSAGE("node to keep " << nToKeep->GetID());
7502 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7503 const SMDS_MeshNode* nToRemove = *nIt;
7504 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7505 if ( nToRemove != nToKeep ) {
7506 //MESSAGE(" node to remove " << nToRemove->GetID());
7507 rmNodeIds.push_back( nToRemove->GetID() );
7508 AddToSameGroups( nToKeep, nToRemove, aMesh );
7511 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7512 while ( invElemIt->more() ) {
7513 const SMDS_MeshElement* elem = invElemIt->next();
7518 // Change element nodes or remove an element
7520 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7521 for ( ; eIt != elems.end(); eIt++ ) {
7522 const SMDS_MeshElement* elem = *eIt;
7523 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7524 int nbNodes = elem->NbNodes();
7525 int aShapeId = FindShape( elem );
7527 set<const SMDS_MeshNode*> nodeSet;
7528 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7529 int iUnique = 0, iCur = 0, nbRepl = 0;
7530 vector<int> iRepl( nbNodes );
7532 // get new seq of nodes
7533 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7534 while ( itN->more() ) {
7535 const SMDS_MeshNode* n =
7536 static_cast<const SMDS_MeshNode*>( itN->next() );
7538 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7539 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7541 // BUG 0020185: begin
7543 bool stopRecur = false;
7544 set<const SMDS_MeshNode*> nodesRecur;
7545 nodesRecur.insert(n);
7546 while (!stopRecur) {
7547 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7548 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7549 n = (*nnIt_i).second;
7550 if (!nodesRecur.insert(n).second) {
7551 // error: recursive dependancy
7561 curNodes[ iCur ] = n;
7562 bool isUnique = nodeSet.insert( n ).second;
7564 uniqueNodes[ iUnique++ ] = n;
7566 iRepl[ nbRepl++ ] = iCur;
7570 // Analyse element topology after replacement
7573 int nbUniqueNodes = nodeSet.size();
7574 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7575 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7576 // Polygons and Polyhedral volumes
7577 if (elem->IsPoly()) {
7579 if (elem->GetType() == SMDSAbs_Face) {
7581 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7583 for (; inode < nbNodes; inode++) {
7584 face_nodes[inode] = curNodes[inode];
7587 vector<const SMDS_MeshNode *> polygons_nodes;
7588 vector<int> quantities;
7589 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7592 for (int iface = 0; iface < nbNew; iface++) {
7593 int nbNodes = quantities[iface];
7594 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7595 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7596 poly_nodes[ii] = polygons_nodes[inode];
7598 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7599 myLastCreatedElems.Append(newElem);
7601 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7604 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7605 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7606 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7608 if (nbNew > 0) quid = nbNew - 1;
7609 vector<int> newquant(quantities.begin()+quid, quantities.end());
7610 const SMDS_MeshElement* newElem = 0;
7611 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7612 myLastCreatedElems.Append(newElem);
7613 if ( aShapeId && newElem )
7614 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7615 rmElemIds.push_back(elem->GetID());
7618 rmElemIds.push_back(elem->GetID());
7622 else if (elem->GetType() == SMDSAbs_Volume) {
7623 // Polyhedral volume
7624 if (nbUniqueNodes < 4) {
7625 rmElemIds.push_back(elem->GetID());
7628 // each face has to be analyzed in order to check volume validity
7629 const SMDS_VtkVolume* aPolyedre =
7630 dynamic_cast<const SMDS_VtkVolume*>( elem );
7632 int nbFaces = aPolyedre->NbFaces();
7634 vector<const SMDS_MeshNode *> poly_nodes;
7635 vector<int> quantities;
7637 for (int iface = 1; iface <= nbFaces; iface++) {
7638 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7639 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7641 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7642 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7643 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7644 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7645 faceNode = (*nnIt).second;
7647 faceNodes[inode - 1] = faceNode;
7650 SimplifyFace(faceNodes, poly_nodes, quantities);
7653 if (quantities.size() > 3) {
7654 // to be done: remove coincident faces
7657 if (quantities.size() > 3)
7659 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7660 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7661 const SMDS_MeshElement* newElem = 0;
7662 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7663 myLastCreatedElems.Append(newElem);
7664 if ( aShapeId && newElem )
7665 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7666 rmElemIds.push_back(elem->GetID());
7670 rmElemIds.push_back(elem->GetID());
7681 // TODO not all the possible cases are solved. Find something more generic?
7682 switch ( nbNodes ) {
7683 case 2: ///////////////////////////////////// EDGE
7684 isOk = false; break;
7685 case 3: ///////////////////////////////////// TRIANGLE
7686 isOk = false; break;
7688 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7690 else { //////////////////////////////////// QUADRANGLE
7691 if ( nbUniqueNodes < 3 )
7693 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7694 isOk = false; // opposite nodes stick
7695 //MESSAGE("isOk " << isOk);
7698 case 6: ///////////////////////////////////// PENTAHEDRON
7699 if ( nbUniqueNodes == 4 ) {
7700 // ---------------------------------> tetrahedron
7702 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7703 // all top nodes stick: reverse a bottom
7704 uniqueNodes[ 0 ] = curNodes [ 1 ];
7705 uniqueNodes[ 1 ] = curNodes [ 0 ];
7707 else if (nbRepl == 3 &&
7708 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7709 // all bottom nodes stick: set a top before
7710 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7711 uniqueNodes[ 0 ] = curNodes [ 3 ];
7712 uniqueNodes[ 1 ] = curNodes [ 4 ];
7713 uniqueNodes[ 2 ] = curNodes [ 5 ];
7715 else if (nbRepl == 4 &&
7716 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7717 // a lateral face turns into a line: reverse a bottom
7718 uniqueNodes[ 0 ] = curNodes [ 1 ];
7719 uniqueNodes[ 1 ] = curNodes [ 0 ];
7724 else if ( nbUniqueNodes == 5 ) {
7725 // PENTAHEDRON --------------------> 2 tetrahedrons
7726 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7727 // a bottom node sticks with a linked top one
7729 SMDS_MeshElement* newElem =
7730 aMesh->AddVolume(curNodes[ 3 ],
7733 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7734 myLastCreatedElems.Append(newElem);
7736 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7737 // 2. : reverse a bottom
7738 uniqueNodes[ 0 ] = curNodes [ 1 ];
7739 uniqueNodes[ 1 ] = curNodes [ 0 ];
7749 if(elem->IsQuadratic()) { // Quadratic quadrangle
7761 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7764 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7766 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7767 uniqueNodes[0] = curNodes[0];
7768 uniqueNodes[1] = curNodes[2];
7769 uniqueNodes[2] = curNodes[3];
7770 uniqueNodes[3] = curNodes[5];
7771 uniqueNodes[4] = curNodes[6];
7772 uniqueNodes[5] = curNodes[7];
7775 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7776 uniqueNodes[0] = curNodes[0];
7777 uniqueNodes[1] = curNodes[1];
7778 uniqueNodes[2] = curNodes[2];
7779 uniqueNodes[3] = curNodes[4];
7780 uniqueNodes[4] = curNodes[5];
7781 uniqueNodes[5] = curNodes[6];
7784 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7785 uniqueNodes[0] = curNodes[1];
7786 uniqueNodes[1] = curNodes[2];
7787 uniqueNodes[2] = curNodes[3];
7788 uniqueNodes[3] = curNodes[5];
7789 uniqueNodes[4] = curNodes[6];
7790 uniqueNodes[5] = curNodes[0];
7793 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7794 uniqueNodes[0] = curNodes[0];
7795 uniqueNodes[1] = curNodes[1];
7796 uniqueNodes[2] = curNodes[3];
7797 uniqueNodes[3] = curNodes[4];
7798 uniqueNodes[4] = curNodes[6];
7799 uniqueNodes[5] = curNodes[7];
7802 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7803 uniqueNodes[0] = curNodes[0];
7804 uniqueNodes[1] = curNodes[2];
7805 uniqueNodes[2] = curNodes[3];
7806 uniqueNodes[3] = curNodes[1];
7807 uniqueNodes[4] = curNodes[6];
7808 uniqueNodes[5] = curNodes[7];
7811 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7812 uniqueNodes[0] = curNodes[0];
7813 uniqueNodes[1] = curNodes[1];
7814 uniqueNodes[2] = curNodes[2];
7815 uniqueNodes[3] = curNodes[4];
7816 uniqueNodes[4] = curNodes[5];
7817 uniqueNodes[5] = curNodes[7];
7820 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7821 uniqueNodes[0] = curNodes[0];
7822 uniqueNodes[1] = curNodes[1];
7823 uniqueNodes[2] = curNodes[3];
7824 uniqueNodes[3] = curNodes[4];
7825 uniqueNodes[4] = curNodes[2];
7826 uniqueNodes[5] = curNodes[7];
7829 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7830 uniqueNodes[0] = curNodes[0];
7831 uniqueNodes[1] = curNodes[1];
7832 uniqueNodes[2] = curNodes[2];
7833 uniqueNodes[3] = curNodes[4];
7834 uniqueNodes[4] = curNodes[5];
7835 uniqueNodes[5] = curNodes[3];
7840 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7843 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7847 //////////////////////////////////// HEXAHEDRON
7849 SMDS_VolumeTool hexa (elem);
7850 hexa.SetExternalNormal();
7851 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7852 //////////////////////// HEX ---> 1 tetrahedron
7853 for ( int iFace = 0; iFace < 6; iFace++ ) {
7854 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7855 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7856 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7857 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7858 // one face turns into a point ...
7859 int iOppFace = hexa.GetOppFaceIndex( iFace );
7860 ind = hexa.GetFaceNodesIndices( iOppFace );
7862 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7863 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7866 if ( nbStick == 1 ) {
7867 // ... and the opposite one - into a triangle.
7869 ind = hexa.GetFaceNodesIndices( iFace );
7870 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7877 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7878 //////////////////////// HEX ---> 1 prism
7879 int nbTria = 0, iTria[3];
7880 const int *ind; // indices of face nodes
7881 // look for triangular faces
7882 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7883 ind = hexa.GetFaceNodesIndices( iFace );
7884 TIDSortedNodeSet faceNodes;
7885 for ( iCur = 0; iCur < 4; iCur++ )
7886 faceNodes.insert( curNodes[ind[iCur]] );
7887 if ( faceNodes.size() == 3 )
7888 iTria[ nbTria++ ] = iFace;
7890 // check if triangles are opposite
7891 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7894 // set nodes of the bottom triangle
7895 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7897 for ( iCur = 0; iCur < 4; iCur++ )
7898 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7899 indB.push_back( ind[iCur] );
7900 if ( !hexa.IsForward() )
7901 std::swap( indB[0], indB[2] );
7902 for ( iCur = 0; iCur < 3; iCur++ )
7903 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7904 // set nodes of the top triangle
7905 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7906 for ( iCur = 0; iCur < 3; ++iCur )
7907 for ( int j = 0; j < 4; ++j )
7908 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7910 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7916 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7917 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7918 for ( int iFace = 0; iFace < 6; iFace++ ) {
7919 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7920 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7921 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7922 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7923 // one face turns into a point ...
7924 int iOppFace = hexa.GetOppFaceIndex( iFace );
7925 ind = hexa.GetFaceNodesIndices( iOppFace );
7927 iUnique = 2; // reverse a tetrahedron 1 bottom
7928 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7929 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7931 else if ( iUnique >= 0 )
7932 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7934 if ( nbStick == 0 ) {
7935 // ... and the opposite one is a quadrangle
7937 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7938 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7941 SMDS_MeshElement* newElem =
7942 aMesh->AddVolume(curNodes[ind[ 0 ]],
7945 curNodes[indTop[ 0 ]]);
7946 myLastCreatedElems.Append(newElem);
7948 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7955 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7956 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7957 // find indices of quad and tri faces
7958 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7959 for ( iFace = 0; iFace < 6; iFace++ ) {
7960 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7962 for ( iCur = 0; iCur < 4; iCur++ )
7963 nodeSet.insert( curNodes[ind[ iCur ]] );
7964 nbUniqueNodes = nodeSet.size();
7965 if ( nbUniqueNodes == 3 )
7966 iTriFace[ nbTri++ ] = iFace;
7967 else if ( nbUniqueNodes == 4 )
7968 iQuadFace[ nbQuad++ ] = iFace;
7970 if (nbQuad == 2 && nbTri == 4 &&
7971 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7972 // 2 opposite quadrangles stuck with a diagonal;
7973 // sample groups of merged indices: (0-4)(2-6)
7974 // --------------------------------------------> 2 tetrahedrons
7975 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7976 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7977 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7978 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7979 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7980 // stuck with 0-2 diagonal
7988 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7989 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7990 // stuck with 1-3 diagonal
8002 uniqueNodes[ 0 ] = curNodes [ i0 ];
8003 uniqueNodes[ 1 ] = curNodes [ i1d ];
8004 uniqueNodes[ 2 ] = curNodes [ i3d ];
8005 uniqueNodes[ 3 ] = curNodes [ i0t ];
8008 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8012 myLastCreatedElems.Append(newElem);
8014 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8017 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8018 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8019 // --------------------------------------------> prism
8020 // find 2 opposite triangles
8022 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8023 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8024 // find indices of kept and replaced nodes
8025 // and fill unique nodes of 2 opposite triangles
8026 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8027 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8028 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8029 // fill unique nodes
8032 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8033 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8034 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8036 // iCur of a linked node of the opposite face (make normals co-directed):
8037 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8038 // check that correspondent corners of triangles are linked
8039 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8042 uniqueNodes[ iUnique ] = n;
8043 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8052 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8055 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8062 } // switch ( nbNodes )
8064 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8066 if ( isOk ) { // the elem remains valid after sticking nodes
8067 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8069 // Change nodes of polyedre
8070 const SMDS_VtkVolume* aPolyedre =
8071 dynamic_cast<const SMDS_VtkVolume*>( elem );
8073 int nbFaces = aPolyedre->NbFaces();
8075 vector<const SMDS_MeshNode *> poly_nodes;
8076 vector<int> quantities (nbFaces);
8078 for (int iface = 1; iface <= nbFaces; iface++) {
8079 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8080 quantities[iface - 1] = nbFaceNodes;
8082 for (inode = 1; inode <= nbFaceNodes; inode++) {
8083 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8085 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8086 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8087 curNode = (*nnIt).second;
8089 poly_nodes.push_back(curNode);
8092 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8095 else // replace non-polyhedron elements
8097 const SMDSAbs_ElementType etyp = elem->GetType();
8098 const int elemId = elem->GetID();
8099 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8100 uniqueNodes.resize(nbUniqueNodes);
8102 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8104 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8105 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8106 if ( sm && newElem )
8107 sm->AddElement( newElem );
8108 if ( elem != newElem )
8109 ReplaceElemInGroups( elem, newElem, aMesh );
8113 // Remove invalid regular element or invalid polygon
8114 rmElemIds.push_back( elem->GetID() );
8117 } // loop on elements
8119 // Remove bad elements, then equal nodes (order important)
8121 Remove( rmElemIds, false );
8122 Remove( rmNodeIds, true );
8127 // ========================================================
8128 // class : SortableElement
8129 // purpose : allow sorting elements basing on their nodes
8130 // ========================================================
8131 class SortableElement : public set <const SMDS_MeshElement*>
8135 SortableElement( const SMDS_MeshElement* theElem )
8138 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8139 while ( nodeIt->more() )
8140 this->insert( nodeIt->next() );
8143 const SMDS_MeshElement* Get() const
8146 void Set(const SMDS_MeshElement* e) const
8151 mutable const SMDS_MeshElement* myElem;
8154 //=======================================================================
8155 //function : FindEqualElements
8156 //purpose : Return list of group of elements built on the same nodes.
8157 // Search among theElements or in the whole mesh if theElements is empty
8158 //=======================================================================
8159 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
8160 TListOfListOfElementsID & theGroupsOfElementsID)
8162 myLastCreatedElems.Clear();
8163 myLastCreatedNodes.Clear();
8165 typedef set<const SMDS_MeshElement*> TElemsSet;
8166 typedef map< SortableElement, int > TMapOfNodeSet;
8167 typedef list<int> TGroupOfElems;
8170 if ( theElements.empty() )
8171 { // get all elements in the mesh
8172 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8173 while ( eIt->more() )
8174 elems.insert( elems.end(), eIt->next());
8177 elems = theElements;
8179 vector< TGroupOfElems > arrayOfGroups;
8180 TGroupOfElems groupOfElems;
8181 TMapOfNodeSet mapOfNodeSet;
8183 TElemsSet::iterator elemIt = elems.begin();
8184 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
8185 const SMDS_MeshElement* curElem = *elemIt;
8186 SortableElement SE(curElem);
8189 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8190 if( !(pp.second) ) {
8191 TMapOfNodeSet::iterator& itSE = pp.first;
8192 ind = (*itSE).second;
8193 arrayOfGroups[ind].push_back(curElem->GetID());
8196 groupOfElems.clear();
8197 groupOfElems.push_back(curElem->GetID());
8198 arrayOfGroups.push_back(groupOfElems);
8203 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8204 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8205 groupOfElems = *groupIt;
8206 if ( groupOfElems.size() > 1 ) {
8207 groupOfElems.sort();
8208 theGroupsOfElementsID.push_back(groupOfElems);
8213 //=======================================================================
8214 //function : MergeElements
8215 //purpose : In each given group, substitute all elements by the first one.
8216 //=======================================================================
8218 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8220 myLastCreatedElems.Clear();
8221 myLastCreatedNodes.Clear();
8223 typedef list<int> TListOfIDs;
8224 TListOfIDs rmElemIds; // IDs of elems to remove
8226 SMESHDS_Mesh* aMesh = GetMeshDS();
8228 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8229 while ( groupsIt != theGroupsOfElementsID.end() ) {
8230 TListOfIDs& aGroupOfElemID = *groupsIt;
8231 aGroupOfElemID.sort();
8232 int elemIDToKeep = aGroupOfElemID.front();
8233 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8234 aGroupOfElemID.pop_front();
8235 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8236 while ( idIt != aGroupOfElemID.end() ) {
8237 int elemIDToRemove = *idIt;
8238 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8239 // add the kept element in groups of removed one (PAL15188)
8240 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8241 rmElemIds.push_back( elemIDToRemove );
8247 Remove( rmElemIds, false );
8250 //=======================================================================
8251 //function : MergeEqualElements
8252 //purpose : Remove all but one of elements built on the same nodes.
8253 //=======================================================================
8255 void SMESH_MeshEditor::MergeEqualElements()
8257 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
8258 to merge equal elements in the whole mesh */
8259 TListOfListOfElementsID aGroupsOfElementsID;
8260 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8261 MergeElements(aGroupsOfElementsID);
8264 //=======================================================================
8265 //function : FindFaceInSet
8266 //purpose : Return a face having linked nodes n1 and n2 and which is
8267 // - not in avoidSet,
8268 // - in elemSet provided that !elemSet.empty()
8269 // i1 and i2 optionally returns indices of n1 and n2
8270 //=======================================================================
8272 const SMDS_MeshElement*
8273 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8274 const SMDS_MeshNode* n2,
8275 const TIDSortedElemSet& elemSet,
8276 const TIDSortedElemSet& avoidSet,
8282 const SMDS_MeshElement* face = 0;
8284 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8285 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8286 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8288 //MESSAGE("in while ( invElemIt->more() && !face )");
8289 const SMDS_MeshElement* elem = invElemIt->next();
8290 if (avoidSet.count( elem ))
8292 if ( !elemSet.empty() && !elemSet.count( elem ))
8295 i1 = elem->GetNodeIndex( n1 );
8296 // find a n2 linked to n1
8297 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8298 for ( int di = -1; di < 2 && !face; di += 2 )
8300 i2 = (i1+di+nbN) % nbN;
8301 if ( elem->GetNode( i2 ) == n2 )
8304 if ( !face && elem->IsQuadratic())
8306 // analysis for quadratic elements using all nodes
8307 const SMDS_VtkFace* F =
8308 dynamic_cast<const SMDS_VtkFace*>(elem);
8309 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8310 // use special nodes iterator
8311 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8312 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8313 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8315 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8316 if ( n1 == prevN && n2 == n )
8320 else if ( n2 == prevN && n1 == n )
8322 face = elem; swap( i1, i2 );
8328 if ( n1ind ) *n1ind = i1;
8329 if ( n2ind ) *n2ind = i2;
8333 //=======================================================================
8334 //function : findAdjacentFace
8336 //=======================================================================
8338 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8339 const SMDS_MeshNode* n2,
8340 const SMDS_MeshElement* elem)
8342 TIDSortedElemSet elemSet, avoidSet;
8344 avoidSet.insert ( elem );
8345 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8348 //=======================================================================
8349 //function : FindFreeBorder
8351 //=======================================================================
8353 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8355 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8356 const SMDS_MeshNode* theSecondNode,
8357 const SMDS_MeshNode* theLastNode,
8358 list< const SMDS_MeshNode* > & theNodes,
8359 list< const SMDS_MeshElement* >& theFaces)
8361 if ( !theFirstNode || !theSecondNode )
8363 // find border face between theFirstNode and theSecondNode
8364 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8368 theFaces.push_back( curElem );
8369 theNodes.push_back( theFirstNode );
8370 theNodes.push_back( theSecondNode );
8372 //vector<const SMDS_MeshNode*> nodes;
8373 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8374 TIDSortedElemSet foundElems;
8375 bool needTheLast = ( theLastNode != 0 );
8377 while ( nStart != theLastNode ) {
8378 if ( nStart == theFirstNode )
8379 return !needTheLast;
8381 // find all free border faces sharing form nStart
8383 list< const SMDS_MeshElement* > curElemList;
8384 list< const SMDS_MeshNode* > nStartList;
8385 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8386 while ( invElemIt->more() ) {
8387 const SMDS_MeshElement* e = invElemIt->next();
8388 if ( e == curElem || foundElems.insert( e ).second ) {
8390 int iNode = 0, nbNodes = e->NbNodes();
8391 //const SMDS_MeshNode* nodes[nbNodes+1];
8392 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8394 if(e->IsQuadratic()) {
8395 const SMDS_VtkFace* F =
8396 dynamic_cast<const SMDS_VtkFace*>(e);
8397 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8398 // use special nodes iterator
8399 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8400 while( anIter->more() ) {
8401 nodes[ iNode++ ] = cast2Node(anIter->next());
8405 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8406 while ( nIt->more() )
8407 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8409 nodes[ iNode ] = nodes[ 0 ];
8411 for ( iNode = 0; iNode < nbNodes; iNode++ )
8412 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8413 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8414 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8416 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8417 curElemList.push_back( e );
8421 // analyse the found
8423 int nbNewBorders = curElemList.size();
8424 if ( nbNewBorders == 0 ) {
8425 // no free border furthermore
8426 return !needTheLast;
8428 else if ( nbNewBorders == 1 ) {
8429 // one more element found
8431 nStart = nStartList.front();
8432 curElem = curElemList.front();
8433 theFaces.push_back( curElem );
8434 theNodes.push_back( nStart );
8437 // several continuations found
8438 list< const SMDS_MeshElement* >::iterator curElemIt;
8439 list< const SMDS_MeshNode* >::iterator nStartIt;
8440 // check if one of them reached the last node
8441 if ( needTheLast ) {
8442 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8443 curElemIt!= curElemList.end();
8444 curElemIt++, nStartIt++ )
8445 if ( *nStartIt == theLastNode ) {
8446 theFaces.push_back( *curElemIt );
8447 theNodes.push_back( *nStartIt );
8451 // find the best free border by the continuations
8452 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8453 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8454 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8455 curElemIt!= curElemList.end();
8456 curElemIt++, nStartIt++ )
8458 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8459 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8460 // find one more free border
8461 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8465 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8466 // choice: clear a worse one
8467 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8468 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8469 contNodes[ iWorse ].clear();
8470 contFaces[ iWorse ].clear();
8473 if ( contNodes[0].empty() && contNodes[1].empty() )
8476 // append the best free border
8477 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8478 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8479 theNodes.pop_back(); // remove nIgnore
8480 theNodes.pop_back(); // remove nStart
8481 theFaces.pop_back(); // remove curElem
8482 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8483 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8484 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8485 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8488 } // several continuations found
8489 } // while ( nStart != theLastNode )
8494 //=======================================================================
8495 //function : CheckFreeBorderNodes
8496 //purpose : Return true if the tree nodes are on a free border
8497 //=======================================================================
8499 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8500 const SMDS_MeshNode* theNode2,
8501 const SMDS_MeshNode* theNode3)
8503 list< const SMDS_MeshNode* > nodes;
8504 list< const SMDS_MeshElement* > faces;
8505 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8508 //=======================================================================
8509 //function : SewFreeBorder
8511 //=======================================================================
8513 SMESH_MeshEditor::Sew_Error
8514 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8515 const SMDS_MeshNode* theBordSecondNode,
8516 const SMDS_MeshNode* theBordLastNode,
8517 const SMDS_MeshNode* theSideFirstNode,
8518 const SMDS_MeshNode* theSideSecondNode,
8519 const SMDS_MeshNode* theSideThirdNode,
8520 const bool theSideIsFreeBorder,
8521 const bool toCreatePolygons,
8522 const bool toCreatePolyedrs)
8524 myLastCreatedElems.Clear();
8525 myLastCreatedNodes.Clear();
8527 MESSAGE("::SewFreeBorder()");
8528 Sew_Error aResult = SEW_OK;
8530 // ====================================
8531 // find side nodes and elements
8532 // ====================================
8534 list< const SMDS_MeshNode* > nSide[ 2 ];
8535 list< const SMDS_MeshElement* > eSide[ 2 ];
8536 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8537 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8541 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8542 nSide[0], eSide[0])) {
8543 MESSAGE(" Free Border 1 not found " );
8544 aResult = SEW_BORDER1_NOT_FOUND;
8546 if (theSideIsFreeBorder) {
8549 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8550 nSide[1], eSide[1])) {
8551 MESSAGE(" Free Border 2 not found " );
8552 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8555 if ( aResult != SEW_OK )
8558 if (!theSideIsFreeBorder) {
8562 // -------------------------------------------------------------------------
8564 // 1. If nodes to merge are not coincident, move nodes of the free border
8565 // from the coord sys defined by the direction from the first to last
8566 // nodes of the border to the correspondent sys of the side 2
8567 // 2. On the side 2, find the links most co-directed with the correspondent
8568 // links of the free border
8569 // -------------------------------------------------------------------------
8571 // 1. Since sewing may break if there are volumes to split on the side 2,
8572 // we wont move nodes but just compute new coordinates for them
8573 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8574 TNodeXYZMap nBordXYZ;
8575 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8576 list< const SMDS_MeshNode* >::iterator nBordIt;
8578 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8579 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8580 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8581 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8582 double tol2 = 1.e-8;
8583 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8584 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8585 // Need node movement.
8587 // find X and Z axes to create trsf
8588 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8590 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8592 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8595 gp_Ax3 toBordAx( Pb1, Zb, X );
8596 gp_Ax3 fromSideAx( Ps1, Zs, X );
8597 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8599 gp_Trsf toBordSys, fromSide2Sys;
8600 toBordSys.SetTransformation( toBordAx );
8601 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8602 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8605 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8606 const SMDS_MeshNode* n = *nBordIt;
8607 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8608 toBordSys.Transforms( xyz );
8609 fromSide2Sys.Transforms( xyz );
8610 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8614 // just insert nodes XYZ in the nBordXYZ map
8615 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8616 const SMDS_MeshNode* n = *nBordIt;
8617 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8621 // 2. On the side 2, find the links most co-directed with the correspondent
8622 // links of the free border
8624 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8625 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8626 sideNodes.push_back( theSideFirstNode );
8628 bool hasVolumes = false;
8629 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8630 set<long> foundSideLinkIDs, checkedLinkIDs;
8631 SMDS_VolumeTool volume;
8632 //const SMDS_MeshNode* faceNodes[ 4 ];
8634 const SMDS_MeshNode* sideNode;
8635 const SMDS_MeshElement* sideElem;
8636 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8637 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8638 nBordIt = bordNodes.begin();
8640 // border node position and border link direction to compare with
8641 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8642 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8643 // choose next side node by link direction or by closeness to
8644 // the current border node:
8645 bool searchByDir = ( *nBordIt != theBordLastNode );
8647 // find the next node on the Side 2
8649 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8651 checkedLinkIDs.clear();
8652 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8654 // loop on inverse elements of current node (prevSideNode) on the Side 2
8655 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8656 while ( invElemIt->more() )
8658 const SMDS_MeshElement* elem = invElemIt->next();
8659 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8660 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8661 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8662 bool isVolume = volume.Set( elem );
8663 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8664 if ( isVolume ) // --volume
8666 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8667 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8668 if(elem->IsQuadratic()) {
8669 const SMDS_VtkFace* F =
8670 dynamic_cast<const SMDS_VtkFace*>(elem);
8671 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8672 // use special nodes iterator
8673 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8674 while( anIter->more() ) {
8675 nodes[ iNode ] = cast2Node(anIter->next());
8676 if ( nodes[ iNode++ ] == prevSideNode )
8677 iPrevNode = iNode - 1;
8681 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8682 while ( nIt->more() ) {
8683 nodes[ iNode ] = cast2Node( nIt->next() );
8684 if ( nodes[ iNode++ ] == prevSideNode )
8685 iPrevNode = iNode - 1;
8688 // there are 2 links to check
8693 // loop on links, to be precise, on the second node of links
8694 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8695 const SMDS_MeshNode* n = nodes[ iNode ];
8697 if ( !volume.IsLinked( n, prevSideNode ))
8701 if ( iNode ) // a node before prevSideNode
8702 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8703 else // a node after prevSideNode
8704 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8706 // check if this link was already used
8707 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8708 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8709 if (!isJustChecked &&
8710 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8712 // test a link geometrically
8713 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8714 bool linkIsBetter = false;
8715 double dot = 0.0, dist = 0.0;
8716 if ( searchByDir ) { // choose most co-directed link
8717 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8718 linkIsBetter = ( dot > maxDot );
8720 else { // choose link with the node closest to bordPos
8721 dist = ( nextXYZ - bordPos ).SquareModulus();
8722 linkIsBetter = ( dist < minDist );
8724 if ( linkIsBetter ) {
8733 } // loop on inverse elements of prevSideNode
8736 MESSAGE(" Cant find path by links of the Side 2 ");
8737 return SEW_BAD_SIDE_NODES;
8739 sideNodes.push_back( sideNode );
8740 sideElems.push_back( sideElem );
8741 foundSideLinkIDs.insert ( linkID );
8742 prevSideNode = sideNode;
8744 if ( *nBordIt == theBordLastNode )
8745 searchByDir = false;
8747 // find the next border link to compare with
8748 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8749 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8750 // move to next border node if sideNode is before forward border node (bordPos)
8751 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8752 prevBordNode = *nBordIt;
8754 bordPos = nBordXYZ[ *nBordIt ];
8755 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8756 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8760 while ( sideNode != theSideSecondNode );
8762 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8763 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8764 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8766 } // end nodes search on the side 2
8768 // ============================
8769 // sew the border to the side 2
8770 // ============================
8772 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8773 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8775 TListOfListOfNodes nodeGroupsToMerge;
8776 if ( nbNodes[0] == nbNodes[1] ||
8777 ( theSideIsFreeBorder && !theSideThirdNode)) {
8779 // all nodes are to be merged
8781 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8782 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8783 nIt[0]++, nIt[1]++ )
8785 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8786 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8787 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8792 // insert new nodes into the border and the side to get equal nb of segments
8794 // get normalized parameters of nodes on the borders
8795 //double param[ 2 ][ maxNbNodes ];
8797 param[0] = new double [ maxNbNodes ];
8798 param[1] = new double [ maxNbNodes ];
8800 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8801 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8802 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8803 const SMDS_MeshNode* nPrev = *nIt;
8804 double bordLength = 0;
8805 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8806 const SMDS_MeshNode* nCur = *nIt;
8807 gp_XYZ segment (nCur->X() - nPrev->X(),
8808 nCur->Y() - nPrev->Y(),
8809 nCur->Z() - nPrev->Z());
8810 double segmentLen = segment.Modulus();
8811 bordLength += segmentLen;
8812 param[ iBord ][ iNode ] = bordLength;
8815 // normalize within [0,1]
8816 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8817 param[ iBord ][ iNode ] /= bordLength;
8821 // loop on border segments
8822 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8823 int i[ 2 ] = { 0, 0 };
8824 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8825 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8827 TElemOfNodeListMap insertMap;
8828 TElemOfNodeListMap::iterator insertMapIt;
8830 // key: elem to insert nodes into
8831 // value: 2 nodes to insert between + nodes to be inserted
8833 bool next[ 2 ] = { false, false };
8835 // find min adjacent segment length after sewing
8836 double nextParam = 10., prevParam = 0;
8837 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8838 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8839 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8840 if ( i[ iBord ] > 0 )
8841 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8843 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8844 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8845 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8847 // choose to insert or to merge nodes
8848 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8849 if ( Abs( du ) <= minSegLen * 0.2 ) {
8852 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8853 const SMDS_MeshNode* n0 = *nIt[0];
8854 const SMDS_MeshNode* n1 = *nIt[1];
8855 nodeGroupsToMerge.back().push_back( n1 );
8856 nodeGroupsToMerge.back().push_back( n0 );
8857 // position of node of the border changes due to merge
8858 param[ 0 ][ i[0] ] += du;
8859 // move n1 for the sake of elem shape evaluation during insertion.
8860 // n1 will be removed by MergeNodes() anyway
8861 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8862 next[0] = next[1] = true;
8867 int intoBord = ( du < 0 ) ? 0 : 1;
8868 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8869 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8870 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8871 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8872 if ( intoBord == 1 ) {
8873 // move node of the border to be on a link of elem of the side
8874 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8875 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8876 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8877 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8878 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8880 insertMapIt = insertMap.find( elem );
8881 bool notFound = ( insertMapIt == insertMap.end() );
8882 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8884 // insert into another link of the same element:
8885 // 1. perform insertion into the other link of the elem
8886 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8887 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8888 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8889 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8890 // 2. perform insertion into the link of adjacent faces
8892 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8894 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8898 if (toCreatePolyedrs) {
8899 // perform insertion into the links of adjacent volumes
8900 UpdateVolumes(n12, n22, nodeList);
8902 // 3. find an element appeared on n1 and n2 after the insertion
8903 insertMap.erase( elem );
8904 elem = findAdjacentFace( n1, n2, 0 );
8906 if ( notFound || otherLink ) {
8907 // add element and nodes of the side into the insertMap
8908 insertMapIt = insertMap.insert
8909 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8910 (*insertMapIt).second.push_back( n1 );
8911 (*insertMapIt).second.push_back( n2 );
8913 // add node to be inserted into elem
8914 (*insertMapIt).second.push_back( nIns );
8915 next[ 1 - intoBord ] = true;
8918 // go to the next segment
8919 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8920 if ( next[ iBord ] ) {
8921 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8923 nPrev[ iBord ] = *nIt[ iBord ];
8924 nIt[ iBord ]++; i[ iBord ]++;
8928 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8930 // perform insertion of nodes into elements
8932 for (insertMapIt = insertMap.begin();
8933 insertMapIt != insertMap.end();
8936 const SMDS_MeshElement* elem = (*insertMapIt).first;
8937 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8938 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8939 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8941 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8943 if ( !theSideIsFreeBorder ) {
8944 // look for and insert nodes into the faces adjacent to elem
8946 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8948 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8953 if (toCreatePolyedrs) {
8954 // perform insertion into the links of adjacent volumes
8955 UpdateVolumes(n1, n2, nodeList);
8961 } // end: insert new nodes
8963 MergeNodes ( nodeGroupsToMerge );
8968 //=======================================================================
8969 //function : InsertNodesIntoLink
8970 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8971 // and theBetweenNode2 and split theElement
8972 //=======================================================================
8974 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8975 const SMDS_MeshNode* theBetweenNode1,
8976 const SMDS_MeshNode* theBetweenNode2,
8977 list<const SMDS_MeshNode*>& theNodesToInsert,
8978 const bool toCreatePoly)
8980 if ( theFace->GetType() != SMDSAbs_Face ) return;
8982 // find indices of 2 link nodes and of the rest nodes
8983 int iNode = 0, il1, il2, i3, i4;
8984 il1 = il2 = i3 = i4 = -1;
8985 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8986 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8988 if(theFace->IsQuadratic()) {
8989 const SMDS_VtkFace* F =
8990 dynamic_cast<const SMDS_VtkFace*>(theFace);
8991 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8992 // use special nodes iterator
8993 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8994 while( anIter->more() ) {
8995 const SMDS_MeshNode* n = cast2Node(anIter->next());
8996 if ( n == theBetweenNode1 )
8998 else if ( n == theBetweenNode2 )
9004 nodes[ iNode++ ] = n;
9008 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9009 while ( nodeIt->more() ) {
9010 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9011 if ( n == theBetweenNode1 )
9013 else if ( n == theBetweenNode2 )
9019 nodes[ iNode++ ] = n;
9022 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9025 // arrange link nodes to go one after another regarding the face orientation
9026 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9027 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9032 aNodesToInsert.reverse();
9034 // check that not link nodes of a quadrangles are in good order
9035 int nbFaceNodes = theFace->NbNodes();
9036 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9042 if (toCreatePoly || theFace->IsPoly()) {
9045 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9047 // add nodes of face up to first node of link
9050 if(theFace->IsQuadratic()) {
9051 const SMDS_VtkFace* F =
9052 dynamic_cast<const SMDS_VtkFace*>(theFace);
9053 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9054 // use special nodes iterator
9055 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9056 while( anIter->more() && !isFLN ) {
9057 const SMDS_MeshNode* n = cast2Node(anIter->next());
9058 poly_nodes[iNode++] = n;
9059 if (n == nodes[il1]) {
9063 // add nodes to insert
9064 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9065 for (; nIt != aNodesToInsert.end(); nIt++) {
9066 poly_nodes[iNode++] = *nIt;
9068 // add nodes of face starting from last node of link
9069 while ( anIter->more() ) {
9070 poly_nodes[iNode++] = cast2Node(anIter->next());
9074 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9075 while ( nodeIt->more() && !isFLN ) {
9076 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9077 poly_nodes[iNode++] = n;
9078 if (n == nodes[il1]) {
9082 // add nodes to insert
9083 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9084 for (; nIt != aNodesToInsert.end(); nIt++) {
9085 poly_nodes[iNode++] = *nIt;
9087 // add nodes of face starting from last node of link
9088 while ( nodeIt->more() ) {
9089 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9090 poly_nodes[iNode++] = n;
9094 // edit or replace the face
9095 SMESHDS_Mesh *aMesh = GetMeshDS();
9097 if (theFace->IsPoly()) {
9098 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9101 int aShapeId = FindShape( theFace );
9103 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9104 myLastCreatedElems.Append(newElem);
9105 if ( aShapeId && newElem )
9106 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9108 aMesh->RemoveElement(theFace);
9113 SMESHDS_Mesh *aMesh = GetMeshDS();
9114 if( !theFace->IsQuadratic() ) {
9116 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9117 int nbLinkNodes = 2 + aNodesToInsert.size();
9118 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9119 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9120 linkNodes[ 0 ] = nodes[ il1 ];
9121 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9122 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9123 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9124 linkNodes[ iNode++ ] = *nIt;
9126 // decide how to split a quadrangle: compare possible variants
9127 // and choose which of splits to be a quadrangle
9128 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9129 if ( nbFaceNodes == 3 ) {
9130 iBestQuad = nbSplits;
9133 else if ( nbFaceNodes == 4 ) {
9134 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9135 double aBestRate = DBL_MAX;
9136 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9138 double aBadRate = 0;
9139 // evaluate elements quality
9140 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9141 if ( iSplit == iQuad ) {
9142 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9146 aBadRate += getBadRate( &quad, aCrit );
9149 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9151 nodes[ iSplit < iQuad ? i4 : i3 ]);
9152 aBadRate += getBadRate( &tria, aCrit );
9156 if ( aBadRate < aBestRate ) {
9158 aBestRate = aBadRate;
9163 // create new elements
9164 int aShapeId = FindShape( theFace );
9167 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9168 SMDS_MeshElement* newElem = 0;
9169 if ( iSplit == iBestQuad )
9170 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9175 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9177 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9178 myLastCreatedElems.Append(newElem);
9179 if ( aShapeId && newElem )
9180 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9183 // change nodes of theFace
9184 const SMDS_MeshNode* newNodes[ 4 ];
9185 newNodes[ 0 ] = linkNodes[ i1 ];
9186 newNodes[ 1 ] = linkNodes[ i2 ];
9187 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9188 newNodes[ 3 ] = nodes[ i4 ];
9189 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9190 const SMDS_MeshElement* newElem = 0;
9191 if (iSplit == iBestQuad)
9192 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9194 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9195 myLastCreatedElems.Append(newElem);
9196 if ( aShapeId && newElem )
9197 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9198 } // end if(!theFace->IsQuadratic())
9199 else { // theFace is quadratic
9200 // we have to split theFace on simple triangles and one simple quadrangle
9202 int nbshift = tmp*2;
9203 // shift nodes in nodes[] by nbshift
9205 for(i=0; i<nbshift; i++) {
9206 const SMDS_MeshNode* n = nodes[0];
9207 for(j=0; j<nbFaceNodes-1; j++) {
9208 nodes[j] = nodes[j+1];
9210 nodes[nbFaceNodes-1] = n;
9212 il1 = il1 - nbshift;
9213 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9214 // n0 n1 n2 n0 n1 n2
9215 // +-----+-----+ +-----+-----+
9224 // create new elements
9225 int aShapeId = FindShape( theFace );
9228 if(nbFaceNodes==6) { // quadratic triangle
9229 SMDS_MeshElement* newElem =
9230 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9231 myLastCreatedElems.Append(newElem);
9232 if ( aShapeId && newElem )
9233 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9234 if(theFace->IsMediumNode(nodes[il1])) {
9235 // create quadrangle
9236 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9237 myLastCreatedElems.Append(newElem);
9238 if ( aShapeId && newElem )
9239 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9245 // create quadrangle
9246 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9247 myLastCreatedElems.Append(newElem);
9248 if ( aShapeId && newElem )
9249 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9255 else { // nbFaceNodes==8 - quadratic quadrangle
9256 SMDS_MeshElement* newElem =
9257 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9258 myLastCreatedElems.Append(newElem);
9259 if ( aShapeId && newElem )
9260 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9261 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9262 myLastCreatedElems.Append(newElem);
9263 if ( aShapeId && newElem )
9264 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9265 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9266 myLastCreatedElems.Append(newElem);
9267 if ( aShapeId && newElem )
9268 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9269 if(theFace->IsMediumNode(nodes[il1])) {
9270 // create quadrangle
9271 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9272 myLastCreatedElems.Append(newElem);
9273 if ( aShapeId && newElem )
9274 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9280 // create quadrangle
9281 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9282 myLastCreatedElems.Append(newElem);
9283 if ( aShapeId && newElem )
9284 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9290 // create needed triangles using n1,n2,n3 and inserted nodes
9291 int nbn = 2 + aNodesToInsert.size();
9292 //const SMDS_MeshNode* aNodes[nbn];
9293 vector<const SMDS_MeshNode*> aNodes(nbn);
9294 aNodes[0] = nodes[n1];
9295 aNodes[nbn-1] = nodes[n2];
9296 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9297 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9298 aNodes[iNode++] = *nIt;
9300 for(i=1; i<nbn; i++) {
9301 SMDS_MeshElement* newElem =
9302 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9303 myLastCreatedElems.Append(newElem);
9304 if ( aShapeId && newElem )
9305 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9309 aMesh->RemoveElement(theFace);
9312 //=======================================================================
9313 //function : UpdateVolumes
9315 //=======================================================================
9316 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9317 const SMDS_MeshNode* theBetweenNode2,
9318 list<const SMDS_MeshNode*>& theNodesToInsert)
9320 myLastCreatedElems.Clear();
9321 myLastCreatedNodes.Clear();
9323 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9324 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9325 const SMDS_MeshElement* elem = invElemIt->next();
9327 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9328 SMDS_VolumeTool aVolume (elem);
9329 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9332 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9333 int iface, nbFaces = aVolume.NbFaces();
9334 vector<const SMDS_MeshNode *> poly_nodes;
9335 vector<int> quantities (nbFaces);
9337 for (iface = 0; iface < nbFaces; iface++) {
9338 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9339 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9340 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9342 for (int inode = 0; inode < nbFaceNodes; inode++) {
9343 poly_nodes.push_back(faceNodes[inode]);
9345 if (nbInserted == 0) {
9346 if (faceNodes[inode] == theBetweenNode1) {
9347 if (faceNodes[inode + 1] == theBetweenNode2) {
9348 nbInserted = theNodesToInsert.size();
9350 // add nodes to insert
9351 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9352 for (; nIt != theNodesToInsert.end(); nIt++) {
9353 poly_nodes.push_back(*nIt);
9357 else if (faceNodes[inode] == theBetweenNode2) {
9358 if (faceNodes[inode + 1] == theBetweenNode1) {
9359 nbInserted = theNodesToInsert.size();
9361 // add nodes to insert in reversed order
9362 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9364 for (; nIt != theNodesToInsert.begin(); nIt--) {
9365 poly_nodes.push_back(*nIt);
9367 poly_nodes.push_back(*nIt);
9374 quantities[iface] = nbFaceNodes + nbInserted;
9377 // Replace or update the volume
9378 SMESHDS_Mesh *aMesh = GetMeshDS();
9380 if (elem->IsPoly()) {
9381 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9385 int aShapeId = FindShape( elem );
9387 SMDS_MeshElement* newElem =
9388 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9389 myLastCreatedElems.Append(newElem);
9390 if (aShapeId && newElem)
9391 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9393 aMesh->RemoveElement(elem);
9400 //================================================================================
9402 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9404 //================================================================================
9406 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9407 vector<const SMDS_MeshNode *> & nodes,
9408 vector<int> & nbNodeInFaces )
9411 nbNodeInFaces.clear();
9412 SMDS_VolumeTool vTool ( elem );
9413 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9415 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9416 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9417 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9422 //=======================================================================
9424 * \brief Convert elements contained in a submesh to quadratic
9425 * \return int - nb of checked elements
9427 //=======================================================================
9429 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9430 SMESH_MesherHelper& theHelper,
9431 const bool theForce3d)
9434 if( !theSm ) return nbElem;
9436 vector<int> nbNodeInFaces;
9437 vector<const SMDS_MeshNode *> nodes;
9438 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9439 while(ElemItr->more())
9442 const SMDS_MeshElement* elem = ElemItr->next();
9443 if( !elem || elem->IsQuadratic() ) continue;
9445 // get elem data needed to re-create it
9447 const int id = elem->GetID();
9448 const int nbNodes = elem->NbNodes();
9449 const SMDSAbs_ElementType aType = elem->GetType();
9450 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9451 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9452 if ( aGeomType == SMDSEntity_Polyhedra )
9453 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9454 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9455 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9457 // remove a linear element
9458 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9460 const SMDS_MeshElement* NewElem = 0;
9466 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9474 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9477 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9480 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9485 case SMDSAbs_Volume :
9489 case SMDSEntity_Tetra:
9490 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9492 case SMDSEntity_Pyramid:
9493 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9495 case SMDSEntity_Penta:
9496 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9498 case SMDSEntity_Hexa:
9499 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9500 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9502 case SMDSEntity_Hexagonal_Prism:
9504 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9511 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9513 theSm->AddElement( NewElem );
9518 //=======================================================================
9519 //function : ConvertToQuadratic
9521 //=======================================================================
9523 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9525 SMESHDS_Mesh* meshDS = GetMeshDS();
9527 SMESH_MesherHelper aHelper(*myMesh);
9528 aHelper.SetIsQuadratic( true );
9530 int nbCheckedElems = 0;
9531 if ( myMesh->HasShapeToMesh() )
9533 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9535 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9536 while ( smIt->more() ) {
9537 SMESH_subMesh* sm = smIt->next();
9538 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9539 aHelper.SetSubShape( sm->GetSubShape() );
9540 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9545 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9546 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9548 SMESHDS_SubMesh *smDS = 0;
9549 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9550 while(aEdgeItr->more())
9552 const SMDS_MeshEdge* edge = aEdgeItr->next();
9553 if(edge && !edge->IsQuadratic())
9555 int id = edge->GetID();
9556 //MESSAGE("edge->GetID() " << id);
9557 const SMDS_MeshNode* n1 = edge->GetNode(0);
9558 const SMDS_MeshNode* n2 = edge->GetNode(1);
9560 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9562 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9563 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9566 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9567 while(aFaceItr->more())
9569 const SMDS_MeshFace* face = aFaceItr->next();
9570 if(!face || face->IsQuadratic() ) continue;
9572 const int id = face->GetID();
9573 const SMDSAbs_EntityType type = face->GetEntityType();
9574 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9576 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9578 SMDS_MeshFace * NewFace = 0;
9581 case SMDSEntity_Triangle:
9582 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9584 case SMDSEntity_Quadrangle:
9585 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9588 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9590 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9592 vector<int> nbNodeInFaces;
9593 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9594 while(aVolumeItr->more())
9596 const SMDS_MeshVolume* volume = aVolumeItr->next();
9597 if(!volume || volume->IsQuadratic() ) continue;
9599 const int id = volume->GetID();
9600 const SMDSAbs_EntityType type = volume->GetEntityType();
9601 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9602 if ( type == SMDSEntity_Polyhedra )
9603 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9604 else if ( type == SMDSEntity_Hexagonal_Prism )
9605 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9607 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9609 SMDS_MeshVolume * NewVolume = 0;
9612 case SMDSEntity_Tetra:
9613 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9615 case SMDSEntity_Hexa:
9616 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9617 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9619 case SMDSEntity_Pyramid:
9620 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9621 nodes[3], nodes[4], id, theForce3d);
9623 case SMDSEntity_Penta:
9624 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9625 nodes[3], nodes[4], nodes[5], id, theForce3d);
9627 case SMDSEntity_Hexagonal_Prism:
9629 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9631 ReplaceElemInGroups(volume, NewVolume, meshDS);
9636 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9637 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9638 aHelper.FixQuadraticElements();
9642 //================================================================================
9644 * \brief Makes given elements quadratic
9645 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9646 * \param theElements - elements to make quadratic
9648 //================================================================================
9650 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9651 TIDSortedElemSet& theElements)
9653 if ( theElements.empty() ) return;
9655 // we believe that all theElements are of the same type
9656 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9658 // get all nodes shared by theElements
9659 TIDSortedNodeSet allNodes;
9660 TIDSortedElemSet::iterator eIt = theElements.begin();
9661 for ( ; eIt != theElements.end(); ++eIt )
9662 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9664 // complete theElements with elements of lower dim whose all nodes are in allNodes
9666 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9667 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9668 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9669 for ( ; nIt != allNodes.end(); ++nIt )
9671 const SMDS_MeshNode* n = *nIt;
9672 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9673 while ( invIt->more() )
9675 const SMDS_MeshElement* e = invIt->next();
9676 if ( e->IsQuadratic() )
9678 quadAdjacentElems[ e->GetType() ].insert( e );
9681 if ( e->GetType() >= elemType )
9683 continue; // same type of more complex linear element
9686 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9687 continue; // e is already checked
9691 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9692 while ( nodeIt->more() && allIn )
9693 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9695 theElements.insert(e );
9699 SMESH_MesherHelper helper(*myMesh);
9700 helper.SetIsQuadratic( true );
9702 // add links of quadratic adjacent elements to the helper
9704 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9705 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9706 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9708 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9710 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9711 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9712 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9714 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9716 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9717 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9718 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9720 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9723 // make quadratic elements instead of linear ones
9725 SMESHDS_Mesh* meshDS = GetMeshDS();
9726 SMESHDS_SubMesh* smDS = 0;
9727 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9729 const SMDS_MeshElement* elem = *eIt;
9730 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9733 const int id = elem->GetID();
9734 const SMDSAbs_ElementType type = elem->GetType();
9735 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9737 if ( !smDS || !smDS->Contains( elem ))
9738 smDS = meshDS->MeshElements( elem->getshapeId() );
9739 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9741 SMDS_MeshElement * newElem = 0;
9742 switch( nodes.size() )
9744 case 4: // cases for most frequently used element types go first (for optimization)
9745 if ( type == SMDSAbs_Volume )
9746 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9748 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9751 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9752 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9755 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9758 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9761 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9762 nodes[4], id, theForce3d);
9765 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9766 nodes[4], nodes[5], id, theForce3d);
9770 ReplaceElemInGroups( elem, newElem, meshDS);
9771 if( newElem && smDS )
9772 smDS->AddElement( newElem );
9775 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9776 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9777 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9778 helper.FixQuadraticElements();
9782 //=======================================================================
9784 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9785 * \return int - nb of checked elements
9787 //=======================================================================
9789 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9790 SMDS_ElemIteratorPtr theItr,
9791 const int theShapeID)
9794 SMESHDS_Mesh* meshDS = GetMeshDS();
9796 while( theItr->more() )
9798 const SMDS_MeshElement* elem = theItr->next();
9800 if( elem && elem->IsQuadratic())
9802 int id = elem->GetID();
9803 int nbCornerNodes = elem->NbCornerNodes();
9804 SMDSAbs_ElementType aType = elem->GetType();
9806 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9808 //remove a quadratic element
9809 if ( !theSm || !theSm->Contains( elem ))
9810 theSm = meshDS->MeshElements( elem->getshapeId() );
9811 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9813 // remove medium nodes
9814 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9815 if ( nodes[i]->NbInverseElements() == 0 )
9816 meshDS->RemoveFreeNode( nodes[i], theSm );
9818 // add a linear element
9819 nodes.resize( nbCornerNodes );
9820 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9821 ReplaceElemInGroups(elem, newElem, meshDS);
9822 if( theSm && newElem )
9823 theSm->AddElement( newElem );
9829 //=======================================================================
9830 //function : ConvertFromQuadratic
9832 //=======================================================================
9834 bool SMESH_MeshEditor::ConvertFromQuadratic()
9836 int nbCheckedElems = 0;
9837 if ( myMesh->HasShapeToMesh() )
9839 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9841 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9842 while ( smIt->more() ) {
9843 SMESH_subMesh* sm = smIt->next();
9844 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9845 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9851 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9852 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9854 SMESHDS_SubMesh *aSM = 0;
9855 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9863 //================================================================================
9865 * \brief Return true if all medium nodes of the element are in the node set
9867 //================================================================================
9869 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9871 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9872 if ( !nodeSet.count( elem->GetNode(i) ))
9878 //================================================================================
9880 * \brief Makes given elements linear
9882 //================================================================================
9884 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9886 if ( theElements.empty() ) return;
9888 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9889 set<int> mediumNodeIDs;
9890 TIDSortedElemSet::iterator eIt = theElements.begin();
9891 for ( ; eIt != theElements.end(); ++eIt )
9893 const SMDS_MeshElement* e = *eIt;
9894 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9895 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9898 // replace given elements by linear ones
9899 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9900 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9901 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9903 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9904 // except those elements sharing medium nodes of quadratic element whose medium nodes
9905 // are not all in mediumNodeIDs
9907 // get remaining medium nodes
9908 TIDSortedNodeSet mediumNodes;
9909 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9910 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9911 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9912 mediumNodes.insert( mediumNodes.end(), n );
9914 // find more quadratic elements to convert
9915 TIDSortedElemSet moreElemsToConvert;
9916 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9917 for ( ; nIt != mediumNodes.end(); ++nIt )
9919 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9920 while ( invIt->more() )
9922 const SMDS_MeshElement* e = invIt->next();
9923 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9925 // find a more complex element including e and
9926 // whose medium nodes are not in mediumNodes
9927 bool complexFound = false;
9928 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9930 SMDS_ElemIteratorPtr invIt2 =
9931 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9932 while ( invIt2->more() )
9934 const SMDS_MeshElement* eComplex = invIt2->next();
9935 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9937 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9938 if ( nbCommonNodes == e->NbNodes())
9940 complexFound = true;
9941 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9947 if ( !complexFound )
9948 moreElemsToConvert.insert( e );
9952 elemIt = SMDS_ElemIteratorPtr
9953 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9954 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9957 //=======================================================================
9958 //function : SewSideElements
9960 //=======================================================================
9962 SMESH_MeshEditor::Sew_Error
9963 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9964 TIDSortedElemSet& theSide2,
9965 const SMDS_MeshNode* theFirstNode1,
9966 const SMDS_MeshNode* theFirstNode2,
9967 const SMDS_MeshNode* theSecondNode1,
9968 const SMDS_MeshNode* theSecondNode2)
9970 myLastCreatedElems.Clear();
9971 myLastCreatedNodes.Clear();
9973 MESSAGE ("::::SewSideElements()");
9974 if ( theSide1.size() != theSide2.size() )
9975 return SEW_DIFF_NB_OF_ELEMENTS;
9977 Sew_Error aResult = SEW_OK;
9979 // 1. Build set of faces representing each side
9980 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9981 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9983 // =======================================================================
9984 // 1. Build set of faces representing each side:
9985 // =======================================================================
9986 // a. build set of nodes belonging to faces
9987 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9988 // c. create temporary faces representing side of volumes if correspondent
9989 // face does not exist
9991 SMESHDS_Mesh* aMesh = GetMeshDS();
9992 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9993 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9994 TIDSortedElemSet faceSet1, faceSet2;
9995 set<const SMDS_MeshElement*> volSet1, volSet2;
9996 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9997 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9998 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9999 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
10000 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
10001 int iSide, iFace, iNode;
10003 list<const SMDS_MeshElement* > tempFaceList;
10004 for ( iSide = 0; iSide < 2; iSide++ ) {
10005 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10006 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10007 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10008 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10009 set<const SMDS_MeshElement*>::iterator vIt;
10010 TIDSortedElemSet::iterator eIt;
10011 set<const SMDS_MeshNode*>::iterator nIt;
10013 // check that given nodes belong to given elements
10014 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10015 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10016 int firstIndex = -1, secondIndex = -1;
10017 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10018 const SMDS_MeshElement* elem = *eIt;
10019 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10020 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10021 if ( firstIndex > -1 && secondIndex > -1 ) break;
10023 if ( firstIndex < 0 || secondIndex < 0 ) {
10024 // we can simply return until temporary faces created
10025 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10028 // -----------------------------------------------------------
10029 // 1a. Collect nodes of existing faces
10030 // and build set of face nodes in order to detect missing
10031 // faces corresponding to sides of volumes
10032 // -----------------------------------------------------------
10034 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10036 // loop on the given element of a side
10037 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10038 //const SMDS_MeshElement* elem = *eIt;
10039 const SMDS_MeshElement* elem = *eIt;
10040 if ( elem->GetType() == SMDSAbs_Face ) {
10041 faceSet->insert( elem );
10042 set <const SMDS_MeshNode*> faceNodeSet;
10043 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10044 while ( nodeIt->more() ) {
10045 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10046 nodeSet->insert( n );
10047 faceNodeSet.insert( n );
10049 setOfFaceNodeSet.insert( faceNodeSet );
10051 else if ( elem->GetType() == SMDSAbs_Volume )
10052 volSet->insert( elem );
10054 // ------------------------------------------------------------------------------
10055 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10056 // ------------------------------------------------------------------------------
10058 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10059 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10060 while ( fIt->more() ) { // loop on faces sharing a node
10061 const SMDS_MeshElement* f = fIt->next();
10062 if ( faceSet->find( f ) == faceSet->end() ) {
10063 // check if all nodes are in nodeSet and
10064 // complete setOfFaceNodeSet if they are
10065 set <const SMDS_MeshNode*> faceNodeSet;
10066 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10067 bool allInSet = true;
10068 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10069 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10070 if ( nodeSet->find( n ) == nodeSet->end() )
10073 faceNodeSet.insert( n );
10076 faceSet->insert( f );
10077 setOfFaceNodeSet.insert( faceNodeSet );
10083 // -------------------------------------------------------------------------
10084 // 1c. Create temporary faces representing sides of volumes if correspondent
10085 // face does not exist
10086 // -------------------------------------------------------------------------
10088 if ( !volSet->empty() ) {
10089 //int nodeSetSize = nodeSet->size();
10091 // loop on given volumes
10092 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10093 SMDS_VolumeTool vol (*vIt);
10094 // loop on volume faces: find free faces
10095 // --------------------------------------
10096 list<const SMDS_MeshElement* > freeFaceList;
10097 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10098 if ( !vol.IsFreeFace( iFace ))
10100 // check if there is already a face with same nodes in a face set
10101 const SMDS_MeshElement* aFreeFace = 0;
10102 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10103 int nbNodes = vol.NbFaceNodes( iFace );
10104 set <const SMDS_MeshNode*> faceNodeSet;
10105 vol.GetFaceNodes( iFace, faceNodeSet );
10106 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10108 // no such a face is given but it still can exist, check it
10109 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10110 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10112 if ( !aFreeFace ) {
10113 // create a temporary face
10114 if ( nbNodes == 3 ) {
10115 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10116 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10118 else if ( nbNodes == 4 ) {
10119 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10120 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10123 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10124 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10125 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10128 tempFaceList.push_back( aFreeFace );
10132 freeFaceList.push_back( aFreeFace );
10134 } // loop on faces of a volume
10136 // choose one of several free faces of a volume
10137 // --------------------------------------------
10138 if ( freeFaceList.size() > 1 ) {
10139 // choose a face having max nb of nodes shared by other elems of a side
10140 int maxNbNodes = -1;
10141 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10142 while ( fIt != freeFaceList.end() ) { // loop on free faces
10143 int nbSharedNodes = 0;
10144 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10145 while ( nodeIt->more() ) { // loop on free face nodes
10146 const SMDS_MeshNode* n =
10147 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10148 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10149 while ( invElemIt->more() ) {
10150 const SMDS_MeshElement* e = invElemIt->next();
10151 nbSharedNodes += faceSet->count( e );
10152 nbSharedNodes += elemSet->count( e );
10155 if ( nbSharedNodes > maxNbNodes ) {
10156 maxNbNodes = nbSharedNodes;
10157 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10159 else if ( nbSharedNodes == maxNbNodes ) {
10163 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10166 if ( freeFaceList.size() > 1 )
10168 // could not choose one face, use another way
10169 // choose a face most close to the bary center of the opposite side
10170 gp_XYZ aBC( 0., 0., 0. );
10171 set <const SMDS_MeshNode*> addedNodes;
10172 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10173 eIt = elemSet2->begin();
10174 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10175 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10176 while ( nodeIt->more() ) { // loop on free face nodes
10177 const SMDS_MeshNode* n =
10178 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10179 if ( addedNodes.insert( n ).second )
10180 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10183 aBC /= addedNodes.size();
10184 double minDist = DBL_MAX;
10185 fIt = freeFaceList.begin();
10186 while ( fIt != freeFaceList.end() ) { // loop on free faces
10188 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10189 while ( nodeIt->more() ) { // loop on free face nodes
10190 const SMDS_MeshNode* n =
10191 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10192 gp_XYZ p( n->X(),n->Y(),n->Z() );
10193 dist += ( aBC - p ).SquareModulus();
10195 if ( dist < minDist ) {
10197 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10200 fIt = freeFaceList.erase( fIt++ );
10203 } // choose one of several free faces of a volume
10205 if ( freeFaceList.size() == 1 ) {
10206 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10207 faceSet->insert( aFreeFace );
10208 // complete a node set with nodes of a found free face
10209 // for ( iNode = 0; iNode < ; iNode++ )
10210 // nodeSet->insert( fNodes[ iNode ] );
10213 } // loop on volumes of a side
10215 // // complete a set of faces if new nodes in a nodeSet appeared
10216 // // ----------------------------------------------------------
10217 // if ( nodeSetSize != nodeSet->size() ) {
10218 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10219 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10220 // while ( fIt->more() ) { // loop on faces sharing a node
10221 // const SMDS_MeshElement* f = fIt->next();
10222 // if ( faceSet->find( f ) == faceSet->end() ) {
10223 // // check if all nodes are in nodeSet and
10224 // // complete setOfFaceNodeSet if they are
10225 // set <const SMDS_MeshNode*> faceNodeSet;
10226 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10227 // bool allInSet = true;
10228 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10229 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10230 // if ( nodeSet->find( n ) == nodeSet->end() )
10231 // allInSet = false;
10233 // faceNodeSet.insert( n );
10235 // if ( allInSet ) {
10236 // faceSet->insert( f );
10237 // setOfFaceNodeSet.insert( faceNodeSet );
10243 } // Create temporary faces, if there are volumes given
10246 if ( faceSet1.size() != faceSet2.size() ) {
10247 // delete temporary faces: they are in reverseElements of actual nodes
10248 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10249 // while ( tmpFaceIt->more() )
10250 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10251 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10252 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10253 // aMesh->RemoveElement(*tmpFaceIt);
10254 MESSAGE("Diff nb of faces");
10255 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10258 // ============================================================
10259 // 2. Find nodes to merge:
10260 // bind a node to remove to a node to put instead
10261 // ============================================================
10263 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10264 if ( theFirstNode1 != theFirstNode2 )
10265 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10266 if ( theSecondNode1 != theSecondNode2 )
10267 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10269 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10270 set< long > linkIdSet; // links to process
10271 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10273 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10274 list< NLink > linkList[2];
10275 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10276 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10277 // loop on links in linkList; find faces by links and append links
10278 // of the found faces to linkList
10279 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10280 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10282 NLink link[] = { *linkIt[0], *linkIt[1] };
10283 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10284 if ( !linkIdSet.count( linkID ) )
10287 // by links, find faces in the face sets,
10288 // and find indices of link nodes in the found faces;
10289 // in a face set, there is only one or no face sharing a link
10290 // ---------------------------------------------------------------
10292 const SMDS_MeshElement* face[] = { 0, 0 };
10293 vector<const SMDS_MeshNode*> fnodes[2];
10294 int iLinkNode[2][2];
10295 TIDSortedElemSet avoidSet;
10296 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10297 const SMDS_MeshNode* n1 = link[iSide].first;
10298 const SMDS_MeshNode* n2 = link[iSide].second;
10299 //cout << "Side " << iSide << " ";
10300 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10301 // find a face by two link nodes
10302 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10303 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10304 if ( face[ iSide ])
10306 //cout << " F " << face[ iSide]->GetID() <<endl;
10307 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10308 // put face nodes to fnodes
10309 if ( face[ iSide ]->IsQuadratic() )
10311 // use interlaced nodes iterator
10312 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10313 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10314 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10315 while ( nIter->more() )
10316 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10320 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10321 face[ iSide ]->end_nodes() );
10323 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10327 // check similarity of elements of the sides
10328 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10329 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10330 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10331 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10334 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10336 break; // do not return because it's necessary to remove tmp faces
10339 // set nodes to merge
10340 // -------------------
10342 if ( face[0] && face[1] ) {
10343 const int nbNodes = face[0]->NbNodes();
10344 if ( nbNodes != face[1]->NbNodes() ) {
10345 MESSAGE("Diff nb of face nodes");
10346 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10347 break; // do not return because it s necessary to remove tmp faces
10349 bool reverse[] = { false, false }; // order of nodes in the link
10350 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10351 // analyse link orientation in faces
10352 int i1 = iLinkNode[ iSide ][ 0 ];
10353 int i2 = iLinkNode[ iSide ][ 1 ];
10354 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10356 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10357 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10358 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10360 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10361 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10364 // add other links of the faces to linkList
10365 // -----------------------------------------
10367 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10368 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10369 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10370 if ( !iter_isnew.second ) { // already in a set: no need to process
10371 linkIdSet.erase( iter_isnew.first );
10373 else // new in set == encountered for the first time: add
10375 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10376 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10377 linkList[0].push_back ( NLink( n1, n2 ));
10378 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10383 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10386 } // loop on link lists
10388 if ( aResult == SEW_OK &&
10389 ( //linkIt[0] != linkList[0].end() ||
10390 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10391 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10392 " " << (faceSetPtr[1]->empty()));
10393 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10396 // ====================================================================
10397 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10398 // ====================================================================
10400 // delete temporary faces
10401 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10402 // while ( tmpFaceIt->more() )
10403 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10404 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10405 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10406 aMesh->RemoveElement(*tmpFaceIt);
10408 if ( aResult != SEW_OK)
10411 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10412 // loop on nodes replacement map
10413 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10414 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10415 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10416 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10417 nodeIDsToRemove.push_back( nToRemove->GetID() );
10418 // loop on elements sharing nToRemove
10419 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10420 while ( invElemIt->more() ) {
10421 const SMDS_MeshElement* e = invElemIt->next();
10422 // get a new suite of nodes: make replacement
10423 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10424 vector< const SMDS_MeshNode*> nodes( nbNodes );
10425 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10426 while ( nIt->more() ) {
10427 const SMDS_MeshNode* n =
10428 static_cast<const SMDS_MeshNode*>( nIt->next() );
10429 nnIt = nReplaceMap.find( n );
10430 if ( nnIt != nReplaceMap.end() ) {
10432 n = (*nnIt).second;
10436 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10437 // elemIDsToRemove.push_back( e->GetID() );
10441 SMDSAbs_ElementType etyp = e->GetType();
10442 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10445 myLastCreatedElems.Append(newElem);
10446 AddToSameGroups(newElem, e, aMesh);
10447 int aShapeId = e->getshapeId();
10450 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10453 aMesh->RemoveElement(e);
10458 Remove( nodeIDsToRemove, true );
10463 //================================================================================
10465 * \brief Find corresponding nodes in two sets of faces
10466 * \param theSide1 - first face set
10467 * \param theSide2 - second first face
10468 * \param theFirstNode1 - a boundary node of set 1
10469 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10470 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10471 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10472 * \param nReplaceMap - output map of corresponding nodes
10473 * \return bool - is a success or not
10475 //================================================================================
10478 //#define DEBUG_MATCHING_NODES
10481 SMESH_MeshEditor::Sew_Error
10482 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10483 set<const SMDS_MeshElement*>& theSide2,
10484 const SMDS_MeshNode* theFirstNode1,
10485 const SMDS_MeshNode* theFirstNode2,
10486 const SMDS_MeshNode* theSecondNode1,
10487 const SMDS_MeshNode* theSecondNode2,
10488 TNodeNodeMap & nReplaceMap)
10490 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10492 nReplaceMap.clear();
10493 if ( theFirstNode1 != theFirstNode2 )
10494 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10495 if ( theSecondNode1 != theSecondNode2 )
10496 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10498 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10499 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10501 list< NLink > linkList[2];
10502 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10503 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10505 // loop on links in linkList; find faces by links and append links
10506 // of the found faces to linkList
10507 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10508 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10509 NLink link[] = { *linkIt[0], *linkIt[1] };
10510 if ( linkSet.find( link[0] ) == linkSet.end() )
10513 // by links, find faces in the face sets,
10514 // and find indices of link nodes in the found faces;
10515 // in a face set, there is only one or no face sharing a link
10516 // ---------------------------------------------------------------
10518 const SMDS_MeshElement* face[] = { 0, 0 };
10519 list<const SMDS_MeshNode*> notLinkNodes[2];
10520 //bool reverse[] = { false, false }; // order of notLinkNodes
10522 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10524 const SMDS_MeshNode* n1 = link[iSide].first;
10525 const SMDS_MeshNode* n2 = link[iSide].second;
10526 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10527 set< const SMDS_MeshElement* > facesOfNode1;
10528 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10530 // during a loop of the first node, we find all faces around n1,
10531 // during a loop of the second node, we find one face sharing both n1 and n2
10532 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10533 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10534 while ( fIt->more() ) { // loop on faces sharing a node
10535 const SMDS_MeshElement* f = fIt->next();
10536 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10537 ! facesOfNode1.insert( f ).second ) // f encounters twice
10539 if ( face[ iSide ] ) {
10540 MESSAGE( "2 faces per link " );
10541 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10544 faceSet->erase( f );
10546 // get not link nodes
10547 int nbN = f->NbNodes();
10548 if ( f->IsQuadratic() )
10550 nbNodes[ iSide ] = nbN;
10551 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10552 int i1 = f->GetNodeIndex( n1 );
10553 int i2 = f->GetNodeIndex( n2 );
10554 int iEnd = nbN, iBeg = -1, iDelta = 1;
10555 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10557 std::swap( iEnd, iBeg ); iDelta = -1;
10562 if ( i == iEnd ) i = iBeg + iDelta;
10563 if ( i == i1 ) break;
10564 nodes.push_back ( f->GetNode( i ) );
10570 // check similarity of elements of the sides
10571 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10572 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10573 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10574 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10577 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10581 // set nodes to merge
10582 // -------------------
10584 if ( face[0] && face[1] ) {
10585 if ( nbNodes[0] != nbNodes[1] ) {
10586 MESSAGE("Diff nb of face nodes");
10587 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10589 #ifdef DEBUG_MATCHING_NODES
10590 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10591 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10592 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10594 int nbN = nbNodes[0];
10596 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10597 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10598 for ( int i = 0 ; i < nbN - 2; ++i ) {
10599 #ifdef DEBUG_MATCHING_NODES
10600 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10602 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10606 // add other links of the face 1 to linkList
10607 // -----------------------------------------
10609 const SMDS_MeshElement* f0 = face[0];
10610 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10611 for ( int i = 0; i < nbN; i++ )
10613 const SMDS_MeshNode* n2 = f0->GetNode( i );
10614 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10615 linkSet.insert( SMESH_TLink( n1, n2 ));
10616 if ( !iter_isnew.second ) { // already in a set: no need to process
10617 linkSet.erase( iter_isnew.first );
10619 else // new in set == encountered for the first time: add
10621 #ifdef DEBUG_MATCHING_NODES
10622 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10623 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10625 linkList[0].push_back ( NLink( n1, n2 ));
10626 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10631 } // loop on link lists
10636 //================================================================================
10638 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10639 \param theElems - the list of elements (edges or faces) to be replicated
10640 The nodes for duplication could be found from these elements
10641 \param theNodesNot - list of nodes to NOT replicate
10642 \param theAffectedElems - the list of elements (cells and edges) to which the
10643 replicated nodes should be associated to.
10644 \return TRUE if operation has been completed successfully, FALSE otherwise
10646 //================================================================================
10648 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10649 const TIDSortedElemSet& theNodesNot,
10650 const TIDSortedElemSet& theAffectedElems )
10652 myLastCreatedElems.Clear();
10653 myLastCreatedNodes.Clear();
10655 if ( theElems.size() == 0 )
10658 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10663 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10664 // duplicate elements and nodes
10665 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10666 // replce nodes by duplications
10667 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10671 //================================================================================
10673 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10674 \param theMeshDS - mesh instance
10675 \param theElems - the elements replicated or modified (nodes should be changed)
10676 \param theNodesNot - nodes to NOT replicate
10677 \param theNodeNodeMap - relation of old node to new created node
10678 \param theIsDoubleElem - flag os to replicate element or modify
10679 \return TRUE if operation has been completed successfully, FALSE otherwise
10681 //================================================================================
10683 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10684 const TIDSortedElemSet& theElems,
10685 const TIDSortedElemSet& theNodesNot,
10686 std::map< const SMDS_MeshNode*,
10687 const SMDS_MeshNode* >& theNodeNodeMap,
10688 const bool theIsDoubleElem )
10690 MESSAGE("doubleNodes");
10691 // iterate on through element and duplicate them (by nodes duplication)
10693 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10694 for ( ; elemItr != theElems.end(); ++elemItr )
10696 const SMDS_MeshElement* anElem = *elemItr;
10700 bool isDuplicate = false;
10701 // duplicate nodes to duplicate element
10702 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10703 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10705 while ( anIter->more() )
10708 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10709 SMDS_MeshNode* aNewNode = aCurrNode;
10710 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10711 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10712 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10715 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10716 theNodeNodeMap[ aCurrNode ] = aNewNode;
10717 myLastCreatedNodes.Append( aNewNode );
10719 isDuplicate |= (aCurrNode != aNewNode);
10720 newNodes[ ind++ ] = aNewNode;
10722 if ( !isDuplicate )
10725 if ( theIsDoubleElem )
10726 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10729 MESSAGE("ChangeElementNodes");
10730 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10737 //================================================================================
10739 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10740 \param theNodes - identifiers of nodes to be doubled
10741 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10742 nodes. If list of element identifiers is empty then nodes are doubled but
10743 they not assigned to elements
10744 \return TRUE if operation has been completed successfully, FALSE otherwise
10746 //================================================================================
10748 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10749 const std::list< int >& theListOfModifiedElems )
10751 MESSAGE("DoubleNodes");
10752 myLastCreatedElems.Clear();
10753 myLastCreatedNodes.Clear();
10755 if ( theListOfNodes.size() == 0 )
10758 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10762 // iterate through nodes and duplicate them
10764 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10766 std::list< int >::const_iterator aNodeIter;
10767 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10769 int aCurr = *aNodeIter;
10770 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10776 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10779 anOldNodeToNewNode[ aNode ] = aNewNode;
10780 myLastCreatedNodes.Append( aNewNode );
10784 // Create map of new nodes for modified elements
10786 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10788 std::list< int >::const_iterator anElemIter;
10789 for ( anElemIter = theListOfModifiedElems.begin();
10790 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10792 int aCurr = *anElemIter;
10793 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10797 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10799 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10801 while ( anIter->more() )
10803 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10804 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10806 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10807 aNodeArr[ ind++ ] = aNewNode;
10810 aNodeArr[ ind++ ] = aCurrNode;
10812 anElemToNodes[ anElem ] = aNodeArr;
10815 // Change nodes of elements
10817 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10818 anElemToNodesIter = anElemToNodes.begin();
10819 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10821 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10822 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10825 MESSAGE("ChangeElementNodes");
10826 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10835 //================================================================================
10837 \brief Check if element located inside shape
10838 \return TRUE if IN or ON shape, FALSE otherwise
10840 //================================================================================
10842 template<class Classifier>
10843 bool isInside(const SMDS_MeshElement* theElem,
10844 Classifier& theClassifier,
10845 const double theTol)
10847 gp_XYZ centerXYZ (0, 0, 0);
10848 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10849 while (aNodeItr->more())
10850 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10852 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10853 theClassifier.Perform(aPnt, theTol);
10854 TopAbs_State aState = theClassifier.State();
10855 return (aState == TopAbs_IN || aState == TopAbs_ON );
10858 //================================================================================
10860 * \brief Classifier of the 3D point on the TopoDS_Face
10861 * with interaface suitable for isInside()
10863 //================================================================================
10865 struct _FaceClassifier
10867 Extrema_ExtPS _extremum;
10868 BRepAdaptor_Surface _surface;
10869 TopAbs_State _state;
10871 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10873 _extremum.Initialize( _surface,
10874 _surface.FirstUParameter(), _surface.LastUParameter(),
10875 _surface.FirstVParameter(), _surface.LastVParameter(),
10876 _surface.Tolerance(), _surface.Tolerance() );
10878 void Perform(const gp_Pnt& aPnt, double theTol)
10880 _state = TopAbs_OUT;
10881 _extremum.Perform(aPnt);
10882 if ( _extremum.IsDone() )
10883 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10884 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10885 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10887 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10890 TopAbs_State State() const
10897 //================================================================================
10899 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
10900 This method is the first step of DoubleNodeElemGroupsInRegion.
10901 \param theElems - list of groups of elements (edges or faces) to be replicated
10902 \param theNodesNot - list of groups of nodes not to replicated
10903 \param theShape - shape to detect affected elements (element which geometric center
10904 located on or inside shape).
10905 The replicated nodes should be associated to affected elements.
10906 \return groups of affected elements
10907 \sa DoubleNodeElemGroupsInRegion()
10909 //================================================================================
10911 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10912 const TIDSortedElemSet& theNodesNot,
10913 const TopoDS_Shape& theShape,
10914 TIDSortedElemSet& theAffectedElems)
10916 if ( theShape.IsNull() )
10919 const double aTol = Precision::Confusion();
10920 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10921 auto_ptr<_FaceClassifier> aFaceClassifier;
10922 if ( theShape.ShapeType() == TopAbs_SOLID )
10924 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10925 bsc3d->PerformInfinitePoint(aTol);
10927 else if (theShape.ShapeType() == TopAbs_FACE )
10929 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10932 // iterates on indicated elements and get elements by back references from their nodes
10933 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10934 for ( ; elemItr != theElems.end(); ++elemItr )
10936 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10940 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10941 while ( nodeItr->more() )
10943 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10944 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10946 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10947 while ( backElemItr->more() )
10949 const SMDS_MeshElement* curElem = backElemItr->next();
10950 if ( curElem && theElems.find(curElem) == theElems.end() &&
10952 isInside( curElem, *bsc3d, aTol ) :
10953 isInside( curElem, *aFaceClassifier, aTol )))
10954 theAffectedElems.insert( curElem );
10961 //================================================================================
10963 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10964 \param theElems - group of of elements (edges or faces) to be replicated
10965 \param theNodesNot - group of nodes not to replicate
10966 \param theShape - shape to detect affected elements (element which geometric center
10967 located on or inside shape).
10968 The replicated nodes should be associated to affected elements.
10969 \return TRUE if operation has been completed successfully, FALSE otherwise
10971 //================================================================================
10973 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10974 const TIDSortedElemSet& theNodesNot,
10975 const TopoDS_Shape& theShape )
10977 if ( theShape.IsNull() )
10980 const double aTol = Precision::Confusion();
10981 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10982 auto_ptr<_FaceClassifier> aFaceClassifier;
10983 if ( theShape.ShapeType() == TopAbs_SOLID )
10985 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10986 bsc3d->PerformInfinitePoint(aTol);
10988 else if (theShape.ShapeType() == TopAbs_FACE )
10990 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10993 // iterates on indicated elements and get elements by back references from their nodes
10994 TIDSortedElemSet anAffected;
10995 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10996 for ( ; elemItr != theElems.end(); ++elemItr )
10998 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11002 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11003 while ( nodeItr->more() )
11005 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11006 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11008 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11009 while ( backElemItr->more() )
11011 const SMDS_MeshElement* curElem = backElemItr->next();
11012 if ( curElem && theElems.find(curElem) == theElems.end() &&
11014 isInside( curElem, *bsc3d, aTol ) :
11015 isInside( curElem, *aFaceClassifier, aTol )))
11016 anAffected.insert( curElem );
11020 return DoubleNodes( theElems, theNodesNot, anAffected );
11024 * \brief compute an oriented angle between two planes defined by four points.
11025 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11026 * @param p0 base of the rotation axe
11027 * @param p1 extremity of the rotation axe
11028 * @param g1 belongs to the first plane
11029 * @param g2 belongs to the second plane
11031 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11033 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11034 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11035 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11036 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11037 gp_Vec vref(p0, p1);
11040 gp_Vec n1 = vref.Crossed(v1);
11041 gp_Vec n2 = vref.Crossed(v2);
11042 return n2.AngleWithRef(n1, vref);
11046 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11047 * The list of groups must describe a partition of the mesh volumes.
11048 * The nodes of the internal faces at the boundaries of the groups are doubled.
11049 * In option, the internal faces are replaced by flat elements.
11050 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11051 * The flat elements are stored in groups of volumes.
11052 * @param theElems - list of groups of volumes, where a group of volume is a set of
11053 * SMDS_MeshElements sorted by Id.
11054 * @param createJointElems - if TRUE, create the elements
11055 * @return TRUE if operation has been completed successfully, FALSE otherwise
11057 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11058 bool createJointElems)
11060 MESSAGE("----------------------------------------------");
11061 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11062 MESSAGE("----------------------------------------------");
11064 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11065 meshDS->BuildDownWardConnectivity(true);
11067 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11069 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11070 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11071 // build the list of nodes shared by 2 or more domains, with their domain indexes
11073 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11074 std::map<int,int>celldom; // cell vtkId --> domain
11075 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11076 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11077 faceDomains.clear();
11079 cellDomains.clear();
11080 nodeDomains.clear();
11081 std::map<int,int> emptyMap;
11082 std::set<int> emptySet;
11085 for (int idom = 0; idom < theElems.size(); idom++)
11088 // --- build a map (face to duplicate --> volume to modify)
11089 // with all the faces shared by 2 domains (group of elements)
11090 // and corresponding volume of this domain, for each shared face.
11091 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11093 //MESSAGE("Domain " << idom);
11094 const TIDSortedElemSet& domain = theElems[idom];
11095 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11096 for (; elemItr != domain.end(); ++elemItr)
11098 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11101 int vtkId = anElem->getVtkId();
11102 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11103 int neighborsVtkIds[NBMAXNEIGHBORS];
11104 int downIds[NBMAXNEIGHBORS];
11105 unsigned char downTypes[NBMAXNEIGHBORS];
11106 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11107 for (int n = 0; n < nbNeighbors; n++)
11109 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11110 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11111 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11113 DownIdType face(downIds[n], downTypes[n]);
11114 if (!faceDomains.count(face))
11115 faceDomains[face] = emptyMap; // create an empty entry for face
11116 if (!faceDomains[face].count(idom))
11118 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11119 celldom[vtkId] = idom;
11120 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11127 //MESSAGE("Number of shared faces " << faceDomains.size());
11128 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11130 // --- explore the shared faces domain by domain,
11131 // explore the nodes of the face and see if they belong to a cell in the domain,
11132 // which has only a node or an edge on the border (not a shared face)
11134 for (int idomain = 0; idomain < theElems.size(); idomain++)
11136 //MESSAGE("Domain " << idomain);
11137 const TIDSortedElemSet& domain = theElems[idomain];
11138 itface = faceDomains.begin();
11139 for (; itface != faceDomains.end(); ++itface)
11141 std::map<int, int> domvol = itface->second;
11142 if (!domvol.count(idomain))
11144 DownIdType face = itface->first;
11145 //MESSAGE(" --- face " << face.cellId);
11146 std::set<int> oldNodes;
11148 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11149 std::set<int>::iterator itn = oldNodes.begin();
11150 for (; itn != oldNodes.end(); ++itn)
11153 //MESSAGE(" node " << oldId);
11154 std::set<int> cells;
11156 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11157 for (int i=0; i<l.ncells; i++)
11159 int vtkId = l.cells[i];
11160 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11161 if (!domain.count(anElem))
11163 int vtkType = grid->GetCellType(vtkId);
11164 int downId = grid->CellIdToDownId(vtkId);
11167 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11168 continue; // not OK at this stage of the algorithm:
11169 //no cells created after BuildDownWardConnectivity
11171 DownIdType aCell(downId, vtkType);
11172 if (celldom.count(vtkId))
11174 cellDomains[aCell][idomain] = vtkId;
11175 celldom[vtkId] = idomain;
11176 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11182 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11183 // for each shared face, get the nodes
11184 // for each node, for each domain of the face, create a clone of the node
11186 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11187 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11188 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11190 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11191 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11192 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11194 for (int idomain = 0; idomain < theElems.size(); idomain++)
11196 itface = faceDomains.begin();
11197 for (; itface != faceDomains.end(); ++itface)
11199 std::map<int, int> domvol = itface->second;
11200 if (!domvol.count(idomain))
11202 DownIdType face = itface->first;
11203 //MESSAGE(" --- face " << face.cellId);
11204 std::set<int> oldNodes;
11206 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11207 // bool isMultipleDetected = false;
11208 std::set<int>::iterator itn = oldNodes.begin();
11209 for (; itn != oldNodes.end(); ++itn)
11212 //MESSAGE(" node " << oldId);
11213 if (!nodeDomains.count(oldId))
11214 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11215 if (nodeDomains[oldId].empty())
11216 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11217 std::map<int, int>::iterator itdom = domvol.begin();
11218 for (; itdom != domvol.end(); ++itdom)
11220 int idom = itdom->first;
11221 //MESSAGE(" domain " << idom);
11222 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11224 if (nodeDomains[oldId].size() >= 2) // a multiple node
11226 vector<int> orderedDoms;
11227 //MESSAGE("multiple node " << oldId);
11228 // isMultipleDetected =true;
11229 if (mutipleNodes.count(oldId))
11230 orderedDoms = mutipleNodes[oldId];
11233 map<int,int>::iterator it = nodeDomains[oldId].begin();
11234 for (; it != nodeDomains[oldId].end(); ++it)
11235 orderedDoms.push_back(it->first);
11237 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11238 //stringstream txt;
11239 //for (int i=0; i<orderedDoms.size(); i++)
11240 // txt << orderedDoms[i] << " ";
11241 //MESSAGE("orderedDoms " << txt.str());
11242 mutipleNodes[oldId] = orderedDoms;
11244 double *coords = grid->GetPoint(oldId);
11245 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11246 int newId = newNode->getVtkId();
11247 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11248 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
11250 // if (nodeDomains[oldId].size() >= 3)
11252 // //MESSAGE("confirm multiple node " << oldId);
11253 // isMultipleDetected =true;
11260 for (int idomain = 0; idomain < theElems.size(); idomain++)
11262 itface = faceDomains.begin();
11263 for (; itface != faceDomains.end(); ++itface)
11265 std::map<int, int> domvol = itface->second;
11266 if (!domvol.count(idomain))
11268 DownIdType face = itface->first;
11269 //MESSAGE(" --- face " << face.cellId);
11270 std::set<int> oldNodes;
11272 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11273 int nbMultipleNodes = 0;
11274 std::set<int>::iterator itn = oldNodes.begin();
11275 for (; itn != oldNodes.end(); ++itn)
11278 if (mutipleNodes.count(oldId))
11281 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11283 //MESSAGE("multiple Nodes detected on a shared face");
11284 int downId = itface->first.cellId;
11285 unsigned char cellType = itface->first.cellType;
11286 // --- shared edge or shared face ?
11287 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11290 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11291 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11292 if (mutipleNodes.count(nodes[i]))
11293 if (!mutipleNodesToFace.count(nodes[i]))
11294 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11296 else // shared face (between two volumes)
11298 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11299 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11300 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11301 for (int ie =0; ie < nbEdges; ie++)
11304 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11305 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11307 vector<int> vn0 = mutipleNodes[nodes[0]];
11308 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11310 for (int i0 = 0; i0 < vn0.size(); i0++)
11311 for (int i1 = 0; i1 < vn1.size(); i1++)
11312 if (vn0[i0] == vn1[i1])
11313 doms.push_back(vn0[i0]);
11314 if (doms.size() >2)
11316 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11317 double *coords = grid->GetPoint(nodes[0]);
11318 gp_Pnt p0(coords[0], coords[1], coords[2]);
11319 coords = grid->GetPoint(nodes[nbNodes - 1]);
11320 gp_Pnt p1(coords[0], coords[1], coords[2]);
11322 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11323 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11324 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11325 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11326 for (int id=0; id < doms.size(); id++)
11328 int idom = doms[id];
11329 for (int ivol=0; ivol<nbvol; ivol++)
11331 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11332 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11333 if (theElems[idom].count(elem))
11335 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11336 domvol[idom] = svol;
11337 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11339 vtkIdType npts = 0;
11340 vtkIdType* pts = 0;
11341 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11342 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11345 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11346 angleDom[idom] = 0;
11350 gp_Pnt g(values[0], values[1], values[2]);
11351 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11352 //MESSAGE(" angle=" << angleDom[idom]);
11358 map<double, int> sortedDom; // sort domains by angle
11359 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11360 sortedDom[ia->second] = ia->first;
11361 vector<int> vnodes;
11363 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11365 vdom.push_back(ib->second);
11366 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11368 for (int ino = 0; ino < nbNodes; ino++)
11369 vnodes.push_back(nodes[ino]);
11370 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11379 // --- iterate on shared faces (volumes to modify, face to extrude)
11380 // get node id's of the face (id SMDS = id VTK)
11381 // create flat element with old and new nodes if requested
11383 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11384 // (domain1 X domain2) = domain1 + MAXINT*domain2
11386 std::map<int, std::map<long,int> > nodeQuadDomains;
11387 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11389 if (createJointElems)
11391 itface = faceDomains.begin();
11392 for (; itface != faceDomains.end(); ++itface)
11394 DownIdType face = itface->first;
11395 std::set<int> oldNodes;
11396 std::set<int>::iterator itn;
11398 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11400 std::map<int, int> domvol = itface->second;
11401 std::map<int, int>::iterator itdom = domvol.begin();
11402 int dom1 = itdom->first;
11403 int vtkVolId = itdom->second;
11405 int dom2 = itdom->first;
11406 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11408 stringstream grpname;
11411 grpname << dom1 << "_" << dom2;
11413 grpname << dom2 << "_" << dom1;
11415 string namegrp = grpname.str();
11416 if (!mapOfJunctionGroups.count(namegrp))
11417 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11418 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11420 sgrp->Add(vol->GetID());
11424 // --- create volumes on multiple domain intersection if requested
11425 // iterate on mutipleNodesToFace
11426 // iterate on edgesMultiDomains
11428 if (createJointElems)
11430 // --- iterate on mutipleNodesToFace
11432 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11433 for (; itn != mutipleNodesToFace.end(); ++itn)
11435 int node = itn->first;
11436 vector<int> orderDom = itn->second;
11437 vector<vtkIdType> orderedNodes;
11438 for (int idom = 0; idom <orderDom.size(); idom++)
11439 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11440 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11442 stringstream grpname;
11444 grpname << 0 << "_" << 0;
11446 string namegrp = grpname.str();
11447 if (!mapOfJunctionGroups.count(namegrp))
11448 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11449 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11451 sgrp->Add(face->GetID());
11454 // --- iterate on edgesMultiDomains
11456 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11457 for (; ite != edgesMultiDomains.end(); ++ite)
11459 vector<int> nodes = ite->first;
11460 vector<int> orderDom = ite->second;
11461 vector<vtkIdType> orderedNodes;
11462 if (nodes.size() == 2)
11464 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11465 for (int ino=0; ino < nodes.size(); ino++)
11466 if (orderDom.size() == 3)
11467 for (int idom = 0; idom <orderDom.size(); idom++)
11468 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11470 for (int idom = orderDom.size()-1; idom >=0; idom--)
11471 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11472 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11474 stringstream grpname;
11476 grpname << 0 << "_" << 0;
11478 string namegrp = grpname.str();
11479 if (!mapOfJunctionGroups.count(namegrp))
11480 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11481 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11483 sgrp->Add(vol->GetID());
11487 MESSAGE("Quadratic multiple joints not implemented");
11488 // TODO quadratic nodes
11493 // --- list the explicit faces and edges of the mesh that need to be modified,
11494 // i.e. faces and edges built with one or more duplicated nodes.
11495 // associate these faces or edges to their corresponding domain.
11496 // only the first domain found is kept when a face or edge is shared
11498 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11499 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11500 faceOrEdgeDom.clear();
11503 for (int idomain = 0; idomain < theElems.size(); idomain++)
11505 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11506 for (; itnod != nodeDomains.end(); ++itnod)
11508 int oldId = itnod->first;
11509 //MESSAGE(" node " << oldId);
11510 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11511 for (int i = 0; i < l.ncells; i++)
11513 int vtkId = l.cells[i];
11514 int vtkType = grid->GetCellType(vtkId);
11515 int downId = grid->CellIdToDownId(vtkId);
11517 continue; // new cells: not to be modified
11518 DownIdType aCell(downId, vtkType);
11519 int volParents[1000];
11520 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11521 for (int j = 0; j < nbvol; j++)
11522 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11523 if (!feDom.count(vtkId))
11525 feDom[vtkId] = idomain;
11526 faceOrEdgeDom[aCell] = emptyMap;
11527 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11528 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11529 // << " type " << vtkType << " downId " << downId);
11535 // --- iterate on shared faces (volumes to modify, face to extrude)
11536 // get node id's of the face
11537 // replace old nodes by new nodes in volumes, and update inverse connectivity
11539 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11540 for (int m=0; m<3; m++)
11542 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11543 itface = (*amap).begin();
11544 for (; itface != (*amap).end(); ++itface)
11546 DownIdType face = itface->first;
11547 std::set<int> oldNodes;
11548 std::set<int>::iterator itn;
11550 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11551 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11552 std::map<int, int> localClonedNodeIds;
11554 std::map<int, int> domvol = itface->second;
11555 std::map<int, int>::iterator itdom = domvol.begin();
11556 for (; itdom != domvol.end(); ++itdom)
11558 int idom = itdom->first;
11559 int vtkVolId = itdom->second;
11560 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11561 localClonedNodeIds.clear();
11562 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11565 if (nodeDomains[oldId].count(idom))
11567 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11568 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11571 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11576 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11577 grid->BuildLinks();
11585 * \brief Double nodes on some external faces and create flat elements.
11586 * Flat elements are mainly used by some types of mechanic calculations.
11588 * Each group of the list must be constituted of faces.
11589 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11590 * @param theElems - list of groups of faces, where a group of faces is a set of
11591 * SMDS_MeshElements sorted by Id.
11592 * @return TRUE if operation has been completed successfully, FALSE otherwise
11594 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11596 MESSAGE("-------------------------------------------------");
11597 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11598 MESSAGE("-------------------------------------------------");
11600 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11602 // --- For each group of faces
11603 // duplicate the nodes, create a flat element based on the face
11604 // replace the nodes of the faces by their clones
11606 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11607 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11608 clonedNodes.clear();
11609 intermediateNodes.clear();
11610 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11611 mapOfJunctionGroups.clear();
11613 for (int idom = 0; idom < theElems.size(); idom++)
11615 const TIDSortedElemSet& domain = theElems[idom];
11616 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11617 for (; elemItr != domain.end(); ++elemItr)
11619 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11620 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11623 // MESSAGE("aFace=" << aFace->GetID());
11624 bool isQuad = aFace->IsQuadratic();
11625 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11627 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11629 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11630 while (nodeIt->more())
11632 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11633 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11635 ln2.push_back(node);
11637 ln0.push_back(node);
11639 const SMDS_MeshNode* clone = 0;
11640 if (!clonedNodes.count(node))
11642 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11643 clonedNodes[node] = clone;
11646 clone = clonedNodes[node];
11649 ln3.push_back(clone);
11651 ln1.push_back(clone);
11653 const SMDS_MeshNode* inter = 0;
11654 if (isQuad && (!isMedium))
11656 if (!intermediateNodes.count(node))
11658 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11659 intermediateNodes[node] = inter;
11662 inter = intermediateNodes[node];
11663 ln4.push_back(inter);
11667 // --- extrude the face
11669 vector<const SMDS_MeshNode*> ln;
11670 SMDS_MeshVolume* vol = 0;
11671 vtkIdType aType = aFace->GetVtkType();
11675 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11676 // MESSAGE("vol prism " << vol->GetID());
11677 ln.push_back(ln1[0]);
11678 ln.push_back(ln1[1]);
11679 ln.push_back(ln1[2]);
11682 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11683 // MESSAGE("vol hexa " << vol->GetID());
11684 ln.push_back(ln1[0]);
11685 ln.push_back(ln1[1]);
11686 ln.push_back(ln1[2]);
11687 ln.push_back(ln1[3]);
11689 case VTK_QUADRATIC_TRIANGLE:
11690 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11691 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11692 // MESSAGE("vol quad prism " << vol->GetID());
11693 ln.push_back(ln1[0]);
11694 ln.push_back(ln1[1]);
11695 ln.push_back(ln1[2]);
11696 ln.push_back(ln3[0]);
11697 ln.push_back(ln3[1]);
11698 ln.push_back(ln3[2]);
11700 case VTK_QUADRATIC_QUAD:
11701 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11702 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11703 // ln4[0], ln4[1], ln4[2], ln4[3]);
11704 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11705 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11706 ln4[0], ln4[1], ln4[2], ln4[3]);
11707 // MESSAGE("vol quad hexa " << vol->GetID());
11708 ln.push_back(ln1[0]);
11709 ln.push_back(ln1[1]);
11710 ln.push_back(ln1[2]);
11711 ln.push_back(ln1[3]);
11712 ln.push_back(ln3[0]);
11713 ln.push_back(ln3[1]);
11714 ln.push_back(ln3[2]);
11715 ln.push_back(ln3[3]);
11725 stringstream grpname;
11729 string namegrp = grpname.str();
11730 if (!mapOfJunctionGroups.count(namegrp))
11731 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11732 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11734 sgrp->Add(vol->GetID());
11737 // --- modify the face
11739 aFace->ChangeNodes(&ln[0], ln.size());
11746 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11747 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11748 * groups of faces to remove inside the object, (idem edges).
11749 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11751 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11752 const TopoDS_Shape& theShape,
11753 SMESH_NodeSearcher* theNodeSearcher,
11754 const char* groupName,
11755 std::vector<double>& nodesCoords,
11756 std::vector<std::vector<int> >& listOfListOfNodes)
11758 MESSAGE("--------------------------------");
11759 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11760 MESSAGE("--------------------------------");
11762 // --- zone of volumes to remove is given :
11763 // 1 either by a geom shape (one or more vertices) and a radius,
11764 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11765 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11766 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11767 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11768 // defined by it's name.
11770 SMESHDS_GroupBase* groupDS = 0;
11771 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11772 while ( groupIt->more() )
11775 SMESH_Group * group = groupIt->next();
11776 if ( !group ) continue;
11777 groupDS = group->GetGroupDS();
11778 if ( !groupDS || groupDS->IsEmpty() ) continue;
11779 std::string grpName = group->GetName();
11780 if (grpName == groupName)
11784 bool isNodeGroup = false;
11785 bool isNodeCoords = false;
11788 if (groupDS->GetType() != SMDSAbs_Node)
11790 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11793 if (nodesCoords.size() > 0)
11794 isNodeCoords = true; // a list o nodes given by their coordinates
11796 // --- define groups to build
11798 int idg; // --- group of SMDS volumes
11799 string grpvName = groupName;
11800 grpvName += "_vol";
11801 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11804 MESSAGE("group not created " << grpvName);
11807 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11809 int idgs; // --- group of SMDS faces on the skin
11810 string grpsName = groupName;
11811 grpsName += "_skin";
11812 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11815 MESSAGE("group not created " << grpsName);
11818 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11820 int idgi; // --- group of SMDS faces internal (several shapes)
11821 string grpiName = groupName;
11822 grpiName += "_internalFaces";
11823 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11826 MESSAGE("group not created " << grpiName);
11829 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11831 int idgei; // --- group of SMDS faces internal (several shapes)
11832 string grpeiName = groupName;
11833 grpeiName += "_internalEdges";
11834 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11837 MESSAGE("group not created " << grpeiName);
11840 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11842 // --- build downward connectivity
11844 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11845 meshDS->BuildDownWardConnectivity(true);
11846 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11848 // --- set of volumes detected inside
11850 std::set<int> setOfInsideVol;
11851 std::set<int> setOfVolToCheck;
11853 std::vector<gp_Pnt> gpnts;
11856 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11858 MESSAGE("group of nodes provided");
11859 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11860 while ( elemIt->more() )
11862 const SMDS_MeshElement* elem = elemIt->next();
11865 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11868 SMDS_MeshElement* vol = 0;
11869 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11870 while (volItr->more())
11872 vol = (SMDS_MeshElement*)volItr->next();
11873 setOfInsideVol.insert(vol->getVtkId());
11874 sgrp->Add(vol->GetID());
11878 else if (isNodeCoords)
11880 MESSAGE("list of nodes coordinates provided");
11883 while (i < nodesCoords.size()-2)
11885 double x = nodesCoords[i++];
11886 double y = nodesCoords[i++];
11887 double z = nodesCoords[i++];
11888 gp_Pnt p = gp_Pnt(x, y ,z);
11889 gpnts.push_back(p);
11890 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
11893 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11895 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11896 TopTools_IndexedMapOfShape vertexMap;
11897 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11898 gp_Pnt p = gp_Pnt(0,0,0);
11899 if (vertexMap.Extent() < 1)
11902 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11904 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11905 p = BRep_Tool::Pnt(vertex);
11906 gpnts.push_back(p);
11907 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11911 if (gpnts.size() > 0)
11914 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11916 nodeId = startNode->GetID();
11917 MESSAGE("nodeId " << nodeId);
11919 double radius2 = radius*radius;
11920 MESSAGE("radius2 " << radius2);
11922 // --- volumes on start node
11924 setOfVolToCheck.clear();
11925 SMDS_MeshElement* startVol = 0;
11926 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11927 while (volItr->more())
11929 startVol = (SMDS_MeshElement*)volItr->next();
11930 setOfVolToCheck.insert(startVol->getVtkId());
11932 if (setOfVolToCheck.empty())
11934 MESSAGE("No volumes found");
11938 // --- starting with central volumes then their neighbors, check if they are inside
11939 // or outside the domain, until no more new neighbor volume is inside.
11940 // Fill the group of inside volumes
11942 std::map<int, double> mapOfNodeDistance2;
11943 mapOfNodeDistance2.clear();
11944 std::set<int> setOfOutsideVol;
11945 while (!setOfVolToCheck.empty())
11947 std::set<int>::iterator it = setOfVolToCheck.begin();
11949 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11950 bool volInside = false;
11951 vtkIdType npts = 0;
11952 vtkIdType* pts = 0;
11953 grid->GetCellPoints(vtkId, npts, pts);
11954 for (int i=0; i<npts; i++)
11956 double distance2 = 0;
11957 if (mapOfNodeDistance2.count(pts[i]))
11959 distance2 = mapOfNodeDistance2[pts[i]];
11960 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11964 double *coords = grid->GetPoint(pts[i]);
11965 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11967 for (int j=0; j<gpnts.size(); j++)
11969 double d2 = aPoint.SquareDistance(gpnts[j]);
11970 if (d2 < distance2)
11973 if (distance2 < radius2)
11977 mapOfNodeDistance2[pts[i]] = distance2;
11978 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11980 if (distance2 < radius2)
11982 volInside = true; // one or more nodes inside the domain
11983 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11989 setOfInsideVol.insert(vtkId);
11990 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11991 int neighborsVtkIds[NBMAXNEIGHBORS];
11992 int downIds[NBMAXNEIGHBORS];
11993 unsigned char downTypes[NBMAXNEIGHBORS];
11994 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11995 for (int n = 0; n < nbNeighbors; n++)
11996 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11997 setOfVolToCheck.insert(neighborsVtkIds[n]);
12001 setOfOutsideVol.insert(vtkId);
12002 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12004 setOfVolToCheck.erase(vtkId);
12008 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12009 // If yes, add the volume to the inside set
12011 bool addedInside = true;
12012 std::set<int> setOfVolToReCheck;
12013 while (addedInside)
12015 MESSAGE(" --------------------------- re check");
12016 addedInside = false;
12017 std::set<int>::iterator itv = setOfInsideVol.begin();
12018 for (; itv != setOfInsideVol.end(); ++itv)
12021 int neighborsVtkIds[NBMAXNEIGHBORS];
12022 int downIds[NBMAXNEIGHBORS];
12023 unsigned char downTypes[NBMAXNEIGHBORS];
12024 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12025 for (int n = 0; n < nbNeighbors; n++)
12026 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12027 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12029 setOfVolToCheck = setOfVolToReCheck;
12030 setOfVolToReCheck.clear();
12031 while (!setOfVolToCheck.empty())
12033 std::set<int>::iterator it = setOfVolToCheck.begin();
12035 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12037 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12038 int countInside = 0;
12039 int neighborsVtkIds[NBMAXNEIGHBORS];
12040 int downIds[NBMAXNEIGHBORS];
12041 unsigned char downTypes[NBMAXNEIGHBORS];
12042 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12043 for (int n = 0; n < nbNeighbors; n++)
12044 if (setOfInsideVol.count(neighborsVtkIds[n]))
12046 MESSAGE("countInside " << countInside);
12047 if (countInside > 1)
12049 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12050 setOfInsideVol.insert(vtkId);
12051 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12052 addedInside = true;
12055 setOfVolToReCheck.insert(vtkId);
12057 setOfVolToCheck.erase(vtkId);
12061 // --- map of Downward faces at the boundary, inside the global volume
12062 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12063 // fill group of SMDS faces inside the volume (when several volume shapes)
12064 // fill group of SMDS faces on the skin of the global volume (if skin)
12066 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12067 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12068 std::set<int>::iterator it = setOfInsideVol.begin();
12069 for (; it != setOfInsideVol.end(); ++it)
12072 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12073 int neighborsVtkIds[NBMAXNEIGHBORS];
12074 int downIds[NBMAXNEIGHBORS];
12075 unsigned char downTypes[NBMAXNEIGHBORS];
12076 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12077 for (int n = 0; n < nbNeighbors; n++)
12079 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12080 if (neighborDim == 3)
12082 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12084 DownIdType face(downIds[n], downTypes[n]);
12085 boundaryFaces[face] = vtkId;
12087 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12088 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12089 if (vtkFaceId >= 0)
12091 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12092 // find also the smds edges on this face
12093 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12094 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12095 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12096 for (int i = 0; i < nbEdges; i++)
12098 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12099 if (vtkEdgeId >= 0)
12100 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12104 else if (neighborDim == 2) // skin of the volume
12106 DownIdType face(downIds[n], downTypes[n]);
12107 skinFaces[face] = vtkId;
12108 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12109 if (vtkFaceId >= 0)
12110 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12115 // --- identify the edges constituting the wire of each subshape on the skin
12116 // define polylines with the nodes of edges, equivalent to wires
12117 // project polylines on subshapes, and partition, to get geom faces
12119 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12120 std::set<int> emptySet;
12122 std::set<int> shapeIds;
12124 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12125 while (itelem->more())
12127 const SMDS_MeshElement *elem = itelem->next();
12128 int shapeId = elem->getshapeId();
12129 int vtkId = elem->getVtkId();
12130 if (!shapeIdToVtkIdSet.count(shapeId))
12132 shapeIdToVtkIdSet[shapeId] = emptySet;
12133 shapeIds.insert(shapeId);
12135 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12138 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12139 std::set<DownIdType, DownIdCompare> emptyEdges;
12140 emptyEdges.clear();
12142 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12143 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12145 int shapeId = itShape->first;
12146 MESSAGE(" --- Shape ID --- "<< shapeId);
12147 shapeIdToEdges[shapeId] = emptyEdges;
12149 std::vector<int> nodesEdges;
12151 std::set<int>::iterator its = itShape->second.begin();
12152 for (; its != itShape->second.end(); ++its)
12155 MESSAGE(" " << vtkId);
12156 int neighborsVtkIds[NBMAXNEIGHBORS];
12157 int downIds[NBMAXNEIGHBORS];
12158 unsigned char downTypes[NBMAXNEIGHBORS];
12159 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12160 for (int n = 0; n < nbNeighbors; n++)
12162 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12164 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12165 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12166 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12168 DownIdType edge(downIds[n], downTypes[n]);
12169 if (!shapeIdToEdges[shapeId].count(edge))
12171 shapeIdToEdges[shapeId].insert(edge);
12173 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12174 nodesEdges.push_back(vtkNodeId[0]);
12175 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12176 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12182 std::list<int> order;
12184 if (nodesEdges.size() > 0)
12186 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12187 nodesEdges[0] = -1;
12188 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12189 nodesEdges[1] = -1; // do not reuse this edge
12193 int nodeTofind = order.back(); // try first to push back
12195 for (i = 0; i<nodesEdges.size(); i++)
12196 if (nodesEdges[i] == nodeTofind)
12198 if (i == nodesEdges.size())
12199 found = false; // no follower found on back
12202 if (i%2) // odd ==> use the previous one
12203 if (nodesEdges[i-1] < 0)
12207 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12208 nodesEdges[i-1] = -1;
12210 else // even ==> use the next one
12211 if (nodesEdges[i+1] < 0)
12215 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12216 nodesEdges[i+1] = -1;
12221 // try to push front
12223 nodeTofind = order.front(); // try to push front
12224 for (i = 0; i<nodesEdges.size(); i++)
12225 if (nodesEdges[i] == nodeTofind)
12227 if (i == nodesEdges.size())
12229 found = false; // no predecessor found on front
12232 if (i%2) // odd ==> use the previous one
12233 if (nodesEdges[i-1] < 0)
12237 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12238 nodesEdges[i-1] = -1;
12240 else // even ==> use the next one
12241 if (nodesEdges[i+1] < 0)
12245 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12246 nodesEdges[i+1] = -1;
12252 std::vector<int> nodes;
12253 nodes.push_back(shapeId);
12254 std::list<int>::iterator itl = order.begin();
12255 for (; itl != order.end(); itl++)
12257 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12258 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12260 listOfListOfNodes.push_back(nodes);
12263 // partition geom faces with blocFissure
12264 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12265 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12271 //================================================================================
12273 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12274 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12275 * \return TRUE if operation has been completed successfully, FALSE otherwise
12277 //================================================================================
12279 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12281 // iterates on volume elements and detect all free faces on them
12282 SMESHDS_Mesh* aMesh = GetMeshDS();
12285 //bool res = false;
12286 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12287 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12290 const SMDS_MeshVolume* volume = vIt->next();
12291 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12292 vTool.SetExternalNormal();
12293 //const bool isPoly = volume->IsPoly();
12294 const int iQuad = volume->IsQuadratic();
12295 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12297 if (!vTool.IsFreeFace(iface))
12300 vector<const SMDS_MeshNode *> nodes;
12301 int nbFaceNodes = vTool.NbFaceNodes(iface);
12302 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12304 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12305 nodes.push_back(faceNodes[inode]);
12306 if (iQuad) { // add medium nodes
12307 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12308 nodes.push_back(faceNodes[inode]);
12309 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12310 nodes.push_back(faceNodes[8]);
12312 // add new face based on volume nodes
12313 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12315 continue; // face already exsist
12317 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12321 return ( nbFree==(nbExisted+nbCreated) );
12326 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12328 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12330 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12333 //================================================================================
12335 * \brief Creates missing boundary elements
12336 * \param elements - elements whose boundary is to be checked
12337 * \param dimension - defines type of boundary elements to create
12338 * \param group - a group to store created boundary elements in
12339 * \param targetMesh - a mesh to store created boundary elements in
12340 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12341 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12342 * boundary elements will be copied into the targetMesh
12343 * \param toAddExistingBondary - if true, not only new but also pre-existing
12344 * boundary elements will be added into the new group
12345 * \param aroundElements - if true, elements will be created on boundary of given
12346 * elements else, on boundary of the whole mesh.
12347 * \return nb of added boundary elements
12349 //================================================================================
12351 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12352 Bnd_Dimension dimension,
12353 SMESH_Group* group/*=0*/,
12354 SMESH_Mesh* targetMesh/*=0*/,
12355 bool toCopyElements/*=false*/,
12356 bool toCopyExistingBoundary/*=false*/,
12357 bool toAddExistingBondary/*= false*/,
12358 bool aroundElements/*= false*/)
12360 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12361 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12362 // hope that all elements are of the same type, do not check them all
12363 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12364 throw SALOME_Exception(LOCALIZED("wrong element type"));
12367 toCopyElements = toCopyExistingBoundary = false;
12369 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12370 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12371 int nbAddedBnd = 0;
12373 // editor adding present bnd elements and optionally holding elements to add to the group
12374 SMESH_MeshEditor* presentEditor;
12375 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12376 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12378 SMESH_MesherHelper helper( *myMesh );
12379 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12380 SMDS_VolumeTool vTool;
12381 TIDSortedElemSet avoidSet;
12382 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12385 typedef vector<const SMDS_MeshNode*> TConnectivity;
12387 SMDS_ElemIteratorPtr eIt;
12388 if (elements.empty())
12389 eIt = aMesh->elementsIterator(elemType);
12391 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12393 while (eIt->more())
12395 const SMDS_MeshElement* elem = eIt->next();
12396 const int iQuad = elem->IsQuadratic();
12398 // ------------------------------------------------------------------------------------
12399 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12400 // ------------------------------------------------------------------------------------
12401 vector<const SMDS_MeshElement*> presentBndElems;
12402 vector<TConnectivity> missingBndElems;
12403 TConnectivity nodes;
12404 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12406 vTool.SetExternalNormal();
12407 const SMDS_MeshElement* otherVol = 0;
12408 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12410 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12411 ( !aroundElements || elements.count( otherVol )))
12413 const int nbFaceNodes = vTool.NbFaceNodes(iface);
12414 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12415 if ( missType == SMDSAbs_Edge ) // boundary edges
12417 nodes.resize( 2+iQuad );
12418 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12420 for ( int j = 0; j < nodes.size(); ++j )
12422 if ( const SMDS_MeshElement* edge =
12423 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12424 presentBndElems.push_back( edge );
12426 missingBndElems.push_back( nodes );
12429 else // boundary face
12432 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12433 nodes.push_back( nn[inode] );
12434 if (iQuad) // add medium nodes
12435 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12436 nodes.push_back( nn[inode] );
12437 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12439 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12441 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12442 SMDSAbs_Face, /*noMedium=*/false ))
12443 presentBndElems.push_back( f );
12445 missingBndElems.push_back( nodes );
12447 if ( targetMesh != myMesh )
12449 // add 1D elements on face boundary to be added to a new mesh
12450 const SMDS_MeshElement* edge;
12451 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12454 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12456 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12457 if ( edge && avoidSet.insert( edge ).second )
12458 presentBndElems.push_back( edge );
12464 else // elem is a face ------------------------------------------
12466 avoidSet.clear(), avoidSet.insert( elem );
12467 int nbNodes = elem->NbCornerNodes();
12468 nodes.resize( 2 /*+ iQuad*/);
12469 for ( int i = 0; i < nbNodes; i++ )
12471 nodes[0] = elem->GetNode(i);
12472 nodes[1] = elem->GetNode((i+1)%nbNodes);
12473 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12474 continue; // not free link
12477 //nodes[2] = elem->GetNode( i + nbNodes );
12478 if ( const SMDS_MeshElement* edge =
12479 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
12480 presentBndElems.push_back( edge );
12482 missingBndElems.push_back( nodes );
12486 // ---------------------------------
12487 // 2. Add missing boundary elements
12488 // ---------------------------------
12489 if ( targetMesh != myMesh )
12490 // instead of making a map of nodes in this mesh and targetMesh,
12491 // we create nodes with same IDs.
12492 for ( int i = 0; i < missingBndElems.size(); ++i )
12494 TConnectivity& srcNodes = missingBndElems[i];
12495 TConnectivity nodes( srcNodes.size() );
12496 for ( inode = 0; inode < nodes.size(); ++inode )
12497 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12498 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12500 /*noMedium=*/false))
12502 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12506 for ( int i = 0; i < missingBndElems.size(); ++i )
12508 TConnectivity& nodes = missingBndElems[i];
12509 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12511 /*noMedium=*/false))
12513 SMDS_MeshElement* elem =
12514 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12517 // try to set a new element to a shape
12518 if ( myMesh->HasShapeToMesh() )
12521 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12522 const int nbN = nodes.size() / (iQuad+1 );
12523 for ( inode = 0; inode < nbN && ok; ++inode )
12525 pair<int, TopAbs_ShapeEnum> i_stype =
12526 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12527 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12528 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12530 if ( ok && mediumShapes.size() > 1 )
12532 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12533 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12534 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12536 if (( ok = ( stype_i->first != stype_i_0.first )))
12537 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12538 aMesh->IndexToShape( stype_i_0.second ));
12541 if ( ok && mediumShapes.begin()->first == missShapeType )
12542 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12546 // ----------------------------------
12547 // 3. Copy present boundary elements
12548 // ----------------------------------
12549 if ( toCopyExistingBoundary )
12550 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12552 const SMDS_MeshElement* e = presentBndElems[i];
12553 TConnectivity nodes( e->NbNodes() );
12554 for ( inode = 0; inode < nodes.size(); ++inode )
12555 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12556 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12558 else // store present elements to add them to a group
12559 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12561 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12564 } // loop on given elements
12566 // ---------------------------------------------
12567 // 4. Fill group with boundary elements
12568 // ---------------------------------------------
12571 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12572 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12573 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12575 tgtEditor.myLastCreatedElems.Clear();
12576 tgtEditor2.myLastCreatedElems.Clear();
12578 // -----------------------
12579 // 5. Copy given elements
12580 // -----------------------
12581 if ( toCopyElements && targetMesh != myMesh )
12583 if (elements.empty())
12584 eIt = aMesh->elementsIterator(elemType);
12586 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
12587 while (eIt->more())
12589 const SMDS_MeshElement* elem = eIt->next();
12590 TConnectivity nodes( elem->NbNodes() );
12591 for ( inode = 0; inode < nodes.size(); ++inode )
12592 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12593 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12595 tgtEditor.myLastCreatedElems.Clear();