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 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4035 int nbInitElems = 0;
4036 const SMDS_MeshElement* el = 0;
4037 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4038 while ( eIt->more() && nbInitElems < 2 ) {
4040 SMDSAbs_ElementType type = el->GetType();
4041 if ( type == SMDSAbs_Volume || type < highType ) continue;
4042 if ( type > highType ) {
4046 nbInitElems += elemSet.count(el);
4048 if ( nbInitElems < 2 ) {
4049 bool NotCreateEdge = el && el->IsMediumNode(node);
4050 if(!NotCreateEdge) {
4051 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4052 list<const SMDS_MeshElement*> newEdges;
4053 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4058 // Make a ceiling for each element ie an equal element of last new nodes.
4059 // Find free links of faces - make edges and sweep them into faces.
4061 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4062 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4063 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4065 const SMDS_MeshElement* elem = itElem->first;
4066 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4068 if(itElem->second.size()==0) continue;
4070 const bool isQuadratic = elem->IsQuadratic();
4072 if ( elem->GetType() == SMDSAbs_Edge ) {
4073 // create a ceiling edge
4074 if ( !isQuadratic ) {
4075 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4076 vecNewNodes[ 1 ]->second.back())) {
4077 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4078 vecNewNodes[ 1 ]->second.back()));
4079 srcElements.Append( myLastCreatedElems.Last() );
4083 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4084 vecNewNodes[ 1 ]->second.back(),
4085 vecNewNodes[ 2 ]->second.back())) {
4086 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4087 vecNewNodes[ 1 ]->second.back(),
4088 vecNewNodes[ 2 ]->second.back()));
4089 srcElements.Append( myLastCreatedElems.Last() );
4093 if ( elem->GetType() != SMDSAbs_Face )
4096 bool hasFreeLinks = false;
4098 TIDSortedElemSet avoidSet;
4099 avoidSet.insert( elem );
4101 set<const SMDS_MeshNode*> aFaceLastNodes;
4102 int iNode, nbNodes = vecNewNodes.size();
4103 if ( !isQuadratic ) {
4104 // loop on the face nodes
4105 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4106 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4107 // look for free links of the face
4108 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4109 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4110 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4111 // check if a link is free
4112 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4113 hasFreeLinks = true;
4114 // make an edge and a ceiling for a new edge
4115 if ( !aMesh->FindEdge( n1, n2 )) {
4116 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // free link edge
4117 srcElements.Append( myLastCreatedElems.Last() );
4119 n1 = vecNewNodes[ iNode ]->second.back();
4120 n2 = vecNewNodes[ iNext ]->second.back();
4121 if ( !aMesh->FindEdge( n1, n2 )) {
4122 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // ceiling edge
4123 srcElements.Append( myLastCreatedElems.Last() );
4128 else { // elem is quadratic face
4129 int nbn = nbNodes/2;
4130 for ( iNode = 0; iNode < nbn; iNode++ ) {
4131 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4132 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4133 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4134 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4135 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4136 // check if a link is free
4137 if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4138 ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4139 ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4140 hasFreeLinks = true;
4141 // make an edge and a ceiling for a new edge
4143 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4144 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4145 srcElements.Append( myLastCreatedElems.Last() );
4147 n1 = vecNewNodes[ iNode ]->second.back();
4148 n2 = vecNewNodes[ iNext ]->second.back();
4149 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4150 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4151 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4152 srcElements.Append( myLastCreatedElems.Last() );
4156 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4157 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4161 // sweep free links into faces
4163 if ( hasFreeLinks ) {
4164 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4165 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4167 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4168 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4169 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4170 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4172 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4173 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4174 std::advance( v, volNb );
4175 // find indices of free faces of a volume and their source edges
4176 list< int > freeInd;
4177 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4178 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4179 int iF, nbF = vTool.NbFaces();
4180 for ( iF = 0; iF < nbF; iF ++ ) {
4181 if (vTool.IsFreeFace( iF ) &&
4182 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4183 initNodeSet != faceNodeSet) // except an initial face
4185 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4187 freeInd.push_back( iF );
4188 // find source edge of a free face iF
4189 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4190 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4191 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4192 initNodeSet.begin(), initNodeSet.end(),
4193 commonNodes.begin());
4194 if ( (*v)->IsQuadratic() )
4195 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4197 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4199 if ( !srcEdges.back() )
4201 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4202 << iF << " of volume #" << vTool.ID() << endl;
4207 if ( freeInd.empty() )
4210 // create faces for all steps;
4211 // if such a face has been already created by sweep of edge,
4212 // assure that its orientation is OK
4213 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4214 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4215 vTool.SetExternalNormal();
4216 const int nextShift = vTool.IsForward() ? +1 : -1;
4217 list< int >::iterator ind = freeInd.begin();
4218 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4219 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4221 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4222 int nbn = vTool.NbFaceNodes( *ind );
4223 const SMDS_MeshElement * f = 0;
4224 if ( nbn == 3 ) ///// triangle
4226 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4228 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4230 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4232 nodes[ 1 + nextShift ] };
4234 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4236 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4240 else if ( nbn == 4 ) ///// quadrangle
4242 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4244 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4246 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4247 nodes[ 2 ], nodes[ 2+nextShift ] };
4249 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4251 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4252 newOrder[ 2 ], newOrder[ 3 ]));
4255 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4257 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4259 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4261 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4263 nodes[2 + 2*nextShift],
4264 nodes[3 - 2*nextShift],
4266 nodes[3 + 2*nextShift]};
4268 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4270 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4278 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4280 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4281 nodes[1], nodes[3], nodes[5], nodes[7] );
4283 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4285 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4286 nodes[4 - 2*nextShift],
4288 nodes[4 + 2*nextShift],
4290 nodes[5 - 2*nextShift],
4292 nodes[5 + 2*nextShift] };
4294 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4296 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4297 newOrder[ 2 ], newOrder[ 3 ],
4298 newOrder[ 4 ], newOrder[ 5 ],
4299 newOrder[ 6 ], newOrder[ 7 ]));
4302 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4304 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4305 SMDSAbs_Face, /*noMedium=*/false);
4307 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4309 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4310 nodes[4 - 2*nextShift],
4312 nodes[4 + 2*nextShift],
4314 nodes[5 - 2*nextShift],
4316 nodes[5 + 2*nextShift],
4319 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4321 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4322 newOrder[ 2 ], newOrder[ 3 ],
4323 newOrder[ 4 ], newOrder[ 5 ],
4324 newOrder[ 6 ], newOrder[ 7 ],
4328 else //////// polygon
4330 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4331 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4333 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4335 if ( !vTool.IsForward() )
4336 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4338 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4340 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4344 while ( srcElements.Length() < myLastCreatedElems.Length() )
4345 srcElements.Append( *srcEdge );
4347 } // loop on free faces
4349 // go to the next volume
4351 while ( iVol++ < nbVolumesByStep ) v++;
4354 } // loop on volumes of one step
4355 } // sweep free links into faces
4357 // Make a ceiling face with a normal external to a volume
4359 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4361 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4363 lastVol.SetExternalNormal();
4364 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4365 int nbn = lastVol.NbFaceNodes( iF );
4367 if (!hasFreeLinks ||
4368 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]))
4369 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] ));
4371 else if ( nbn == 4 )
4373 if (!hasFreeLinks ||
4374 !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]))
4375 myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]));
4377 else if ( nbn == 6 && isQuadratic )
4379 if (!hasFreeLinks ||
4380 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) )
4381 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4],
4382 nodes[1], nodes[3], nodes[5]));
4384 else if ( nbn == 8 && isQuadratic )
4386 if (!hasFreeLinks ||
4387 !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6],
4388 nodes[1], nodes[3], nodes[5], nodes[7]) )
4389 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4390 nodes[1], nodes[3], nodes[5], nodes[7]));
4392 else if ( nbn == 9 && isQuadratic )
4394 if (!hasFreeLinks ||
4395 !aMesh->FindElement(vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4396 SMDSAbs_Face, /*noMedium=*/false) )
4397 myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6],
4398 nodes[1], nodes[3], nodes[5], nodes[7],
4402 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes + nbn );
4403 if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes))
4404 myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes));
4407 while ( srcElements.Length() < myLastCreatedElems.Length() )
4408 srcElements.Append( myLastCreatedElems.Last() );
4410 } // loop on swept elements
4413 //=======================================================================
4414 //function : RotationSweep
4416 //=======================================================================
4418 SMESH_MeshEditor::PGroupIDs
4419 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4420 const gp_Ax1& theAxis,
4421 const double theAngle,
4422 const int theNbSteps,
4423 const double theTol,
4424 const bool theMakeGroups,
4425 const bool theMakeWalls)
4427 myLastCreatedElems.Clear();
4428 myLastCreatedNodes.Clear();
4430 // source elements for each generated one
4431 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4433 MESSAGE( "RotationSweep()");
4435 aTrsf.SetRotation( theAxis, theAngle );
4437 aTrsf2.SetRotation( theAxis, theAngle/2. );
4439 gp_Lin aLine( theAxis );
4440 double aSqTol = theTol * theTol;
4442 SMESHDS_Mesh* aMesh = GetMeshDS();
4444 TNodeOfNodeListMap mapNewNodes;
4445 TElemOfVecOfNnlmiMap mapElemNewNodes;
4446 TElemOfElemListMap newElemsMap;
4448 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4449 myMesh->NbFaces(ORDER_QUADRATIC) +
4450 myMesh->NbVolumes(ORDER_QUADRATIC) );
4452 TIDSortedElemSet::iterator itElem;
4453 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4454 const SMDS_MeshElement* elem = *itElem;
4455 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4457 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4458 newNodesItVec.reserve( elem->NbNodes() );
4460 // loop on elem nodes
4461 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4462 while ( itN->more() )
4464 // check if a node has been already sweeped
4465 const SMDS_MeshNode* node = cast2Node( itN->next() );
4467 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4469 aXYZ.Coord( coord[0], coord[1], coord[2] );
4470 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4472 TNodeOfNodeListMapItr nIt =
4473 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4474 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4475 if ( listNewNodes.empty() )
4477 // check if we are to create medium nodes between corner ones
4478 bool needMediumNodes = false;
4479 if ( isQuadraticMesh )
4481 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4482 while (it->more() && !needMediumNodes )
4484 const SMDS_MeshElement* invElem = it->next();
4485 if ( invElem != elem && !theElems.count( invElem )) continue;
4486 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4487 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4488 needMediumNodes = true;
4493 const SMDS_MeshNode * newNode = node;
4494 for ( int i = 0; i < theNbSteps; i++ ) {
4496 if ( needMediumNodes ) // create a medium node
4498 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4499 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4500 myLastCreatedNodes.Append(newNode);
4501 srcNodes.Append( node );
4502 listNewNodes.push_back( newNode );
4503 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4506 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4508 // create a corner node
4509 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4510 myLastCreatedNodes.Append(newNode);
4511 srcNodes.Append( node );
4512 listNewNodes.push_back( newNode );
4515 listNewNodes.push_back( newNode );
4516 // if ( needMediumNodes )
4517 // listNewNodes.push_back( newNode );
4521 newNodesItVec.push_back( nIt );
4523 // make new elements
4524 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4528 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4530 PGroupIDs newGroupIDs;
4531 if ( theMakeGroups )
4532 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4538 //=======================================================================
4539 //function : CreateNode
4541 //=======================================================================
4542 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4545 const double tolnode,
4546 SMESH_SequenceOfNode& aNodes)
4548 // myLastCreatedElems.Clear();
4549 // myLastCreatedNodes.Clear();
4552 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4554 // try to search in sequence of existing nodes
4555 // if aNodes.Length()>0 we 'nave to use given sequence
4556 // else - use all nodes of mesh
4557 if(aNodes.Length()>0) {
4559 for(i=1; i<=aNodes.Length(); i++) {
4560 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4561 if(P1.Distance(P2)<tolnode)
4562 return aNodes.Value(i);
4566 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4567 while(itn->more()) {
4568 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4569 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4570 if(P1.Distance(P2)<tolnode)
4575 // create new node and return it
4576 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4577 //myLastCreatedNodes.Append(NewNode);
4582 //=======================================================================
4583 //function : ExtrusionSweep
4585 //=======================================================================
4587 SMESH_MeshEditor::PGroupIDs
4588 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4589 const gp_Vec& theStep,
4590 const int theNbSteps,
4591 TElemOfElemListMap& newElemsMap,
4592 const bool theMakeGroups,
4594 const double theTolerance)
4596 ExtrusParam aParams;
4597 aParams.myDir = gp_Dir(theStep);
4598 aParams.myNodes.Clear();
4599 aParams.mySteps = new TColStd_HSequenceOfReal;
4601 for(i=1; i<=theNbSteps; i++)
4602 aParams.mySteps->Append(theStep.Magnitude());
4605 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4609 //=======================================================================
4610 //function : ExtrusionSweep
4612 //=======================================================================
4614 SMESH_MeshEditor::PGroupIDs
4615 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4616 ExtrusParam& theParams,
4617 TElemOfElemListMap& newElemsMap,
4618 const bool theMakeGroups,
4620 const double theTolerance)
4622 myLastCreatedElems.Clear();
4623 myLastCreatedNodes.Clear();
4625 // source elements for each generated one
4626 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4628 SMESHDS_Mesh* aMesh = GetMeshDS();
4630 int nbsteps = theParams.mySteps->Length();
4632 TNodeOfNodeListMap mapNewNodes;
4633 //TNodeOfNodeVecMap mapNewNodes;
4634 TElemOfVecOfNnlmiMap mapElemNewNodes;
4635 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4637 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4638 myMesh->NbFaces(ORDER_QUADRATIC) +
4639 myMesh->NbVolumes(ORDER_QUADRATIC) );
4641 TIDSortedElemSet::iterator itElem;
4642 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4643 // check element type
4644 const SMDS_MeshElement* elem = *itElem;
4645 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4648 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4649 newNodesItVec.reserve( elem->NbNodes() );
4651 // loop on elem nodes
4652 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4653 while ( itN->more() )
4655 // check if a node has been already sweeped
4656 const SMDS_MeshNode* node = cast2Node( itN->next() );
4657 TNodeOfNodeListMap::iterator nIt =
4658 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4659 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4660 if ( listNewNodes.empty() )
4664 // check if we are to create medium nodes between corner ones
4665 bool needMediumNodes = false;
4666 if ( isQuadraticMesh )
4668 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4669 while (it->more() && !needMediumNodes )
4671 const SMDS_MeshElement* invElem = it->next();
4672 if ( invElem != elem && !theElems.count( invElem )) continue;
4673 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4674 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4675 needMediumNodes = true;
4679 double coord[] = { node->X(), node->Y(), node->Z() };
4680 for ( int i = 0; i < nbsteps; i++ )
4682 if ( needMediumNodes ) // create a medium node
4684 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4685 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4686 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4687 if( theFlags & EXTRUSION_FLAG_SEW ) {
4688 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4689 theTolerance, theParams.myNodes);
4690 listNewNodes.push_back( newNode );
4693 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4694 myLastCreatedNodes.Append(newNode);
4695 srcNodes.Append( node );
4696 listNewNodes.push_back( newNode );
4699 // create a corner node
4700 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4701 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4702 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4703 if( theFlags & EXTRUSION_FLAG_SEW ) {
4704 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4705 theTolerance, theParams.myNodes);
4706 listNewNodes.push_back( newNode );
4709 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4710 myLastCreatedNodes.Append(newNode);
4711 srcNodes.Append( node );
4712 listNewNodes.push_back( newNode );
4716 newNodesItVec.push_back( nIt );
4718 // make new elements
4719 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4722 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4723 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4725 PGroupIDs newGroupIDs;
4726 if ( theMakeGroups )
4727 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4732 //=======================================================================
4733 //function : ExtrusionAlongTrack
4735 //=======================================================================
4736 SMESH_MeshEditor::Extrusion_Error
4737 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4738 SMESH_subMesh* theTrack,
4739 const SMDS_MeshNode* theN1,
4740 const bool theHasAngles,
4741 list<double>& theAngles,
4742 const bool theLinearVariation,
4743 const bool theHasRefPoint,
4744 const gp_Pnt& theRefPoint,
4745 const bool theMakeGroups)
4747 MESSAGE("ExtrusionAlongTrack");
4748 myLastCreatedElems.Clear();
4749 myLastCreatedNodes.Clear();
4752 std::list<double> aPrms;
4753 TIDSortedElemSet::iterator itElem;
4756 TopoDS_Edge aTrackEdge;
4757 TopoDS_Vertex aV1, aV2;
4759 SMDS_ElemIteratorPtr aItE;
4760 SMDS_NodeIteratorPtr aItN;
4761 SMDSAbs_ElementType aTypeE;
4763 TNodeOfNodeListMap mapNewNodes;
4766 aNbE = theElements.size();
4769 return EXTR_NO_ELEMENTS;
4771 // 1.1 Track Pattern
4774 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4776 aItE = pSubMeshDS->GetElements();
4777 while ( aItE->more() ) {
4778 const SMDS_MeshElement* pE = aItE->next();
4779 aTypeE = pE->GetType();
4780 // Pattern must contain links only
4781 if ( aTypeE != SMDSAbs_Edge )
4782 return EXTR_PATH_NOT_EDGE;
4785 list<SMESH_MeshEditor_PathPoint> fullList;
4787 const TopoDS_Shape& aS = theTrack->GetSubShape();
4788 // Sub-shape for the Pattern must be an Edge or Wire
4789 if( aS.ShapeType() == TopAbs_EDGE ) {
4790 aTrackEdge = TopoDS::Edge( aS );
4791 // the Edge must not be degenerated
4792 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4793 return EXTR_BAD_PATH_SHAPE;
4794 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4795 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4796 const SMDS_MeshNode* aN1 = aItN->next();
4797 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4798 const SMDS_MeshNode* aN2 = aItN->next();
4799 // starting node must be aN1 or aN2
4800 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4801 return EXTR_BAD_STARTING_NODE;
4802 aItN = pSubMeshDS->GetNodes();
4803 while ( aItN->more() ) {
4804 const SMDS_MeshNode* pNode = aItN->next();
4805 const SMDS_EdgePosition* pEPos =
4806 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4807 double aT = pEPos->GetUParameter();
4808 aPrms.push_back( aT );
4810 //Extrusion_Error err =
4811 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4812 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4813 list< SMESH_subMesh* > LSM;
4814 TopTools_SequenceOfShape Edges;
4815 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4816 while(itSM->more()) {
4817 SMESH_subMesh* SM = itSM->next();
4819 const TopoDS_Shape& aS = SM->GetSubShape();
4822 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4823 int startNid = theN1->GetID();
4824 TColStd_MapOfInteger UsedNums;
4826 int NbEdges = Edges.Length();
4828 for(; i<=NbEdges; i++) {
4830 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
4831 for(; itLSM!=LSM.end(); itLSM++) {
4833 if(UsedNums.Contains(k)) continue;
4834 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
4835 SMESH_subMesh* locTrack = *itLSM;
4836 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
4837 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4838 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
4839 const SMDS_MeshNode* aN1 = aItN->next();
4840 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
4841 const SMDS_MeshNode* aN2 = aItN->next();
4842 // starting node must be aN1 or aN2
4843 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
4844 // 2. Collect parameters on the track edge
4846 aItN = locMeshDS->GetNodes();
4847 while ( aItN->more() ) {
4848 const SMDS_MeshNode* pNode = aItN->next();
4849 const SMDS_EdgePosition* pEPos =
4850 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4851 double aT = pEPos->GetUParameter();
4852 aPrms.push_back( aT );
4854 list<SMESH_MeshEditor_PathPoint> LPP;
4855 //Extrusion_Error err =
4856 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
4857 LLPPs.push_back(LPP);
4859 // update startN for search following egde
4860 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
4861 else startNid = aN1->GetID();
4865 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
4866 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
4867 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
4868 for(; itPP!=firstList.end(); itPP++) {
4869 fullList.push_back( *itPP );
4871 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
4872 fullList.pop_back();
4874 for(; itLLPP!=LLPPs.end(); itLLPP++) {
4875 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
4876 itPP = currList.begin();
4877 SMESH_MeshEditor_PathPoint PP2 = currList.front();
4878 gp_Dir D1 = PP1.Tangent();
4879 gp_Dir D2 = PP2.Tangent();
4880 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
4881 (D1.Z()+D2.Z())/2 ) );
4882 PP1.SetTangent(Dnew);
4883 fullList.push_back(PP1);
4885 for(; itPP!=firstList.end(); itPP++) {
4886 fullList.push_back( *itPP );
4888 PP1 = fullList.back();
4889 fullList.pop_back();
4891 // if wire not closed
4892 fullList.push_back(PP1);
4896 return EXTR_BAD_PATH_SHAPE;
4899 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
4900 theHasRefPoint, theRefPoint, theMakeGroups);
4904 //=======================================================================
4905 //function : ExtrusionAlongTrack
4907 //=======================================================================
4908 SMESH_MeshEditor::Extrusion_Error
4909 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4910 SMESH_Mesh* theTrack,
4911 const SMDS_MeshNode* theN1,
4912 const bool theHasAngles,
4913 list<double>& theAngles,
4914 const bool theLinearVariation,
4915 const bool theHasRefPoint,
4916 const gp_Pnt& theRefPoint,
4917 const bool theMakeGroups)
4919 myLastCreatedElems.Clear();
4920 myLastCreatedNodes.Clear();
4923 std::list<double> aPrms;
4924 TIDSortedElemSet::iterator itElem;
4927 TopoDS_Edge aTrackEdge;
4928 TopoDS_Vertex aV1, aV2;
4930 SMDS_ElemIteratorPtr aItE;
4931 SMDS_NodeIteratorPtr aItN;
4932 SMDSAbs_ElementType aTypeE;
4934 TNodeOfNodeListMap mapNewNodes;
4937 aNbE = theElements.size();
4940 return EXTR_NO_ELEMENTS;
4942 // 1.1 Track Pattern
4945 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
4947 aItE = pMeshDS->elementsIterator();
4948 while ( aItE->more() ) {
4949 const SMDS_MeshElement* pE = aItE->next();
4950 aTypeE = pE->GetType();
4951 // Pattern must contain links only
4952 if ( aTypeE != SMDSAbs_Edge )
4953 return EXTR_PATH_NOT_EDGE;
4956 list<SMESH_MeshEditor_PathPoint> fullList;
4958 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
4960 if( aS == SMESH_Mesh::PseudoShape() ) {
4961 //Mesh without shape
4962 const SMDS_MeshNode* currentNode = NULL;
4963 const SMDS_MeshNode* prevNode = theN1;
4964 std::vector<const SMDS_MeshNode*> aNodesList;
4965 aNodesList.push_back(theN1);
4966 int nbEdges = 0, conn=0;
4967 const SMDS_MeshElement* prevElem = NULL;
4968 const SMDS_MeshElement* currentElem = NULL;
4969 int totalNbEdges = theTrack->NbEdges();
4970 SMDS_ElemIteratorPtr nIt;
4973 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
4974 return EXTR_BAD_STARTING_NODE;
4977 conn = nbEdgeConnectivity(theN1);
4979 return EXTR_PATH_NOT_EDGE;
4981 aItE = theN1->GetInverseElementIterator();
4982 prevElem = aItE->next();
4983 currentElem = prevElem;
4985 if(totalNbEdges == 1 ) {
4986 nIt = currentElem->nodesIterator();
4987 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4988 if(currentNode == prevNode)
4989 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4990 aNodesList.push_back(currentNode);
4992 nIt = currentElem->nodesIterator();
4993 while( nIt->more() ) {
4994 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4995 if(currentNode == prevNode)
4996 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
4997 aNodesList.push_back(currentNode);
4999 //case of the closed mesh
5000 if(currentNode == theN1) {
5005 conn = nbEdgeConnectivity(currentNode);
5007 return EXTR_PATH_NOT_EDGE;
5008 }else if( conn == 1 && nbEdges > 0 ) {
5013 prevNode = currentNode;
5014 aItE = currentNode->GetInverseElementIterator();
5015 currentElem = aItE->next();
5016 if( currentElem == prevElem)
5017 currentElem = aItE->next();
5018 nIt = currentElem->nodesIterator();
5019 prevElem = currentElem;
5025 if(nbEdges != totalNbEdges)
5026 return EXTR_PATH_NOT_EDGE;
5028 TopTools_SequenceOfShape Edges;
5029 double x1,x2,y1,y2,z1,z2;
5030 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5031 int startNid = theN1->GetID();
5032 for(int i = 1; i < aNodesList.size(); i++) {
5033 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5034 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5035 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5036 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5037 list<SMESH_MeshEditor_PathPoint> LPP;
5039 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5040 LLPPs.push_back(LPP);
5041 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5042 else startNid = aNodesList[i-1]->GetID();
5046 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5047 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5048 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5049 for(; itPP!=firstList.end(); itPP++) {
5050 fullList.push_back( *itPP );
5053 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5054 SMESH_MeshEditor_PathPoint PP2;
5055 fullList.pop_back();
5057 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5058 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5059 itPP = currList.begin();
5060 PP2 = currList.front();
5061 gp_Dir D1 = PP1.Tangent();
5062 gp_Dir D2 = PP2.Tangent();
5063 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5064 (D1.Z()+D2.Z())/2 ) );
5065 PP1.SetTangent(Dnew);
5066 fullList.push_back(PP1);
5068 for(; itPP!=currList.end(); itPP++) {
5069 fullList.push_back( *itPP );
5071 PP1 = fullList.back();
5072 fullList.pop_back();
5074 fullList.push_back(PP1);
5076 } // Sub-shape for the Pattern must be an Edge or Wire
5077 else if( aS.ShapeType() == TopAbs_EDGE ) {
5078 aTrackEdge = TopoDS::Edge( aS );
5079 // the Edge must not be degenerated
5080 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5081 return EXTR_BAD_PATH_SHAPE;
5082 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5083 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5084 const SMDS_MeshNode* aN1 = aItN->next();
5085 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5086 const SMDS_MeshNode* aN2 = aItN->next();
5087 // starting node must be aN1 or aN2
5088 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5089 return EXTR_BAD_STARTING_NODE;
5090 aItN = pMeshDS->nodesIterator();
5091 while ( aItN->more() ) {
5092 const SMDS_MeshNode* pNode = aItN->next();
5093 if( pNode==aN1 || pNode==aN2 ) continue;
5094 const SMDS_EdgePosition* pEPos =
5095 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5096 double aT = pEPos->GetUParameter();
5097 aPrms.push_back( aT );
5099 //Extrusion_Error err =
5100 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5102 else if( aS.ShapeType() == TopAbs_WIRE ) {
5103 list< SMESH_subMesh* > LSM;
5104 TopTools_SequenceOfShape Edges;
5105 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5106 for(; eExp.More(); eExp.Next()) {
5107 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5108 if( BRep_Tool::Degenerated(E) ) continue;
5109 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5115 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5116 int startNid = theN1->GetID();
5117 TColStd_MapOfInteger UsedNums;
5118 int NbEdges = Edges.Length();
5120 for(; i<=NbEdges; i++) {
5122 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5123 for(; itLSM!=LSM.end(); itLSM++) {
5125 if(UsedNums.Contains(k)) continue;
5126 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5127 SMESH_subMesh* locTrack = *itLSM;
5128 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5129 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5130 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5131 const SMDS_MeshNode* aN1 = aItN->next();
5132 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5133 const SMDS_MeshNode* aN2 = aItN->next();
5134 // starting node must be aN1 or aN2
5135 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5136 // 2. Collect parameters on the track edge
5138 aItN = locMeshDS->GetNodes();
5139 while ( aItN->more() ) {
5140 const SMDS_MeshNode* pNode = aItN->next();
5141 const SMDS_EdgePosition* pEPos =
5142 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5143 double aT = pEPos->GetUParameter();
5144 aPrms.push_back( aT );
5146 list<SMESH_MeshEditor_PathPoint> LPP;
5147 //Extrusion_Error err =
5148 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5149 LLPPs.push_back(LPP);
5151 // update startN for search following egde
5152 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5153 else startNid = aN1->GetID();
5157 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5158 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5159 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5160 for(; itPP!=firstList.end(); itPP++) {
5161 fullList.push_back( *itPP );
5163 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5164 fullList.pop_back();
5166 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5167 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5168 itPP = currList.begin();
5169 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5170 gp_Dir D1 = PP1.Tangent();
5171 gp_Dir D2 = PP2.Tangent();
5172 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5173 (D1.Z()+D2.Z())/2 ) );
5174 PP1.SetTangent(Dnew);
5175 fullList.push_back(PP1);
5177 for(; itPP!=currList.end(); itPP++) {
5178 fullList.push_back( *itPP );
5180 PP1 = fullList.back();
5181 fullList.pop_back();
5183 // if wire not closed
5184 fullList.push_back(PP1);
5188 return EXTR_BAD_PATH_SHAPE;
5191 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5192 theHasRefPoint, theRefPoint, theMakeGroups);
5196 //=======================================================================
5197 //function : MakeEdgePathPoints
5198 //purpose : auxilary for ExtrusionAlongTrack
5199 //=======================================================================
5200 SMESH_MeshEditor::Extrusion_Error
5201 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5202 const TopoDS_Edge& aTrackEdge,
5204 list<SMESH_MeshEditor_PathPoint>& LPP)
5206 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5208 aTolVec2=aTolVec*aTolVec;
5210 TopoDS_Vertex aV1, aV2;
5211 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5212 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5213 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5214 // 2. Collect parameters on the track edge
5215 aPrms.push_front( aT1 );
5216 aPrms.push_back( aT2 );
5219 if( FirstIsStart ) {
5230 SMESH_MeshEditor_PathPoint aPP;
5231 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5232 std::list<double>::iterator aItD = aPrms.begin();
5233 for(; aItD != aPrms.end(); ++aItD) {
5237 aC3D->D1( aT, aP3D, aVec );
5238 aL2 = aVec.SquareMagnitude();
5239 if ( aL2 < aTolVec2 )
5240 return EXTR_CANT_GET_TANGENT;
5241 gp_Dir aTgt( aVec );
5243 aPP.SetTangent( aTgt );
5244 aPP.SetParameter( aT );
5251 //=======================================================================
5252 //function : MakeExtrElements
5253 //purpose : auxilary for ExtrusionAlongTrack
5254 //=======================================================================
5255 SMESH_MeshEditor::Extrusion_Error
5256 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5257 list<SMESH_MeshEditor_PathPoint>& fullList,
5258 const bool theHasAngles,
5259 list<double>& theAngles,
5260 const bool theLinearVariation,
5261 const bool theHasRefPoint,
5262 const gp_Pnt& theRefPoint,
5263 const bool theMakeGroups)
5265 MESSAGE("MakeExtrElements");
5266 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5267 int aNbTP = fullList.size();
5268 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5270 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5271 LinearAngleVariation(aNbTP-1, theAngles);
5273 vector<double> aAngles( aNbTP );
5275 for(; j<aNbTP; ++j) {
5278 if ( theHasAngles ) {
5280 std::list<double>::iterator aItD = theAngles.begin();
5281 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5283 aAngles[j] = anAngle;
5286 // fill vector of path points with angles
5287 //aPPs.resize(fullList.size());
5289 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5290 for(; itPP!=fullList.end(); itPP++) {
5292 SMESH_MeshEditor_PathPoint PP = *itPP;
5293 PP.SetAngle(aAngles[j]);
5297 TNodeOfNodeListMap mapNewNodes;
5298 TElemOfVecOfNnlmiMap mapElemNewNodes;
5299 TElemOfElemListMap newElemsMap;
5300 TIDSortedElemSet::iterator itElem;
5303 SMDSAbs_ElementType aTypeE;
5304 // source elements for each generated one
5305 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5307 // 3. Center of rotation aV0
5308 gp_Pnt aV0 = theRefPoint;
5310 if ( !theHasRefPoint ) {
5312 aGC.SetCoord( 0.,0.,0. );
5314 itElem = theElements.begin();
5315 for ( ; itElem != theElements.end(); itElem++ ) {
5316 const SMDS_MeshElement* elem = *itElem;
5318 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5319 while ( itN->more() ) {
5320 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5325 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5326 list<const SMDS_MeshNode*> aLNx;
5327 mapNewNodes[node] = aLNx;
5329 gp_XYZ aXYZ( aX, aY, aZ );
5337 } // if (!theHasRefPoint) {
5338 mapNewNodes.clear();
5340 // 4. Processing the elements
5341 SMESHDS_Mesh* aMesh = GetMeshDS();
5343 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5344 // check element type
5345 const SMDS_MeshElement* elem = *itElem;
5346 aTypeE = elem->GetType();
5347 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5350 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5351 newNodesItVec.reserve( elem->NbNodes() );
5353 // loop on elem nodes
5355 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5356 while ( itN->more() )
5359 // check if a node has been already processed
5360 const SMDS_MeshNode* node =
5361 static_cast<const SMDS_MeshNode*>( itN->next() );
5362 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5363 if ( nIt == mapNewNodes.end() ) {
5364 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5365 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5368 aX = node->X(); aY = node->Y(); aZ = node->Z();
5370 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5371 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5372 gp_Ax1 anAx1, anAxT1T0;
5373 gp_Dir aDT1x, aDT0x, aDT1T0;
5378 aPN0.SetCoord(aX, aY, aZ);
5380 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5382 aDT0x= aPP0.Tangent();
5383 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5385 for ( j = 1; j < aNbTP; ++j ) {
5386 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5388 aDT1x = aPP1.Tangent();
5389 aAngle1x = aPP1.Angle();
5391 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5393 gp_Vec aV01x( aP0x, aP1x );
5394 aTrsf.SetTranslation( aV01x );
5397 aV1x = aV0x.Transformed( aTrsf );
5398 aPN1 = aPN0.Transformed( aTrsf );
5400 // rotation 1 [ T1,T0 ]
5401 aAngleT1T0=-aDT1x.Angle( aDT0x );
5402 if (fabs(aAngleT1T0) > aTolAng) {
5404 anAxT1T0.SetLocation( aV1x );
5405 anAxT1T0.SetDirection( aDT1T0 );
5406 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5408 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5412 if ( theHasAngles ) {
5413 anAx1.SetLocation( aV1x );
5414 anAx1.SetDirection( aDT1x );
5415 aTrsfRot.SetRotation( anAx1, aAngle1x );
5417 aPN1 = aPN1.Transformed( aTrsfRot );
5421 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5422 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5423 // create additional node
5424 double x = ( aPN1.X() + aPN0.X() )/2.;
5425 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5426 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5427 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5428 myLastCreatedNodes.Append(newNode);
5429 srcNodes.Append( node );
5430 listNewNodes.push_back( newNode );
5435 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5436 myLastCreatedNodes.Append(newNode);
5437 srcNodes.Append( node );
5438 listNewNodes.push_back( newNode );
5448 // if current elem is quadratic and current node is not medium
5449 // we have to check - may be it is needed to insert additional nodes
5450 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5451 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5452 if(listNewNodes.size()==aNbTP-1) {
5453 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5454 gp_XYZ P(node->X(), node->Y(), node->Z());
5455 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5457 for(i=0; i<aNbTP-1; i++) {
5458 const SMDS_MeshNode* N = *it;
5459 double x = ( N->X() + P.X() )/2.;
5460 double y = ( N->Y() + P.Y() )/2.;
5461 double z = ( N->Z() + P.Z() )/2.;
5462 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5463 srcNodes.Append( node );
5464 myLastCreatedNodes.Append(newN);
5467 P = gp_XYZ(N->X(),N->Y(),N->Z());
5469 listNewNodes.clear();
5470 for(i=0; i<2*(aNbTP-1); i++) {
5471 listNewNodes.push_back(aNodes[i]);
5477 newNodesItVec.push_back( nIt );
5479 // make new elements
5480 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5481 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5482 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5485 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5487 if ( theMakeGroups )
5488 generateGroups( srcNodes, srcElems, "extruded");
5494 //=======================================================================
5495 //function : LinearAngleVariation
5496 //purpose : auxilary for ExtrusionAlongTrack
5497 //=======================================================================
5498 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5499 list<double>& Angles)
5501 int nbAngles = Angles.size();
5502 if( nbSteps > nbAngles ) {
5503 vector<double> theAngles(nbAngles);
5504 list<double>::iterator it = Angles.begin();
5506 for(; it!=Angles.end(); it++) {
5508 theAngles[i] = (*it);
5511 double rAn2St = double( nbAngles ) / double( nbSteps );
5512 double angPrev = 0, angle;
5513 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5514 double angCur = rAn2St * ( iSt+1 );
5515 double angCurFloor = floor( angCur );
5516 double angPrevFloor = floor( angPrev );
5517 if ( angPrevFloor == angCurFloor )
5518 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5520 int iP = int( angPrevFloor );
5521 double angPrevCeil = ceil(angPrev);
5522 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5524 int iC = int( angCurFloor );
5525 if ( iC < nbAngles )
5526 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5528 iP = int( angPrevCeil );
5530 angle += theAngles[ iC ];
5532 res.push_back(angle);
5537 for(; it!=res.end(); it++)
5538 Angles.push_back( *it );
5543 //================================================================================
5545 * \brief Move or copy theElements applying theTrsf to their nodes
5546 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5547 * \param theTrsf - transformation to apply
5548 * \param theCopy - if true, create translated copies of theElems
5549 * \param theMakeGroups - if true and theCopy, create translated groups
5550 * \param theTargetMesh - mesh to copy translated elements into
5551 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5553 //================================================================================
5555 SMESH_MeshEditor::PGroupIDs
5556 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5557 const gp_Trsf& theTrsf,
5559 const bool theMakeGroups,
5560 SMESH_Mesh* theTargetMesh)
5562 myLastCreatedElems.Clear();
5563 myLastCreatedNodes.Clear();
5565 bool needReverse = false;
5566 string groupPostfix;
5567 switch ( theTrsf.Form() ) {
5569 MESSAGE("gp_PntMirror");
5571 groupPostfix = "mirrored";
5574 MESSAGE("gp_Ax1Mirror");
5575 groupPostfix = "mirrored";
5578 MESSAGE("gp_Ax2Mirror");
5580 groupPostfix = "mirrored";
5583 MESSAGE("gp_Rotation");
5584 groupPostfix = "rotated";
5586 case gp_Translation:
5587 MESSAGE("gp_Translation");
5588 groupPostfix = "translated";
5591 MESSAGE("gp_Scale");
5592 groupPostfix = "scaled";
5594 case gp_CompoundTrsf: // different scale by axis
5595 MESSAGE("gp_CompoundTrsf");
5596 groupPostfix = "scaled";
5600 needReverse = false;
5601 groupPostfix = "transformed";
5604 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5605 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5606 SMESHDS_Mesh* aMesh = GetMeshDS();
5609 // map old node to new one
5610 TNodeNodeMap nodeMap;
5612 // elements sharing moved nodes; those of them which have all
5613 // nodes mirrored but are not in theElems are to be reversed
5614 TIDSortedElemSet inverseElemSet;
5616 // source elements for each generated one
5617 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5619 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5620 TIDSortedElemSet orphanNode;
5622 if ( theElems.empty() ) // transform the whole mesh
5625 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5626 while ( eIt->more() ) theElems.insert( eIt->next() );
5628 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5629 while ( nIt->more() )
5631 const SMDS_MeshNode* node = nIt->next();
5632 if ( node->NbInverseElements() == 0)
5633 orphanNode.insert( node );
5637 // loop on elements to transform nodes : first orphan nodes then elems
5638 TIDSortedElemSet::iterator itElem;
5639 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5640 for (int i=0; i<2; i++)
5641 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5642 const SMDS_MeshElement* elem = *itElem;
5646 // loop on elem nodes
5647 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5648 while ( itN->more() ) {
5650 const SMDS_MeshNode* node = cast2Node( itN->next() );
5651 // check if a node has been already transformed
5652 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5653 nodeMap.insert( make_pair ( node, node ));
5654 if ( !n2n_isnew.second )
5658 coord[0] = node->X();
5659 coord[1] = node->Y();
5660 coord[2] = node->Z();
5661 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5662 if ( theTargetMesh ) {
5663 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5664 n2n_isnew.first->second = newNode;
5665 myLastCreatedNodes.Append(newNode);
5666 srcNodes.Append( node );
5668 else if ( theCopy ) {
5669 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5670 n2n_isnew.first->second = newNode;
5671 myLastCreatedNodes.Append(newNode);
5672 srcNodes.Append( node );
5675 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5676 // node position on shape becomes invalid
5677 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5678 ( SMDS_SpacePosition::originSpacePosition() );
5681 // keep inverse elements
5682 if ( !theCopy && !theTargetMesh && needReverse ) {
5683 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5684 while ( invElemIt->more() ) {
5685 const SMDS_MeshElement* iel = invElemIt->next();
5686 inverseElemSet.insert( iel );
5692 // either create new elements or reverse mirrored ones
5693 if ( !theCopy && !needReverse && !theTargetMesh )
5696 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5697 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5698 theElems.insert( *invElemIt );
5700 // Replicate or reverse elements
5702 std::vector<int> iForw;
5703 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5705 const SMDS_MeshElement* elem = *itElem;
5706 if ( !elem ) continue;
5708 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5709 int nbNodes = elem->NbNodes();
5710 if ( geomType == SMDSGeom_POINT ) continue; // node
5712 switch ( geomType ) {
5714 case SMDSGeom_POLYGON: // ---------------------- polygon
5716 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5718 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5719 while (itN->more()) {
5720 const SMDS_MeshNode* node =
5721 static_cast<const SMDS_MeshNode*>(itN->next());
5722 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5723 if (nodeMapIt == nodeMap.end())
5724 break; // not all nodes transformed
5726 // reverse mirrored faces and volumes
5727 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5729 poly_nodes[iNode] = (*nodeMapIt).second;
5733 if ( iNode != nbNodes )
5734 continue; // not all nodes transformed
5736 if ( theTargetMesh ) {
5737 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5738 srcElems.Append( elem );
5740 else if ( theCopy ) {
5741 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5742 srcElems.Append( elem );
5745 aMesh->ChangePolygonNodes(elem, poly_nodes);
5750 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5752 const SMDS_VtkVolume* aPolyedre =
5753 dynamic_cast<const SMDS_VtkVolume*>( elem );
5755 MESSAGE("Warning: bad volumic element");
5759 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5760 vector<int> quantities; quantities.reserve( nbNodes );
5762 bool allTransformed = true;
5763 int nbFaces = aPolyedre->NbFaces();
5764 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5765 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5766 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5767 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5768 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5769 if (nodeMapIt == nodeMap.end()) {
5770 allTransformed = false; // not all nodes transformed
5772 poly_nodes.push_back((*nodeMapIt).second);
5774 if ( needReverse && allTransformed )
5775 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5777 quantities.push_back(nbFaceNodes);
5779 if ( !allTransformed )
5780 continue; // not all nodes transformed
5782 if ( theTargetMesh ) {
5783 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5784 srcElems.Append( elem );
5786 else if ( theCopy ) {
5787 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5788 srcElems.Append( elem );
5791 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5796 case SMDSGeom_BALL: // -------------------- Ball
5798 if ( !theCopy && !theTargetMesh ) continue;
5800 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5801 if (nodeMapIt == nodeMap.end())
5802 continue; // not all nodes transformed
5804 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5805 if ( theTargetMesh ) {
5806 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5807 srcElems.Append( elem );
5810 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
5811 srcElems.Append( elem );
5816 default: // ----------------------- Regular elements
5818 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
5819 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
5820 const std::vector<int>& i = needReverse ? iRev : iForw;
5822 // find transformed nodes
5823 vector<const SMDS_MeshNode*> nodes(nbNodes);
5825 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5826 while ( itN->more() ) {
5827 const SMDS_MeshNode* node =
5828 static_cast<const SMDS_MeshNode*>( itN->next() );
5829 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
5830 if ( nodeMapIt == nodeMap.end() )
5831 break; // not all nodes transformed
5832 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
5834 if ( iNode != nbNodes )
5835 continue; // not all nodes transformed
5837 if ( theTargetMesh ) {
5838 if ( SMDS_MeshElement* copy =
5839 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
5840 myLastCreatedElems.Append( copy );
5841 srcElems.Append( elem );
5844 else if ( theCopy ) {
5845 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
5846 srcElems.Append( elem );
5849 // reverse element as it was reversed by transformation
5851 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
5853 } // switch ( geomType )
5855 } // loop on elements
5857 PGroupIDs newGroupIDs;
5859 if ( ( theMakeGroups && theCopy ) ||
5860 ( theMakeGroups && theTargetMesh ) )
5861 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
5866 //=======================================================================
5868 * \brief Create groups of elements made during transformation
5869 * \param nodeGens - nodes making corresponding myLastCreatedNodes
5870 * \param elemGens - elements making corresponding myLastCreatedElems
5871 * \param postfix - to append to names of new groups
5873 //=======================================================================
5875 SMESH_MeshEditor::PGroupIDs
5876 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
5877 const SMESH_SequenceOfElemPtr& elemGens,
5878 const std::string& postfix,
5879 SMESH_Mesh* targetMesh)
5881 PGroupIDs newGroupIDs( new list<int> );
5882 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
5884 // Sort existing groups by types and collect their names
5886 // to store an old group and a generated new one
5887 typedef pair< SMESHDS_GroupBase*, SMDS_MeshGroup* > TOldNewGroup;
5888 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
5890 set< string > groupNames;
5892 SMDS_MeshGroup* nullNewGroup = (SMDS_MeshGroup*) 0;
5893 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
5894 while ( groupIt->more() ) {
5895 SMESH_Group * group = groupIt->next();
5896 if ( !group ) continue;
5897 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
5898 if ( !groupDS || groupDS->IsEmpty() ) continue;
5899 groupNames.insert( group->GetName() );
5900 groupDS->SetStoreName( group->GetName() );
5901 groupsByType[ groupDS->GetType() ].push_back( make_pair( groupDS, nullNewGroup ));
5906 // loop on nodes and elements
5907 for ( int isNodes = 0; isNodes < 2; ++isNodes )
5909 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
5910 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
5911 if ( gens.Length() != elems.Length() )
5912 throw SALOME_Exception(LOCALIZED("invalid args"));
5914 // loop on created elements
5915 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
5917 const SMDS_MeshElement* sourceElem = gens( iElem );
5918 if ( !sourceElem ) {
5919 MESSAGE("generateGroups(): NULL source element");
5922 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
5923 if ( groupsOldNew.empty() ) {
5924 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5925 ++iElem; // skip all elements made by sourceElem
5928 // collect all elements made by sourceElem
5929 list< const SMDS_MeshElement* > resultElems;
5930 if ( const SMDS_MeshElement* resElem = elems( iElem ))
5931 if ( resElem != sourceElem )
5932 resultElems.push_back( resElem );
5933 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
5934 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
5935 if ( resElem != sourceElem )
5936 resultElems.push_back( resElem );
5937 // do not generate element groups from node ones
5938 // if ( sourceElem->GetType() == SMDSAbs_Node &&
5939 // elems( iElem )->GetType() != SMDSAbs_Node )
5942 // add resultElems to groups made by ones the sourceElem belongs to
5943 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
5944 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
5946 SMESHDS_GroupBase* oldGroup = gOldNew->first;
5947 if ( oldGroup->Contains( sourceElem )) // sourceElem in oldGroup
5949 SMDS_MeshGroup* & newGroup = gOldNew->second;
5950 if ( !newGroup )// create a new group
5953 string name = oldGroup->GetStoreName();
5954 if ( !targetMesh ) {
5958 while ( !groupNames.insert( name ).second ) // name exists
5964 TCollection_AsciiString nbStr(nb+1);
5965 name.resize( name.rfind('_')+1 );
5966 name += nbStr.ToCString();
5973 SMESH_Group* group = mesh->AddGroup( resultElems.back()->GetType(),
5975 SMESHDS_Group* groupDS = static_cast<SMESHDS_Group*>(group->GetGroupDS());
5976 newGroup = & groupDS->SMDSGroup();
5977 newGroupIDs->push_back( id );
5980 // fill in a new group
5981 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
5982 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
5983 newGroup->Add( *resElemIt );
5986 } // loop on created elements
5987 }// loop on nodes and elements
5992 //================================================================================
5994 * \brief Return list of group of nodes close to each other within theTolerance
5995 * Search among theNodes or in the whole mesh if theNodes is empty using
5996 * an Octree algorithm
5998 //================================================================================
6000 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6001 const double theTolerance,
6002 TListOfListOfNodes & theGroupsOfNodes)
6004 myLastCreatedElems.Clear();
6005 myLastCreatedNodes.Clear();
6007 if ( theNodes.empty() )
6008 { // get all nodes in the mesh
6009 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6010 while ( nIt->more() )
6011 theNodes.insert( theNodes.end(),nIt->next());
6014 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6018 //=======================================================================
6020 * \brief Implementation of search for the node closest to point
6022 //=======================================================================
6024 struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher
6026 //---------------------------------------------------------------------
6028 * \brief Constructor
6030 SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh )
6032 myMesh = ( SMESHDS_Mesh* ) theMesh;
6034 TIDSortedNodeSet nodes;
6036 SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true);
6037 while ( nIt->more() )
6038 nodes.insert( nodes.end(), nIt->next() );
6040 myOctreeNode = new SMESH_OctreeNode(nodes) ;
6042 // get max size of a leaf box
6043 SMESH_OctreeNode* tree = myOctreeNode;
6044 while ( !tree->isLeaf() )
6046 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6050 myHalfLeafSize = tree->maxSize() / 2.;
6053 //---------------------------------------------------------------------
6055 * \brief Move node and update myOctreeNode accordingly
6057 void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt )
6059 myOctreeNode->UpdateByMoveNode( node, toPnt );
6060 myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() );
6063 //---------------------------------------------------------------------
6065 * \brief Do it's job
6067 const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt )
6069 map<double, const SMDS_MeshNode*> dist2Nodes;
6070 myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize );
6071 if ( !dist2Nodes.empty() )
6072 return dist2Nodes.begin()->second;
6073 list<const SMDS_MeshNode*> nodes;
6074 //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize );
6076 double minSqDist = DBL_MAX;
6077 if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt
6079 // sort leafs by their distance from thePnt
6080 typedef map< double, SMESH_OctreeNode* > TDistTreeMap;
6081 TDistTreeMap treeMap;
6082 list< SMESH_OctreeNode* > treeList;
6083 list< SMESH_OctreeNode* >::iterator trIt;
6084 treeList.push_back( myOctreeNode );
6086 gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() );
6087 bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize );
6088 for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt)
6090 SMESH_OctreeNode* tree = *trIt;
6091 if ( !tree->isLeaf() ) // put children to the queue
6093 if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue;
6094 SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator();
6095 while ( cIt->more() )
6096 treeList.push_back( cIt->next() );
6098 else if ( tree->NbNodes() ) // put a tree to the treeMap
6100 const Bnd_B3d& box = tree->getBox();
6101 double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() ));
6102 pair<TDistTreeMap::iterator,bool> it_in = treeMap.insert( make_pair( sqDist, tree ));
6103 if ( !it_in.second ) // not unique distance to box center
6104 treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree ));
6107 // find distance after which there is no sense to check tree's
6108 double sqLimit = DBL_MAX;
6109 TDistTreeMap::iterator sqDist_tree = treeMap.begin();
6110 if ( treeMap.size() > 5 ) {
6111 SMESH_OctreeNode* closestTree = sqDist_tree->second;
6112 const Bnd_B3d& box = closestTree->getBox();
6113 double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() );
6114 sqLimit = limit * limit;
6116 // get all nodes from trees
6117 for ( ; sqDist_tree != treeMap.end(); ++sqDist_tree) {
6118 if ( sqDist_tree->first > sqLimit )
6120 SMESH_OctreeNode* tree = sqDist_tree->second;
6121 tree->NodesAround( tree->GetNodeIterator()->next(), &nodes );
6124 // find closest among nodes
6125 minSqDist = DBL_MAX;
6126 const SMDS_MeshNode* closestNode = 0;
6127 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6128 for ( ; nIt != nodes.end(); ++nIt ) {
6129 double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) );
6130 if ( minSqDist > sqDist ) {
6138 //---------------------------------------------------------------------
6142 ~SMESH_NodeSearcherImpl() { delete myOctreeNode; }
6144 //---------------------------------------------------------------------
6146 * \brief Return the node tree
6148 const SMESH_OctreeNode* getTree() const { return myOctreeNode; }
6151 SMESH_OctreeNode* myOctreeNode;
6152 SMESHDS_Mesh* myMesh;
6153 double myHalfLeafSize; // max size of a leaf box
6156 //=======================================================================
6158 * \brief Return SMESH_NodeSearcher
6160 //=======================================================================
6162 SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher()
6164 return new SMESH_NodeSearcherImpl( GetMeshDS() );
6167 // ========================================================================
6168 namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint()
6170 const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree
6171 const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152
6172 const double NodeRadius = 1e-9; // to enlarge bnd box of element
6174 //=======================================================================
6176 * \brief Octal tree of bounding boxes of elements
6178 //=======================================================================
6180 class ElementBndBoxTree : public SMESH_Octree
6184 ElementBndBoxTree(const SMDS_Mesh& mesh,
6185 SMDSAbs_ElementType elemType,
6186 SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(),
6187 double tolerance = NodeRadius );
6188 void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems );
6189 void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems);
6190 void getElementsInSphere ( const gp_XYZ& center,
6191 const double radius, TIDSortedElemSet& foundElems);
6192 size_t getSize() { return std::max( _size, _elements.size() ); }
6193 ~ElementBndBoxTree();
6196 ElementBndBoxTree():_size(0) {}
6197 SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; }
6198 void buildChildrenData();
6199 Bnd_B3d* buildRootBox();
6201 //!< Bounding box of element
6202 struct ElementBox : public Bnd_B3d
6204 const SMDS_MeshElement* _element;
6205 int _refCount; // an ElementBox can be included in several tree branches
6206 ElementBox(const SMDS_MeshElement* elem, double tolerance);
6208 vector< ElementBox* > _elements;
6212 //================================================================================
6214 * \brief ElementBndBoxTree creation
6216 //================================================================================
6218 ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance)
6219 :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. ))
6221 int nbElems = mesh.GetMeshInfo().NbElements( elemType );
6222 _elements.reserve( nbElems );
6224 SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType );
6225 while ( elemIt->more() )
6226 _elements.push_back( new ElementBox( elemIt->next(),tolerance ));
6231 //================================================================================
6235 //================================================================================
6237 ElementBndBoxTree::~ElementBndBoxTree()
6239 for ( int i = 0; i < _elements.size(); ++i )
6240 if ( --_elements[i]->_refCount <= 0 )
6241 delete _elements[i];
6244 //================================================================================
6246 * \brief Return the maximal box
6248 //================================================================================
6250 Bnd_B3d* ElementBndBoxTree::buildRootBox()
6252 Bnd_B3d* box = new Bnd_B3d;
6253 for ( int i = 0; i < _elements.size(); ++i )
6254 box->Add( *_elements[i] );
6258 //================================================================================
6260 * \brief Redistrubute element boxes among children
6262 //================================================================================
6264 void ElementBndBoxTree::buildChildrenData()
6266 for ( int i = 0; i < _elements.size(); ++i )
6268 for (int j = 0; j < 8; j++)
6270 if ( !_elements[i]->IsOut( myChildren[j]->getBox() ))
6272 _elements[i]->_refCount++;
6273 ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]);
6276 _elements[i]->_refCount--;
6278 _size = _elements.size();
6279 SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory
6281 for (int j = 0; j < 8; j++)
6283 ElementBndBoxTree* child = static_cast<ElementBndBoxTree*>( myChildren[j]);
6284 if ( child->_elements.size() <= MaxNbElemsInLeaf )
6285 child->myIsLeaf = true;
6287 if ( child->_elements.capacity() - child->_elements.size() > 1000 )
6288 SMESHUtils::CompactVector( child->_elements );
6292 //================================================================================
6294 * \brief Return elements which can include the point
6296 //================================================================================
6298 void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point,
6299 TIDSortedElemSet& foundElems)
6301 if ( getBox().IsOut( point.XYZ() ))
6306 for ( int i = 0; i < _elements.size(); ++i )
6307 if ( !_elements[i]->IsOut( point.XYZ() ))
6308 foundElems.insert( _elements[i]->_element );
6312 for (int i = 0; i < 8; i++)
6313 ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems );
6317 //================================================================================
6319 * \brief Return elements which can be intersected by the line
6321 //================================================================================
6323 void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line,
6324 TIDSortedElemSet& foundElems)
6326 if ( getBox().IsOut( line ))
6331 for ( int i = 0; i < _elements.size(); ++i )
6332 if ( !_elements[i]->IsOut( line ))
6333 foundElems.insert( _elements[i]->_element );
6337 for (int i = 0; i < 8; i++)
6338 ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems );
6342 //================================================================================
6344 * \brief Return elements from leaves intersecting the sphere
6346 //================================================================================
6348 void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center,
6349 const double radius,
6350 TIDSortedElemSet& foundElems)
6352 if ( getBox().IsOut( center, radius ))
6357 for ( int i = 0; i < _elements.size(); ++i )
6358 if ( !_elements[i]->IsOut( center, radius ))
6359 foundElems.insert( _elements[i]->_element );
6363 for (int i = 0; i < 8; i++)
6364 ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems );
6368 //================================================================================
6370 * \brief Construct the element box
6372 //================================================================================
6374 ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance)
6378 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
6379 while ( nIt->more() )
6380 Add( SMESH_TNodeXYZ( nIt->next() ));
6381 Enlarge( tolerance );
6386 //=======================================================================
6388 * \brief Implementation of search for the elements by point and
6389 * of classification of point in 2D mesh
6391 //=======================================================================
6393 struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher
6395 SMESHDS_Mesh* _mesh;
6396 SMDS_ElemIteratorPtr _meshPartIt;
6397 ElementBndBoxTree* _ebbTree;
6398 SMESH_NodeSearcherImpl* _nodeSearcher;
6399 SMDSAbs_ElementType _elementType;
6401 bool _outerFacesFound;
6402 set<const SMDS_MeshElement*> _outerFaces; // empty means "no internal faces at all"
6404 SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr())
6405 : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {}
6406 ~SMESH_ElementSearcherImpl()
6408 if ( _ebbTree ) delete _ebbTree; _ebbTree = 0;
6409 if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0;
6411 virtual int FindElementsByPoint(const gp_Pnt& point,
6412 SMDSAbs_ElementType type,
6413 vector< const SMDS_MeshElement* >& foundElements);
6414 virtual TopAbs_State GetPointState(const gp_Pnt& point);
6415 virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point,
6416 SMDSAbs_ElementType type );
6418 void GetElementsNearLine( const gp_Ax1& line,
6419 SMDSAbs_ElementType type,
6420 vector< const SMDS_MeshElement* >& foundElems);
6421 double getTolerance();
6422 bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face,
6423 const double tolerance, double & param);
6424 void findOuterBoundary(const SMDS_MeshElement* anyOuterFace);
6425 bool isOuterBoundary(const SMDS_MeshElement* face) const
6427 return _outerFaces.empty() || _outerFaces.count(face);
6429 struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState())
6431 const SMDS_MeshElement* _face;
6433 bool _coincides; //!< the line lays in face plane
6434 TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false)
6435 : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {}
6437 struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary())
6440 TIDSortedElemSet _faces;
6441 TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face)
6442 : _link( n1, n2 ), _faces( &face, &face + 1) {}
6446 ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i)
6448 return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0)
6449 << ", _coincides="<<i._coincides << ")";
6452 //=======================================================================
6454 * \brief define tolerance for search
6456 //=======================================================================
6458 double SMESH_ElementSearcherImpl::getTolerance()
6460 if ( _tolerance < 0 )
6462 const SMDS_MeshInfo& meshInfo = _mesh->GetMeshInfo();
6465 if ( _nodeSearcher && meshInfo.NbNodes() > 1 )
6467 double boxSize = _nodeSearcher->getTree()->maxSize();
6468 _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/;
6470 else if ( _ebbTree && meshInfo.NbElements() > 0 )
6472 double boxSize = _ebbTree->maxSize();
6473 _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/;
6475 if ( _tolerance == 0 )
6477 // define tolerance by size of a most complex element
6478 int complexType = SMDSAbs_Volume;
6479 while ( complexType > SMDSAbs_All &&
6480 meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 )
6482 if ( complexType == SMDSAbs_All ) return 0; // empty mesh
6484 if ( complexType == int( SMDSAbs_Node ))
6486 SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator();
6488 if ( meshInfo.NbNodes() > 2 )
6489 elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() );
6493 SMDS_ElemIteratorPtr elemIt =
6494 _mesh->elementsIterator( SMDSAbs_ElementType( complexType ));
6495 const SMDS_MeshElement* elem = elemIt->next();
6496 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
6497 SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() ));
6499 while ( nodeIt->more() )
6501 double dist = n1.Distance( cast2Node( nodeIt->next() ));
6502 elemSize = max( dist, elemSize );
6505 _tolerance = 1e-4 * elemSize;
6511 //================================================================================
6513 * \brief Find intersection of the line and an edge of face and return parameter on line
6515 //================================================================================
6517 bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line,
6518 const SMDS_MeshElement* face,
6525 GeomAPI_ExtremaCurveCurve anExtCC;
6526 Handle(Geom_Curve) lineCurve = new Geom_Line( line );
6528 int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
6529 for ( int i = 0; i < nbNodes && nbInts < 2; ++i )
6531 GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )),
6532 SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) ));
6533 anExtCC.Init( lineCurve, edge);
6534 if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol)
6536 Quantity_Parameter pl, pe;
6537 anExtCC.LowerDistanceParameters( pl, pe );
6539 if ( ++nbInts == 2 )
6543 if ( nbInts > 0 ) param /= nbInts;
6546 //================================================================================
6548 * \brief Find all faces belonging to the outer boundary of mesh
6550 //================================================================================
6552 void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace)
6554 if ( _outerFacesFound ) return;
6556 // Collect all outer faces by passing from one outer face to another via their links
6557 // and BTW find out if there are internal faces at all.
6559 // checked links and links where outer boundary meets internal one
6560 set< SMESH_TLink > visitedLinks, seamLinks;
6562 // links to treat with already visited faces sharing them
6563 list < TFaceLink > startLinks;
6565 // load startLinks with the first outerFace
6566 startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace));
6567 _outerFaces.insert( outerFace );
6569 TIDSortedElemSet emptySet;
6570 while ( !startLinks.empty() )
6572 const SMESH_TLink& link = startLinks.front()._link;
6573 TIDSortedElemSet& faces = startLinks.front()._faces;
6575 outerFace = *faces.begin();
6576 // find other faces sharing the link
6577 const SMDS_MeshElement* f;
6578 while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces )))
6581 // select another outer face among the found
6582 const SMDS_MeshElement* outerFace2 = 0;
6583 if ( faces.size() == 2 )
6585 outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin());
6587 else if ( faces.size() > 2 )
6589 seamLinks.insert( link );
6591 // link direction within the outerFace
6592 gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()),
6593 SMESH_TNodeXYZ( link.node2()));
6594 int i1 = outerFace->GetNodeIndex( link.node1() );
6595 int i2 = outerFace->GetNodeIndex( link.node2() );
6596 bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 );
6597 if ( rev ) n1n2.Reverse();
6599 gp_XYZ ofNorm, fNorm;
6600 if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false ))
6602 // direction from the link inside outerFace
6603 gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2;
6604 // sort all other faces by angle with the dirInOF
6605 map< double, const SMDS_MeshElement* > angle2Face;
6606 set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin();
6607 for ( ; face != faces.end(); ++face )
6609 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false ))
6611 gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2;
6612 double angle = dirInOF.AngleWithRef( dirInF, n1n2 );
6613 if ( angle < 0 ) angle += 2. * M_PI;
6614 angle2Face.insert( make_pair( angle, *face ));
6616 if ( !angle2Face.empty() )
6617 outerFace2 = angle2Face.begin()->second;
6620 // store the found outer face and add its links to continue seaching from
6623 _outerFaces.insert( outerFace );
6624 int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 );
6625 for ( int i = 0; i < nbNodes; ++i )
6627 SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes));
6628 if ( visitedLinks.insert( link2 ).second )
6629 startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 ));
6632 startLinks.pop_front();
6634 _outerFacesFound = true;
6636 if ( !seamLinks.empty() )
6638 // There are internal boundaries touching the outher one,
6639 // find all faces of internal boundaries in order to find
6640 // faces of boundaries of holes, if any.
6645 _outerFaces.clear();
6649 //=======================================================================
6651 * \brief Find elements of given type where the given point is IN or ON.
6652 * Returns nb of found elements and elements them-selves.
6654 * 'ALL' type means elements of any type excluding nodes, balls and 0D elements
6656 //=======================================================================
6658 int SMESH_ElementSearcherImpl::
6659 FindElementsByPoint(const gp_Pnt& point,
6660 SMDSAbs_ElementType type,
6661 vector< const SMDS_MeshElement* >& foundElements)
6663 foundElements.clear();
6665 double tolerance = getTolerance();
6667 // =================================================================================
6668 if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball)
6670 if ( !_nodeSearcher )
6671 _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh );
6673 const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point );
6674 if ( !closeNode ) return foundElements.size();
6676 if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance )
6677 return foundElements.size(); // to far from any node
6679 if ( type == SMDSAbs_Node )
6681 foundElements.push_back( closeNode );
6685 SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type );
6686 while ( elemIt->more() )
6687 foundElements.push_back( elemIt->next() );
6690 // =================================================================================
6691 else // elements more complex than 0D
6693 if ( !_ebbTree || _elementType != type )
6695 if ( _ebbTree ) delete _ebbTree;
6696 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance );
6698 TIDSortedElemSet suspectElems;
6699 _ebbTree->getElementsNearPoint( point, suspectElems );
6700 TIDSortedElemSet::iterator elem = suspectElems.begin();
6701 for ( ; elem != suspectElems.end(); ++elem )
6702 if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance ))
6703 foundElements.push_back( *elem );
6705 return foundElements.size();
6708 //=======================================================================
6710 * \brief Find an element of given type most close to the given point
6712 * WARNING: Only face search is implemeneted so far
6714 //=======================================================================
6716 const SMDS_MeshElement*
6717 SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point,
6718 SMDSAbs_ElementType type )
6720 const SMDS_MeshElement* closestElem = 0;
6722 if ( type == SMDSAbs_Face )
6724 if ( !_ebbTree || _elementType != type )
6726 if ( _ebbTree ) delete _ebbTree;
6727 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
6729 TIDSortedElemSet suspectElems;
6730 _ebbTree->getElementsNearPoint( point, suspectElems );
6732 if ( suspectElems.empty() && _ebbTree->maxSize() > 0 )
6734 gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox().CornerMin() +
6735 _ebbTree->getBox().CornerMax() );
6737 if ( _ebbTree->getBox().IsOut( point.XYZ() ))
6738 radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize();
6740 radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2;
6741 while ( suspectElems.empty() )
6743 _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems );
6747 double minDist = std::numeric_limits<double>::max();
6748 multimap< double, const SMDS_MeshElement* > dist2face;
6749 TIDSortedElemSet::iterator elem = suspectElems.begin();
6750 for ( ; elem != suspectElems.end(); ++elem )
6752 double dist = SMESH_MeshEditor::GetDistance( dynamic_cast<const SMDS_MeshFace*>(*elem),
6754 if ( dist < minDist + 1e-10)
6757 dist2face.insert( dist2face.begin(), make_pair( dist, *elem ));
6760 if ( !dist2face.empty() )
6762 multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin();
6763 closestElem = d2f->second;
6764 // if there are several elements at the same distance, select one
6765 // with GC closest to the point
6766 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
6767 double minDistToGC = 0;
6768 for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f )
6770 if ( minDistToGC == 0 )
6773 gc = accumulate( TXyzIterator(closestElem->nodesIterator()),
6774 TXyzIterator(), gc ) / closestElem->NbNodes();
6775 minDistToGC = point.SquareDistance( gc );
6778 gc = accumulate( TXyzIterator( d2f->second->nodesIterator()),
6779 TXyzIterator(), gc ) / d2f->second->NbNodes();
6780 double d = point.SquareDistance( gc );
6781 if ( d < minDistToGC )
6784 closestElem = d2f->second;
6787 // cout << "FindClosestTo( " <<point.X()<<", "<<point.Y()<<", "<<point.Z()<<" ) FACE "
6788 // <<closestElem->GetID() << " DIST " << minDist << endl;
6793 // NOT IMPLEMENTED SO FAR
6799 //================================================================================
6801 * \brief Classify the given point in the closed 2D mesh
6803 //================================================================================
6805 TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point)
6807 double tolerance = getTolerance();
6808 if ( !_ebbTree || _elementType != SMDSAbs_Face )
6810 if ( _ebbTree ) delete _ebbTree;
6811 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt );
6813 // Algo: analyse transition of a line starting at the point through mesh boundary;
6814 // try three lines parallel to axis of the coordinate system and perform rough
6815 // analysis. If solution is not clear perform thorough analysis.
6817 const int nbAxes = 3;
6818 gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() };
6819 map< double, TInters > paramOnLine2TInters[ nbAxes ];
6820 list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line
6821 multimap< int, int > nbInt2Axis; // to find the simplest case
6822 for ( int axis = 0; axis < nbAxes; ++axis )
6824 gp_Ax1 lineAxis( point, axisDir[axis]);
6825 gp_Lin line ( lineAxis );
6827 TIDSortedElemSet suspectFaces; // faces possibly intersecting the line
6828 _ebbTree->getElementsNearLine( lineAxis, suspectFaces );
6830 // Intersect faces with the line
6832 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6833 TIDSortedElemSet::iterator face = suspectFaces.begin();
6834 for ( ; face != suspectFaces.end(); ++face )
6838 if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue;
6839 gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm );
6841 // perform intersection
6842 IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane ));
6843 if ( !intersection.IsDone() )
6845 if ( intersection.IsInQuadric() )
6847 tangentInters[ axis ].push_back( TInters( *face, fNorm, true ));
6849 else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 )
6851 gp_Pnt intersectionPoint = intersection.Point(1);
6852 if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance ))
6853 u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm )));
6856 // Analyse intersections roughly
6858 int nbInter = u2inters.size();
6862 double f = u2inters.begin()->first, l = u2inters.rbegin()->first;
6863 if ( nbInter == 1 ) // not closed mesh
6864 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6866 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6869 if ( (f<0) == (l<0) )
6872 int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0));
6873 int nbIntAfterPoint = nbInter - nbIntBeforePoint;
6874 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
6877 nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis ));
6879 if ( _outerFacesFound ) break; // pass to thorough analysis
6881 } // three attempts - loop on CS axes
6883 // Analyse intersections thoroughly.
6884 // We make two loops maximum, on the first one we only exclude touching intersections,
6885 // on the second, if situation is still unclear, we gather and use information on
6886 // position of faces (internal or outer). If faces position is already gathered,
6887 // we make the second loop right away.
6889 for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo )
6891 multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin();
6892 for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis )
6894 int axis = nb_axis->second;
6895 map< double, TInters > & u2inters = paramOnLine2TInters[ axis ];
6897 gp_Ax1 lineAxis( point, axisDir[axis]);
6898 gp_Lin line ( lineAxis );
6900 // add tangent intersections to u2inters
6902 list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin();
6903 for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt )
6904 if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param ))
6905 u2inters.insert(make_pair( param, *tgtInt ));
6906 tangentInters[ axis ].clear();
6908 // Count intersections before and after the point excluding touching ones.
6909 // If hasPositionInfo we count intersections of outer boundary only
6911 int nbIntBeforePoint = 0, nbIntAfterPoint = 0;
6912 double f = numeric_limits<double>::max(), l = -numeric_limits<double>::max();
6913 map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1;
6914 bool ok = ! u_int1->second._coincides;
6915 while ( ok && u_int1 != u2inters.end() )
6917 double u = u_int1->first;
6918 bool touchingInt = false;
6919 if ( ++u_int2 != u2inters.end() )
6921 // skip intersections at the same point (if the line passes through edge or node)
6923 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance )
6929 // skip tangent intersections
6931 const SMDS_MeshElement* prevFace = u_int1->second._face;
6932 while ( ok && u_int2->second._coincides )
6934 if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() )
6940 ok = ( u_int2 != u2inters.end() );
6945 // skip intersections at the same point after tangent intersections
6948 double u2 = u_int2->first;
6950 while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance )
6956 // decide if we skipped a touching intersection
6957 if ( nbSamePnt + nbTgt > 0 )
6959 double minDot = numeric_limits<double>::max(), maxDot = -numeric_limits<double>::max();
6960 map< double, TInters >::iterator u_int = u_int1;
6961 for ( ; u_int != u_int2; ++u_int )
6963 if ( u_int->second._coincides ) continue;
6964 double dot = u_int->second._faceNorm * line.Direction();
6965 if ( dot > maxDot ) maxDot = dot;
6966 if ( dot < minDot ) minDot = dot;
6968 touchingInt = ( minDot*maxDot < 0 );
6973 if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face ))
6984 u_int1 = u_int2; // to next intersection
6986 } // loop on intersections with one line
6990 if ( fabs( f ) < tolerance || fabs( l ) < tolerance )
6993 if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0)
6996 if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh
6997 return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN;
6999 if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 )
7002 if ( (f<0) == (l<0) )
7005 if ( hasPositionInfo )
7006 return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT;
7008 } // loop on intersections of the tree lines - thorough analysis
7010 if ( !hasPositionInfo )
7012 // gather info on faces position - is face in the outer boundary or not
7013 map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ];
7014 findOuterBoundary( u2inters.begin()->second._face );
7017 } // two attempts - with and w/o faces position info in the mesh
7019 return TopAbs_UNKNOWN;
7022 //=======================================================================
7024 * \brief Return elements possibly intersecting the line
7026 //=======================================================================
7028 void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line,
7029 SMDSAbs_ElementType type,
7030 vector< const SMDS_MeshElement* >& foundElems)
7032 if ( !_ebbTree || _elementType != type )
7034 if ( _ebbTree ) delete _ebbTree;
7035 _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt );
7037 TIDSortedElemSet suspectFaces; // elements possibly intersecting the line
7038 _ebbTree->getElementsNearLine( line, suspectFaces );
7039 foundElems.assign( suspectFaces.begin(), suspectFaces.end());
7042 //=======================================================================
7044 * \brief Return SMESH_ElementSearcher
7046 //=======================================================================
7048 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher()
7050 return new SMESH_ElementSearcherImpl( *GetMeshDS() );
7053 //=======================================================================
7055 * \brief Return SMESH_ElementSearcher acting on a sub-set of elements
7057 //=======================================================================
7059 SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt)
7061 return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt );
7064 //=======================================================================
7066 * \brief Return true if the point is IN or ON of the element
7068 //=======================================================================
7070 bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol )
7072 if ( element->GetType() == SMDSAbs_Volume)
7074 return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol );
7077 // get ordered nodes
7079 vector< gp_XYZ > xyz;
7080 vector<const SMDS_MeshNode*> nodeList;
7082 SMDS_ElemIteratorPtr nodeIt = element->nodesIterator();
7083 if ( element->IsQuadratic() ) {
7084 if (const SMDS_VtkFace* f=dynamic_cast<const SMDS_VtkFace*>(element))
7085 nodeIt = f->interlacedNodesElemIterator();
7086 else if (const SMDS_VtkEdge* e =dynamic_cast<const SMDS_VtkEdge*>(element))
7087 nodeIt = e->interlacedNodesElemIterator();
7089 while ( nodeIt->more() )
7091 const SMDS_MeshNode* node = cast2Node( nodeIt->next() );
7092 xyz.push_back( SMESH_TNodeXYZ(node) );
7093 nodeList.push_back(node);
7096 int i, nbNodes = element->NbNodes();
7098 if ( element->GetType() == SMDSAbs_Face ) // --------------------------------------------------
7100 // compute face normal
7101 gp_Vec faceNorm(0,0,0);
7102 xyz.push_back( xyz.front() );
7103 nodeList.push_back( nodeList.front() );
7104 for ( i = 0; i < nbNodes; ++i )
7106 gp_Vec edge1( xyz[i+1], xyz[i]);
7107 gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] );
7108 faceNorm += edge1 ^ edge2;
7110 double normSize = faceNorm.Magnitude();
7111 if ( normSize <= tol )
7113 // degenerated face: point is out if it is out of all face edges
7114 for ( i = 0; i < nbNodes; ++i )
7116 SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] );
7117 if ( !IsOut( &edge, point, tol ))
7122 faceNorm /= normSize;
7124 // check if the point lays on face plane
7125 gp_Vec n2p( xyz[0], point );
7126 if ( fabs( n2p * faceNorm ) > tol )
7127 return true; // not on face plane
7129 // check if point is out of face boundary:
7130 // define it by closest transition of a ray point->infinity through face boundary
7131 // on the face plane.
7132 // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool
7133 // to find intersections of the ray with the boundary.
7135 gp_Vec plnNorm = ray ^ faceNorm;
7136 normSize = plnNorm.Magnitude();
7137 if ( normSize <= tol ) return false; // point coincides with the first node
7138 plnNorm /= normSize;
7139 // for each node of the face, compute its signed distance to the plane
7140 vector<double> dist( nbNodes + 1);
7141 for ( i = 0; i < nbNodes; ++i )
7143 gp_Vec n2p( xyz[i], point );
7144 dist[i] = n2p * plnNorm;
7146 dist.back() = dist.front();
7147 // find the closest intersection
7149 double rClosest, distClosest = 1e100;;
7151 for ( i = 0; i < nbNodes; ++i )
7154 if ( fabs( dist[i]) < tol )
7156 else if ( fabs( dist[i+1]) < tol )
7158 else if ( dist[i] * dist[i+1] < 0 )
7159 r = dist[i] / ( dist[i] - dist[i+1] );
7161 continue; // no intersection
7162 gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r;
7163 gp_Vec p2int ( point, pInt);
7164 if ( p2int * ray > -tol ) // right half-space
7166 double intDist = p2int.SquareMagnitude();
7167 if ( intDist < distClosest )
7172 distClosest = intDist;
7177 return true; // no intesections - out
7179 // analyse transition
7180 gp_Vec edge( xyz[iClosest], xyz[iClosest+1] );
7181 gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face
7182 gp_Vec p2int ( point, pClosest );
7183 bool out = (edgeNorm * p2int) < -tol;
7184 if ( rClosest > 0. && rClosest < 1. ) // not node intersection
7187 // ray pass through a face node; analyze transition through an adjacent edge
7188 gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ];
7189 gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ];
7190 gp_Vec edgeAdjacent( p1, p2 );
7191 gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm );
7192 bool out2 = (edgeNorm2 * p2int) < -tol;
7194 bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0;
7195 return covexCorner ? (out || out2) : (out && out2);
7197 if ( element->GetType() == SMDSAbs_Edge ) // --------------------------------------------------
7199 // point is out of edge if it is NOT ON any straight part of edge
7200 // (we consider quadratic edge as being composed of two straight parts)
7201 for ( i = 1; i < nbNodes; ++i )
7203 gp_Vec edge( xyz[i-1], xyz[i]);
7204 gp_Vec n1p ( xyz[i-1], point);
7205 double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude();
7208 gp_Vec n2p( xyz[i], point );
7209 if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol )
7211 return false; // point is ON this part
7215 // Node or 0D element -------------------------------------------------------------------------
7217 gp_Vec n2p ( xyz[0], point );
7218 return n2p.Magnitude() <= tol;
7223 //=======================================================================
7227 // Position of a point relative to a segment
7231 // VERTEX 1 o----ON-----> VERTEX 2
7235 enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8,
7236 POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX };
7240 int _index; // index of vertex or segment
7242 PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {}
7243 bool operator < (const PointPos& other ) const
7245 if ( _name == other._name )
7246 return ( _index < 0 || other._index < 0 ) ? false : _index < other._index;
7247 return _name < other._name;
7251 //================================================================================
7253 * \brief Return of a point relative to a segment
7254 * \param point2D - the point to analyze position of
7255 * \param xyVec - end points of segments
7256 * \param index0 - 0-based index of the first point of segment
7257 * \param posToFindOut - flags of positions to detect
7258 * \retval PointPos - point position
7260 //================================================================================
7262 PointPos getPointPosition( const gp_XY& point2D,
7263 const gp_XY* segEnds,
7264 const int index0 = 0,
7265 const int posToFindOut = POS_ALL)
7267 const gp_XY& p1 = segEnds[ index0 ];
7268 const gp_XY& p2 = segEnds[ index0+1 ];
7269 const gp_XY grad = p2 - p1;
7271 if ( posToFindOut & POS_VERTEX )
7273 // check if the point2D is at "vertex 1" zone
7274 gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(),
7275 p1.Y() + grad.X() ) };
7276 if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT )
7277 return PointPos( POS_VERTEX, index0 );
7279 // check if the point2D is at "vertex 2" zone
7280 gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(),
7281 p2.Y() + grad.X() ) };
7282 if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT )
7283 return PointPos( POS_VERTEX, index0 + 1);
7285 double edgeEquation =
7286 ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X();
7287 return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 );
7291 //=======================================================================
7293 * \brief Return minimal distance from a point to a face
7295 * Currently we ignore non-planarity and 2nd order of face
7297 //=======================================================================
7299 double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face,
7300 const gp_Pnt& point )
7302 double badDistance = -1;
7303 if ( !face ) return badDistance;
7305 // coordinates of nodes (medium nodes, if any, ignored)
7306 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator;
7307 vector<gp_XYZ> xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() );
7308 xyz.resize( face->NbCornerNodes()+1 );
7310 // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis,
7311 // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane.
7313 gp_Vec OZ ( xyz[0], xyz[1] );
7314 gp_Vec OX ( xyz[0], xyz[2] );
7315 if ( OZ.Magnitude() < std::numeric_limits<double>::min() )
7317 if ( xyz.size() < 4 ) return badDistance;
7318 OZ = gp_Vec ( xyz[0], xyz[2] );
7319 OX = gp_Vec ( xyz[0], xyz[3] );
7323 tgtCS = gp_Ax3( xyz[0], OZ, OX );
7325 catch ( Standard_Failure ) {
7328 trsf.SetTransformation( tgtCS );
7330 // move all the nodes to 2D
7331 vector<gp_XY> xy( xyz.size() );
7332 for ( size_t i = 0;i < xyz.size()-1; ++i )
7334 gp_XYZ p3d = xyz[i];
7335 trsf.Transforms( p3d );
7336 xy[i].SetCoord( p3d.X(), p3d.Z() );
7338 xyz.back() = xyz.front();
7339 xy.back() = xy.front();
7341 // // move the point in 2D
7342 gp_XYZ tmpPnt = point.XYZ();
7343 trsf.Transforms( tmpPnt );
7344 gp_XY point2D( tmpPnt.X(), tmpPnt.Z() );
7346 // loop on segments of the face to analyze point position ralative to the face
7347 set< PointPos > pntPosSet;
7348 for ( size_t i = 1; i < xy.size(); ++i )
7350 PointPos pos = getPointPosition( point2D, &xy[0], i-1 );
7351 pntPosSet.insert( pos );
7355 PointPos pos = *pntPosSet.begin();
7356 // cout << "Face " << face->GetID() << " DIST: ";
7357 switch ( pos._name )
7360 // point is most close to a segment
7361 gp_Vec p0p1( point, xyz[ pos._index ] );
7362 gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector
7364 double projDist = p0p1 * p1p2; // distance projected to the segment
7365 gp_Vec projVec = p1p2 * projDist;
7366 gp_Vec distVec = p0p1 - projVec;
7367 // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID()
7368 // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl;
7369 return distVec.Magnitude();
7372 // point is inside the face
7373 double distToFacePlane = tmpPnt.Y();
7374 // cout << distToFacePlane << ", INSIDE " << endl;
7375 return Abs( distToFacePlane );
7378 // point is most close to a node
7379 gp_Vec distVec( point, xyz[ pos._index ]);
7380 // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl;
7381 return distVec.Magnitude();
7387 //=======================================================================
7388 //function : SimplifyFace
7390 //=======================================================================
7391 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *> faceNodes,
7392 vector<const SMDS_MeshNode *>& poly_nodes,
7393 vector<int>& quantities) const
7395 int nbNodes = faceNodes.size();
7400 set<const SMDS_MeshNode*> nodeSet;
7402 // get simple seq of nodes
7403 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7404 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7405 int iSimple = 0, nbUnique = 0;
7407 simpleNodes[iSimple++] = faceNodes[0];
7409 for (int iCur = 1; iCur < nbNodes; iCur++) {
7410 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7411 simpleNodes[iSimple++] = faceNodes[iCur];
7412 if (nodeSet.insert( faceNodes[iCur] ).second)
7416 int nbSimple = iSimple;
7417 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7427 bool foundLoop = (nbSimple > nbUnique);
7430 set<const SMDS_MeshNode*> loopSet;
7431 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7432 const SMDS_MeshNode* n = simpleNodes[iSimple];
7433 if (!loopSet.insert( n ).second) {
7437 int iC = 0, curLast = iSimple;
7438 for (; iC < curLast; iC++) {
7439 if (simpleNodes[iC] == n) break;
7441 int loopLen = curLast - iC;
7443 // create sub-element
7445 quantities.push_back(loopLen);
7446 for (; iC < curLast; iC++) {
7447 poly_nodes.push_back(simpleNodes[iC]);
7450 // shift the rest nodes (place from the first loop position)
7451 for (iC = curLast + 1; iC < nbSimple; iC++) {
7452 simpleNodes[iC - loopLen] = simpleNodes[iC];
7454 nbSimple -= loopLen;
7457 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7458 } // while (foundLoop)
7462 quantities.push_back(iSimple);
7463 for (int i = 0; i < iSimple; i++)
7464 poly_nodes.push_back(simpleNodes[i]);
7470 //=======================================================================
7471 //function : MergeNodes
7472 //purpose : In each group, the cdr of nodes are substituted by the first one
7474 //=======================================================================
7476 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7478 MESSAGE("MergeNodes");
7479 myLastCreatedElems.Clear();
7480 myLastCreatedNodes.Clear();
7482 SMESHDS_Mesh* aMesh = GetMeshDS();
7484 TNodeNodeMap nodeNodeMap; // node to replace - new node
7485 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7486 list< int > rmElemIds, rmNodeIds;
7488 // Fill nodeNodeMap and elems
7490 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7491 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7492 list<const SMDS_MeshNode*>& nodes = *grIt;
7493 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7494 const SMDS_MeshNode* nToKeep = *nIt;
7495 //MESSAGE("node to keep " << nToKeep->GetID());
7496 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7497 const SMDS_MeshNode* nToRemove = *nIt;
7498 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7499 if ( nToRemove != nToKeep ) {
7500 //MESSAGE(" node to remove " << nToRemove->GetID());
7501 rmNodeIds.push_back( nToRemove->GetID() );
7502 AddToSameGroups( nToKeep, nToRemove, aMesh );
7505 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7506 while ( invElemIt->more() ) {
7507 const SMDS_MeshElement* elem = invElemIt->next();
7512 // Change element nodes or remove an element
7514 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7515 for ( ; eIt != elems.end(); eIt++ ) {
7516 const SMDS_MeshElement* elem = *eIt;
7517 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7518 int nbNodes = elem->NbNodes();
7519 int aShapeId = FindShape( elem );
7521 set<const SMDS_MeshNode*> nodeSet;
7522 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7523 int iUnique = 0, iCur = 0, nbRepl = 0;
7524 vector<int> iRepl( nbNodes );
7526 // get new seq of nodes
7527 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7528 while ( itN->more() ) {
7529 const SMDS_MeshNode* n =
7530 static_cast<const SMDS_MeshNode*>( itN->next() );
7532 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7533 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7535 // BUG 0020185: begin
7537 bool stopRecur = false;
7538 set<const SMDS_MeshNode*> nodesRecur;
7539 nodesRecur.insert(n);
7540 while (!stopRecur) {
7541 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7542 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7543 n = (*nnIt_i).second;
7544 if (!nodesRecur.insert(n).second) {
7545 // error: recursive dependancy
7555 curNodes[ iCur ] = n;
7556 bool isUnique = nodeSet.insert( n ).second;
7558 uniqueNodes[ iUnique++ ] = n;
7560 iRepl[ nbRepl++ ] = iCur;
7564 // Analyse element topology after replacement
7567 int nbUniqueNodes = nodeSet.size();
7568 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7569 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7570 // Polygons and Polyhedral volumes
7571 if (elem->IsPoly()) {
7573 if (elem->GetType() == SMDSAbs_Face) {
7575 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7577 for (; inode < nbNodes; inode++) {
7578 face_nodes[inode] = curNodes[inode];
7581 vector<const SMDS_MeshNode *> polygons_nodes;
7582 vector<int> quantities;
7583 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7586 for (int iface = 0; iface < nbNew; iface++) {
7587 int nbNodes = quantities[iface];
7588 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7589 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7590 poly_nodes[ii] = polygons_nodes[inode];
7592 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7593 myLastCreatedElems.Append(newElem);
7595 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7598 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7599 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7600 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7602 if (nbNew > 0) quid = nbNew - 1;
7603 vector<int> newquant(quantities.begin()+quid, quantities.end());
7604 const SMDS_MeshElement* newElem = 0;
7605 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7606 myLastCreatedElems.Append(newElem);
7607 if ( aShapeId && newElem )
7608 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7609 rmElemIds.push_back(elem->GetID());
7612 rmElemIds.push_back(elem->GetID());
7616 else if (elem->GetType() == SMDSAbs_Volume) {
7617 // Polyhedral volume
7618 if (nbUniqueNodes < 4) {
7619 rmElemIds.push_back(elem->GetID());
7622 // each face has to be analyzed in order to check volume validity
7623 const SMDS_VtkVolume* aPolyedre =
7624 dynamic_cast<const SMDS_VtkVolume*>( elem );
7626 int nbFaces = aPolyedre->NbFaces();
7628 vector<const SMDS_MeshNode *> poly_nodes;
7629 vector<int> quantities;
7631 for (int iface = 1; iface <= nbFaces; iface++) {
7632 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7633 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7635 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7636 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7637 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7638 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7639 faceNode = (*nnIt).second;
7641 faceNodes[inode - 1] = faceNode;
7644 SimplifyFace(faceNodes, poly_nodes, quantities);
7647 if (quantities.size() > 3) {
7648 // to be done: remove coincident faces
7651 if (quantities.size() > 3)
7653 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7654 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7655 const SMDS_MeshElement* newElem = 0;
7656 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7657 myLastCreatedElems.Append(newElem);
7658 if ( aShapeId && newElem )
7659 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7660 rmElemIds.push_back(elem->GetID());
7664 rmElemIds.push_back(elem->GetID());
7675 // TODO not all the possible cases are solved. Find something more generic?
7676 switch ( nbNodes ) {
7677 case 2: ///////////////////////////////////// EDGE
7678 isOk = false; break;
7679 case 3: ///////////////////////////////////// TRIANGLE
7680 isOk = false; break;
7682 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7684 else { //////////////////////////////////// QUADRANGLE
7685 if ( nbUniqueNodes < 3 )
7687 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7688 isOk = false; // opposite nodes stick
7689 //MESSAGE("isOk " << isOk);
7692 case 6: ///////////////////////////////////// PENTAHEDRON
7693 if ( nbUniqueNodes == 4 ) {
7694 // ---------------------------------> tetrahedron
7696 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7697 // all top nodes stick: reverse a bottom
7698 uniqueNodes[ 0 ] = curNodes [ 1 ];
7699 uniqueNodes[ 1 ] = curNodes [ 0 ];
7701 else if (nbRepl == 3 &&
7702 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7703 // all bottom nodes stick: set a top before
7704 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7705 uniqueNodes[ 0 ] = curNodes [ 3 ];
7706 uniqueNodes[ 1 ] = curNodes [ 4 ];
7707 uniqueNodes[ 2 ] = curNodes [ 5 ];
7709 else if (nbRepl == 4 &&
7710 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7711 // a lateral face turns into a line: reverse a bottom
7712 uniqueNodes[ 0 ] = curNodes [ 1 ];
7713 uniqueNodes[ 1 ] = curNodes [ 0 ];
7718 else if ( nbUniqueNodes == 5 ) {
7719 // PENTAHEDRON --------------------> 2 tetrahedrons
7720 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7721 // a bottom node sticks with a linked top one
7723 SMDS_MeshElement* newElem =
7724 aMesh->AddVolume(curNodes[ 3 ],
7727 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7728 myLastCreatedElems.Append(newElem);
7730 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7731 // 2. : reverse a bottom
7732 uniqueNodes[ 0 ] = curNodes [ 1 ];
7733 uniqueNodes[ 1 ] = curNodes [ 0 ];
7743 if(elem->IsQuadratic()) { // Quadratic quadrangle
7755 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7758 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7760 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7761 uniqueNodes[0] = curNodes[0];
7762 uniqueNodes[1] = curNodes[2];
7763 uniqueNodes[2] = curNodes[3];
7764 uniqueNodes[3] = curNodes[5];
7765 uniqueNodes[4] = curNodes[6];
7766 uniqueNodes[5] = curNodes[7];
7769 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7770 uniqueNodes[0] = curNodes[0];
7771 uniqueNodes[1] = curNodes[1];
7772 uniqueNodes[2] = curNodes[2];
7773 uniqueNodes[3] = curNodes[4];
7774 uniqueNodes[4] = curNodes[5];
7775 uniqueNodes[5] = curNodes[6];
7778 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7779 uniqueNodes[0] = curNodes[1];
7780 uniqueNodes[1] = curNodes[2];
7781 uniqueNodes[2] = curNodes[3];
7782 uniqueNodes[3] = curNodes[5];
7783 uniqueNodes[4] = curNodes[6];
7784 uniqueNodes[5] = curNodes[0];
7787 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7788 uniqueNodes[0] = curNodes[0];
7789 uniqueNodes[1] = curNodes[1];
7790 uniqueNodes[2] = curNodes[3];
7791 uniqueNodes[3] = curNodes[4];
7792 uniqueNodes[4] = curNodes[6];
7793 uniqueNodes[5] = curNodes[7];
7796 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7797 uniqueNodes[0] = curNodes[0];
7798 uniqueNodes[1] = curNodes[2];
7799 uniqueNodes[2] = curNodes[3];
7800 uniqueNodes[3] = curNodes[1];
7801 uniqueNodes[4] = curNodes[6];
7802 uniqueNodes[5] = curNodes[7];
7805 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7806 uniqueNodes[0] = curNodes[0];
7807 uniqueNodes[1] = curNodes[1];
7808 uniqueNodes[2] = curNodes[2];
7809 uniqueNodes[3] = curNodes[4];
7810 uniqueNodes[4] = curNodes[5];
7811 uniqueNodes[5] = curNodes[7];
7814 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7815 uniqueNodes[0] = curNodes[0];
7816 uniqueNodes[1] = curNodes[1];
7817 uniqueNodes[2] = curNodes[3];
7818 uniqueNodes[3] = curNodes[4];
7819 uniqueNodes[4] = curNodes[2];
7820 uniqueNodes[5] = curNodes[7];
7823 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7824 uniqueNodes[0] = curNodes[0];
7825 uniqueNodes[1] = curNodes[1];
7826 uniqueNodes[2] = curNodes[2];
7827 uniqueNodes[3] = curNodes[4];
7828 uniqueNodes[4] = curNodes[5];
7829 uniqueNodes[5] = curNodes[3];
7834 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7837 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7841 //////////////////////////////////// HEXAHEDRON
7843 SMDS_VolumeTool hexa (elem);
7844 hexa.SetExternalNormal();
7845 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7846 //////////////////////// HEX ---> 1 tetrahedron
7847 for ( int iFace = 0; iFace < 6; iFace++ ) {
7848 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7849 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7850 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7851 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7852 // one face turns into a point ...
7853 int iOppFace = hexa.GetOppFaceIndex( iFace );
7854 ind = hexa.GetFaceNodesIndices( iOppFace );
7856 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7857 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7860 if ( nbStick == 1 ) {
7861 // ... and the opposite one - into a triangle.
7863 ind = hexa.GetFaceNodesIndices( iFace );
7864 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7871 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7872 //////////////////////// HEX ---> 1 prism
7873 int nbTria = 0, iTria[3];
7874 const int *ind; // indices of face nodes
7875 // look for triangular faces
7876 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7877 ind = hexa.GetFaceNodesIndices( iFace );
7878 TIDSortedNodeSet faceNodes;
7879 for ( iCur = 0; iCur < 4; iCur++ )
7880 faceNodes.insert( curNodes[ind[iCur]] );
7881 if ( faceNodes.size() == 3 )
7882 iTria[ nbTria++ ] = iFace;
7884 // check if triangles are opposite
7885 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7888 // set nodes of the bottom triangle
7889 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7891 for ( iCur = 0; iCur < 4; iCur++ )
7892 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7893 indB.push_back( ind[iCur] );
7894 if ( !hexa.IsForward() )
7895 std::swap( indB[0], indB[2] );
7896 for ( iCur = 0; iCur < 3; iCur++ )
7897 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7898 // set nodes of the top triangle
7899 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7900 for ( iCur = 0; iCur < 3; ++iCur )
7901 for ( int j = 0; j < 4; ++j )
7902 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7904 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7910 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7911 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7912 for ( int iFace = 0; iFace < 6; iFace++ ) {
7913 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7914 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7915 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7916 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7917 // one face turns into a point ...
7918 int iOppFace = hexa.GetOppFaceIndex( iFace );
7919 ind = hexa.GetFaceNodesIndices( iOppFace );
7921 iUnique = 2; // reverse a tetrahedron 1 bottom
7922 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7923 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7925 else if ( iUnique >= 0 )
7926 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7928 if ( nbStick == 0 ) {
7929 // ... and the opposite one is a quadrangle
7931 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7932 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7935 SMDS_MeshElement* newElem =
7936 aMesh->AddVolume(curNodes[ind[ 0 ]],
7939 curNodes[indTop[ 0 ]]);
7940 myLastCreatedElems.Append(newElem);
7942 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7949 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7950 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7951 // find indices of quad and tri faces
7952 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7953 for ( iFace = 0; iFace < 6; iFace++ ) {
7954 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7956 for ( iCur = 0; iCur < 4; iCur++ )
7957 nodeSet.insert( curNodes[ind[ iCur ]] );
7958 nbUniqueNodes = nodeSet.size();
7959 if ( nbUniqueNodes == 3 )
7960 iTriFace[ nbTri++ ] = iFace;
7961 else if ( nbUniqueNodes == 4 )
7962 iQuadFace[ nbQuad++ ] = iFace;
7964 if (nbQuad == 2 && nbTri == 4 &&
7965 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7966 // 2 opposite quadrangles stuck with a diagonal;
7967 // sample groups of merged indices: (0-4)(2-6)
7968 // --------------------------------------------> 2 tetrahedrons
7969 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7970 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7971 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7972 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7973 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7974 // stuck with 0-2 diagonal
7982 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7983 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7984 // stuck with 1-3 diagonal
7996 uniqueNodes[ 0 ] = curNodes [ i0 ];
7997 uniqueNodes[ 1 ] = curNodes [ i1d ];
7998 uniqueNodes[ 2 ] = curNodes [ i3d ];
7999 uniqueNodes[ 3 ] = curNodes [ i0t ];
8002 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
8006 myLastCreatedElems.Append(newElem);
8008 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8011 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
8012 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
8013 // --------------------------------------------> prism
8014 // find 2 opposite triangles
8016 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
8017 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
8018 // find indices of kept and replaced nodes
8019 // and fill unique nodes of 2 opposite triangles
8020 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
8021 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
8022 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
8023 // fill unique nodes
8026 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
8027 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
8028 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
8030 // iCur of a linked node of the opposite face (make normals co-directed):
8031 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
8032 // check that correspondent corners of triangles are linked
8033 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
8036 uniqueNodes[ iUnique ] = n;
8037 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
8046 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
8049 MESSAGE("MergeNodes() removes hexahedron "<< elem);
8056 } // switch ( nbNodes )
8058 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
8060 if ( isOk ) { // the elem remains valid after sticking nodes
8061 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
8063 // Change nodes of polyedre
8064 const SMDS_VtkVolume* aPolyedre =
8065 dynamic_cast<const SMDS_VtkVolume*>( elem );
8067 int nbFaces = aPolyedre->NbFaces();
8069 vector<const SMDS_MeshNode *> poly_nodes;
8070 vector<int> quantities (nbFaces);
8072 for (int iface = 1; iface <= nbFaces; iface++) {
8073 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
8074 quantities[iface - 1] = nbFaceNodes;
8076 for (inode = 1; inode <= nbFaceNodes; inode++) {
8077 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
8079 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
8080 if (nnIt != nodeNodeMap.end()) { // curNode sticks
8081 curNode = (*nnIt).second;
8083 poly_nodes.push_back(curNode);
8086 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
8089 else // replace non-polyhedron elements
8091 const SMDSAbs_ElementType etyp = elem->GetType();
8092 const int elemId = elem->GetID();
8093 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
8094 uniqueNodes.resize(nbUniqueNodes);
8096 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
8098 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
8099 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
8100 if ( sm && newElem )
8101 sm->AddElement( newElem );
8102 if ( elem != newElem )
8103 ReplaceElemInGroups( elem, newElem, aMesh );
8107 // Remove invalid regular element or invalid polygon
8108 rmElemIds.push_back( elem->GetID() );
8111 } // loop on elements
8113 // Remove bad elements, then equal nodes (order important)
8115 Remove( rmElemIds, false );
8116 Remove( rmNodeIds, true );
8121 // ========================================================
8122 // class : SortableElement
8123 // purpose : allow sorting elements basing on their nodes
8124 // ========================================================
8125 class SortableElement : public set <const SMDS_MeshElement*>
8129 SortableElement( const SMDS_MeshElement* theElem )
8132 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
8133 while ( nodeIt->more() )
8134 this->insert( nodeIt->next() );
8137 const SMDS_MeshElement* Get() const
8140 void Set(const SMDS_MeshElement* e) const
8145 mutable const SMDS_MeshElement* myElem;
8148 //=======================================================================
8149 //function : FindEqualElements
8150 //purpose : Return list of group of elements built on the same nodes.
8151 // Search among theElements or in the whole mesh if theElements is empty
8152 //=======================================================================
8153 void SMESH_MeshEditor::FindEqualElements(set<const SMDS_MeshElement*> & theElements,
8154 TListOfListOfElementsID & theGroupsOfElementsID)
8156 myLastCreatedElems.Clear();
8157 myLastCreatedNodes.Clear();
8159 typedef set<const SMDS_MeshElement*> TElemsSet;
8160 typedef map< SortableElement, int > TMapOfNodeSet;
8161 typedef list<int> TGroupOfElems;
8164 if ( theElements.empty() )
8165 { // get all elements in the mesh
8166 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
8167 while ( eIt->more() )
8168 elems.insert( elems.end(), eIt->next());
8171 elems = theElements;
8173 vector< TGroupOfElems > arrayOfGroups;
8174 TGroupOfElems groupOfElems;
8175 TMapOfNodeSet mapOfNodeSet;
8177 TElemsSet::iterator elemIt = elems.begin();
8178 for ( int i = 0, j=0; elemIt != elems.end(); ++elemIt, ++j ) {
8179 const SMDS_MeshElement* curElem = *elemIt;
8180 SortableElement SE(curElem);
8183 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8184 if( !(pp.second) ) {
8185 TMapOfNodeSet::iterator& itSE = pp.first;
8186 ind = (*itSE).second;
8187 arrayOfGroups[ind].push_back(curElem->GetID());
8190 groupOfElems.clear();
8191 groupOfElems.push_back(curElem->GetID());
8192 arrayOfGroups.push_back(groupOfElems);
8197 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8198 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
8199 groupOfElems = *groupIt;
8200 if ( groupOfElems.size() > 1 ) {
8201 groupOfElems.sort();
8202 theGroupsOfElementsID.push_back(groupOfElems);
8207 //=======================================================================
8208 //function : MergeElements
8209 //purpose : In each given group, substitute all elements by the first one.
8210 //=======================================================================
8212 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8214 myLastCreatedElems.Clear();
8215 myLastCreatedNodes.Clear();
8217 typedef list<int> TListOfIDs;
8218 TListOfIDs rmElemIds; // IDs of elems to remove
8220 SMESHDS_Mesh* aMesh = GetMeshDS();
8222 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8223 while ( groupsIt != theGroupsOfElementsID.end() ) {
8224 TListOfIDs& aGroupOfElemID = *groupsIt;
8225 aGroupOfElemID.sort();
8226 int elemIDToKeep = aGroupOfElemID.front();
8227 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8228 aGroupOfElemID.pop_front();
8229 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8230 while ( idIt != aGroupOfElemID.end() ) {
8231 int elemIDToRemove = *idIt;
8232 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8233 // add the kept element in groups of removed one (PAL15188)
8234 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8235 rmElemIds.push_back( elemIDToRemove );
8241 Remove( rmElemIds, false );
8244 //=======================================================================
8245 //function : MergeEqualElements
8246 //purpose : Remove all but one of elements built on the same nodes.
8247 //=======================================================================
8249 void SMESH_MeshEditor::MergeEqualElements()
8251 set<const SMDS_MeshElement*> aMeshElements; /* empty input -
8252 to merge equal elements in the whole mesh */
8253 TListOfListOfElementsID aGroupsOfElementsID;
8254 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8255 MergeElements(aGroupsOfElementsID);
8258 //=======================================================================
8259 //function : FindFaceInSet
8260 //purpose : Return a face having linked nodes n1 and n2 and which is
8261 // - not in avoidSet,
8262 // - in elemSet provided that !elemSet.empty()
8263 // i1 and i2 optionally returns indices of n1 and n2
8264 //=======================================================================
8266 const SMDS_MeshElement*
8267 SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1,
8268 const SMDS_MeshNode* n2,
8269 const TIDSortedElemSet& elemSet,
8270 const TIDSortedElemSet& avoidSet,
8276 const SMDS_MeshElement* face = 0;
8278 SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face);
8279 //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt);
8280 while ( invElemIt->more() && !face ) // loop on inverse faces of n1
8282 //MESSAGE("in while ( invElemIt->more() && !face )");
8283 const SMDS_MeshElement* elem = invElemIt->next();
8284 if (avoidSet.count( elem ))
8286 if ( !elemSet.empty() && !elemSet.count( elem ))
8289 i1 = elem->GetNodeIndex( n1 );
8290 // find a n2 linked to n1
8291 int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes();
8292 for ( int di = -1; di < 2 && !face; di += 2 )
8294 i2 = (i1+di+nbN) % nbN;
8295 if ( elem->GetNode( i2 ) == n2 )
8298 if ( !face && elem->IsQuadratic())
8300 // analysis for quadratic elements using all nodes
8301 const SMDS_VtkFace* F =
8302 dynamic_cast<const SMDS_VtkFace*>(elem);
8303 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8304 // use special nodes iterator
8305 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8306 const SMDS_MeshNode* prevN = cast2Node( anIter->next() );
8307 for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ )
8309 const SMDS_MeshNode* n = cast2Node( anIter->next() );
8310 if ( n1 == prevN && n2 == n )
8314 else if ( n2 == prevN && n1 == n )
8316 face = elem; swap( i1, i2 );
8322 if ( n1ind ) *n1ind = i1;
8323 if ( n2ind ) *n2ind = i2;
8327 //=======================================================================
8328 //function : findAdjacentFace
8330 //=======================================================================
8332 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8333 const SMDS_MeshNode* n2,
8334 const SMDS_MeshElement* elem)
8336 TIDSortedElemSet elemSet, avoidSet;
8338 avoidSet.insert ( elem );
8339 return SMESH_MeshEditor::FindFaceInSet( n1, n2, elemSet, avoidSet );
8342 //=======================================================================
8343 //function : FindFreeBorder
8345 //=======================================================================
8347 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8349 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8350 const SMDS_MeshNode* theSecondNode,
8351 const SMDS_MeshNode* theLastNode,
8352 list< const SMDS_MeshNode* > & theNodes,
8353 list< const SMDS_MeshElement* >& theFaces)
8355 if ( !theFirstNode || !theSecondNode )
8357 // find border face between theFirstNode and theSecondNode
8358 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8362 theFaces.push_back( curElem );
8363 theNodes.push_back( theFirstNode );
8364 theNodes.push_back( theSecondNode );
8366 //vector<const SMDS_MeshNode*> nodes;
8367 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8368 TIDSortedElemSet foundElems;
8369 bool needTheLast = ( theLastNode != 0 );
8371 while ( nStart != theLastNode ) {
8372 if ( nStart == theFirstNode )
8373 return !needTheLast;
8375 // find all free border faces sharing form nStart
8377 list< const SMDS_MeshElement* > curElemList;
8378 list< const SMDS_MeshNode* > nStartList;
8379 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8380 while ( invElemIt->more() ) {
8381 const SMDS_MeshElement* e = invElemIt->next();
8382 if ( e == curElem || foundElems.insert( e ).second ) {
8384 int iNode = 0, nbNodes = e->NbNodes();
8385 //const SMDS_MeshNode* nodes[nbNodes+1];
8386 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8388 if(e->IsQuadratic()) {
8389 const SMDS_VtkFace* F =
8390 dynamic_cast<const SMDS_VtkFace*>(e);
8391 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8392 // use special nodes iterator
8393 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8394 while( anIter->more() ) {
8395 nodes[ iNode++ ] = cast2Node(anIter->next());
8399 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8400 while ( nIt->more() )
8401 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8403 nodes[ iNode ] = nodes[ 0 ];
8405 for ( iNode = 0; iNode < nbNodes; iNode++ )
8406 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8407 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8408 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8410 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8411 curElemList.push_back( e );
8415 // analyse the found
8417 int nbNewBorders = curElemList.size();
8418 if ( nbNewBorders == 0 ) {
8419 // no free border furthermore
8420 return !needTheLast;
8422 else if ( nbNewBorders == 1 ) {
8423 // one more element found
8425 nStart = nStartList.front();
8426 curElem = curElemList.front();
8427 theFaces.push_back( curElem );
8428 theNodes.push_back( nStart );
8431 // several continuations found
8432 list< const SMDS_MeshElement* >::iterator curElemIt;
8433 list< const SMDS_MeshNode* >::iterator nStartIt;
8434 // check if one of them reached the last node
8435 if ( needTheLast ) {
8436 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8437 curElemIt!= curElemList.end();
8438 curElemIt++, nStartIt++ )
8439 if ( *nStartIt == theLastNode ) {
8440 theFaces.push_back( *curElemIt );
8441 theNodes.push_back( *nStartIt );
8445 // find the best free border by the continuations
8446 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8447 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8448 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8449 curElemIt!= curElemList.end();
8450 curElemIt++, nStartIt++ )
8452 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8453 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8454 // find one more free border
8455 if ( ! FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8459 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8460 // choice: clear a worse one
8461 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8462 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8463 contNodes[ iWorse ].clear();
8464 contFaces[ iWorse ].clear();
8467 if ( contNodes[0].empty() && contNodes[1].empty() )
8470 // append the best free border
8471 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8472 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8473 theNodes.pop_back(); // remove nIgnore
8474 theNodes.pop_back(); // remove nStart
8475 theFaces.pop_back(); // remove curElem
8476 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8477 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8478 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8479 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8482 } // several continuations found
8483 } // while ( nStart != theLastNode )
8488 //=======================================================================
8489 //function : CheckFreeBorderNodes
8490 //purpose : Return true if the tree nodes are on a free border
8491 //=======================================================================
8493 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8494 const SMDS_MeshNode* theNode2,
8495 const SMDS_MeshNode* theNode3)
8497 list< const SMDS_MeshNode* > nodes;
8498 list< const SMDS_MeshElement* > faces;
8499 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8502 //=======================================================================
8503 //function : SewFreeBorder
8505 //=======================================================================
8507 SMESH_MeshEditor::Sew_Error
8508 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8509 const SMDS_MeshNode* theBordSecondNode,
8510 const SMDS_MeshNode* theBordLastNode,
8511 const SMDS_MeshNode* theSideFirstNode,
8512 const SMDS_MeshNode* theSideSecondNode,
8513 const SMDS_MeshNode* theSideThirdNode,
8514 const bool theSideIsFreeBorder,
8515 const bool toCreatePolygons,
8516 const bool toCreatePolyedrs)
8518 myLastCreatedElems.Clear();
8519 myLastCreatedNodes.Clear();
8521 MESSAGE("::SewFreeBorder()");
8522 Sew_Error aResult = SEW_OK;
8524 // ====================================
8525 // find side nodes and elements
8526 // ====================================
8528 list< const SMDS_MeshNode* > nSide[ 2 ];
8529 list< const SMDS_MeshElement* > eSide[ 2 ];
8530 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8531 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8535 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8536 nSide[0], eSide[0])) {
8537 MESSAGE(" Free Border 1 not found " );
8538 aResult = SEW_BORDER1_NOT_FOUND;
8540 if (theSideIsFreeBorder) {
8543 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8544 nSide[1], eSide[1])) {
8545 MESSAGE(" Free Border 2 not found " );
8546 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8549 if ( aResult != SEW_OK )
8552 if (!theSideIsFreeBorder) {
8556 // -------------------------------------------------------------------------
8558 // 1. If nodes to merge are not coincident, move nodes of the free border
8559 // from the coord sys defined by the direction from the first to last
8560 // nodes of the border to the correspondent sys of the side 2
8561 // 2. On the side 2, find the links most co-directed with the correspondent
8562 // links of the free border
8563 // -------------------------------------------------------------------------
8565 // 1. Since sewing may break if there are volumes to split on the side 2,
8566 // we wont move nodes but just compute new coordinates for them
8567 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8568 TNodeXYZMap nBordXYZ;
8569 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8570 list< const SMDS_MeshNode* >::iterator nBordIt;
8572 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8573 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8574 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8575 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8576 double tol2 = 1.e-8;
8577 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8578 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8579 // Need node movement.
8581 // find X and Z axes to create trsf
8582 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8584 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8586 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8589 gp_Ax3 toBordAx( Pb1, Zb, X );
8590 gp_Ax3 fromSideAx( Ps1, Zs, X );
8591 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8593 gp_Trsf toBordSys, fromSide2Sys;
8594 toBordSys.SetTransformation( toBordAx );
8595 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8596 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8599 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8600 const SMDS_MeshNode* n = *nBordIt;
8601 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8602 toBordSys.Transforms( xyz );
8603 fromSide2Sys.Transforms( xyz );
8604 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8608 // just insert nodes XYZ in the nBordXYZ map
8609 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8610 const SMDS_MeshNode* n = *nBordIt;
8611 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8615 // 2. On the side 2, find the links most co-directed with the correspondent
8616 // links of the free border
8618 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8619 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8620 sideNodes.push_back( theSideFirstNode );
8622 bool hasVolumes = false;
8623 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8624 set<long> foundSideLinkIDs, checkedLinkIDs;
8625 SMDS_VolumeTool volume;
8626 //const SMDS_MeshNode* faceNodes[ 4 ];
8628 const SMDS_MeshNode* sideNode;
8629 const SMDS_MeshElement* sideElem;
8630 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8631 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8632 nBordIt = bordNodes.begin();
8634 // border node position and border link direction to compare with
8635 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8636 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8637 // choose next side node by link direction or by closeness to
8638 // the current border node:
8639 bool searchByDir = ( *nBordIt != theBordLastNode );
8641 // find the next node on the Side 2
8643 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8645 checkedLinkIDs.clear();
8646 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8648 // loop on inverse elements of current node (prevSideNode) on the Side 2
8649 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8650 while ( invElemIt->more() )
8652 const SMDS_MeshElement* elem = invElemIt->next();
8653 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8654 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8655 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8656 bool isVolume = volume.Set( elem );
8657 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8658 if ( isVolume ) // --volume
8660 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8661 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8662 if(elem->IsQuadratic()) {
8663 const SMDS_VtkFace* F =
8664 dynamic_cast<const SMDS_VtkFace*>(elem);
8665 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8666 // use special nodes iterator
8667 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8668 while( anIter->more() ) {
8669 nodes[ iNode ] = cast2Node(anIter->next());
8670 if ( nodes[ iNode++ ] == prevSideNode )
8671 iPrevNode = iNode - 1;
8675 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8676 while ( nIt->more() ) {
8677 nodes[ iNode ] = cast2Node( nIt->next() );
8678 if ( nodes[ iNode++ ] == prevSideNode )
8679 iPrevNode = iNode - 1;
8682 // there are 2 links to check
8687 // loop on links, to be precise, on the second node of links
8688 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8689 const SMDS_MeshNode* n = nodes[ iNode ];
8691 if ( !volume.IsLinked( n, prevSideNode ))
8695 if ( iNode ) // a node before prevSideNode
8696 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8697 else // a node after prevSideNode
8698 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8700 // check if this link was already used
8701 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8702 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8703 if (!isJustChecked &&
8704 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8706 // test a link geometrically
8707 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8708 bool linkIsBetter = false;
8709 double dot = 0.0, dist = 0.0;
8710 if ( searchByDir ) { // choose most co-directed link
8711 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8712 linkIsBetter = ( dot > maxDot );
8714 else { // choose link with the node closest to bordPos
8715 dist = ( nextXYZ - bordPos ).SquareModulus();
8716 linkIsBetter = ( dist < minDist );
8718 if ( linkIsBetter ) {
8727 } // loop on inverse elements of prevSideNode
8730 MESSAGE(" Cant find path by links of the Side 2 ");
8731 return SEW_BAD_SIDE_NODES;
8733 sideNodes.push_back( sideNode );
8734 sideElems.push_back( sideElem );
8735 foundSideLinkIDs.insert ( linkID );
8736 prevSideNode = sideNode;
8738 if ( *nBordIt == theBordLastNode )
8739 searchByDir = false;
8741 // find the next border link to compare with
8742 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8743 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8744 // move to next border node if sideNode is before forward border node (bordPos)
8745 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8746 prevBordNode = *nBordIt;
8748 bordPos = nBordXYZ[ *nBordIt ];
8749 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8750 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8754 while ( sideNode != theSideSecondNode );
8756 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8757 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8758 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8760 } // end nodes search on the side 2
8762 // ============================
8763 // sew the border to the side 2
8764 // ============================
8766 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8767 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8769 TListOfListOfNodes nodeGroupsToMerge;
8770 if ( nbNodes[0] == nbNodes[1] ||
8771 ( theSideIsFreeBorder && !theSideThirdNode)) {
8773 // all nodes are to be merged
8775 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8776 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8777 nIt[0]++, nIt[1]++ )
8779 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8780 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8781 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8786 // insert new nodes into the border and the side to get equal nb of segments
8788 // get normalized parameters of nodes on the borders
8789 //double param[ 2 ][ maxNbNodes ];
8791 param[0] = new double [ maxNbNodes ];
8792 param[1] = new double [ maxNbNodes ];
8794 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8795 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8796 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8797 const SMDS_MeshNode* nPrev = *nIt;
8798 double bordLength = 0;
8799 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8800 const SMDS_MeshNode* nCur = *nIt;
8801 gp_XYZ segment (nCur->X() - nPrev->X(),
8802 nCur->Y() - nPrev->Y(),
8803 nCur->Z() - nPrev->Z());
8804 double segmentLen = segment.Modulus();
8805 bordLength += segmentLen;
8806 param[ iBord ][ iNode ] = bordLength;
8809 // normalize within [0,1]
8810 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8811 param[ iBord ][ iNode ] /= bordLength;
8815 // loop on border segments
8816 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8817 int i[ 2 ] = { 0, 0 };
8818 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8819 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8821 TElemOfNodeListMap insertMap;
8822 TElemOfNodeListMap::iterator insertMapIt;
8824 // key: elem to insert nodes into
8825 // value: 2 nodes to insert between + nodes to be inserted
8827 bool next[ 2 ] = { false, false };
8829 // find min adjacent segment length after sewing
8830 double nextParam = 10., prevParam = 0;
8831 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8832 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8833 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8834 if ( i[ iBord ] > 0 )
8835 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8837 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8838 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8839 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8841 // choose to insert or to merge nodes
8842 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8843 if ( Abs( du ) <= minSegLen * 0.2 ) {
8846 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8847 const SMDS_MeshNode* n0 = *nIt[0];
8848 const SMDS_MeshNode* n1 = *nIt[1];
8849 nodeGroupsToMerge.back().push_back( n1 );
8850 nodeGroupsToMerge.back().push_back( n0 );
8851 // position of node of the border changes due to merge
8852 param[ 0 ][ i[0] ] += du;
8853 // move n1 for the sake of elem shape evaluation during insertion.
8854 // n1 will be removed by MergeNodes() anyway
8855 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8856 next[0] = next[1] = true;
8861 int intoBord = ( du < 0 ) ? 0 : 1;
8862 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8863 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8864 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8865 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8866 if ( intoBord == 1 ) {
8867 // move node of the border to be on a link of elem of the side
8868 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8869 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8870 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8871 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8872 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8874 insertMapIt = insertMap.find( elem );
8875 bool notFound = ( insertMapIt == insertMap.end() );
8876 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8878 // insert into another link of the same element:
8879 // 1. perform insertion into the other link of the elem
8880 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8881 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8882 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8883 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8884 // 2. perform insertion into the link of adjacent faces
8886 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8888 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8892 if (toCreatePolyedrs) {
8893 // perform insertion into the links of adjacent volumes
8894 UpdateVolumes(n12, n22, nodeList);
8896 // 3. find an element appeared on n1 and n2 after the insertion
8897 insertMap.erase( elem );
8898 elem = findAdjacentFace( n1, n2, 0 );
8900 if ( notFound || otherLink ) {
8901 // add element and nodes of the side into the insertMap
8902 insertMapIt = insertMap.insert
8903 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8904 (*insertMapIt).second.push_back( n1 );
8905 (*insertMapIt).second.push_back( n2 );
8907 // add node to be inserted into elem
8908 (*insertMapIt).second.push_back( nIns );
8909 next[ 1 - intoBord ] = true;
8912 // go to the next segment
8913 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8914 if ( next[ iBord ] ) {
8915 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8917 nPrev[ iBord ] = *nIt[ iBord ];
8918 nIt[ iBord ]++; i[ iBord ]++;
8922 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8924 // perform insertion of nodes into elements
8926 for (insertMapIt = insertMap.begin();
8927 insertMapIt != insertMap.end();
8930 const SMDS_MeshElement* elem = (*insertMapIt).first;
8931 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8932 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8933 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8935 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8937 if ( !theSideIsFreeBorder ) {
8938 // look for and insert nodes into the faces adjacent to elem
8940 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8942 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8947 if (toCreatePolyedrs) {
8948 // perform insertion into the links of adjacent volumes
8949 UpdateVolumes(n1, n2, nodeList);
8955 } // end: insert new nodes
8957 MergeNodes ( nodeGroupsToMerge );
8962 //=======================================================================
8963 //function : InsertNodesIntoLink
8964 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8965 // and theBetweenNode2 and split theElement
8966 //=======================================================================
8968 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8969 const SMDS_MeshNode* theBetweenNode1,
8970 const SMDS_MeshNode* theBetweenNode2,
8971 list<const SMDS_MeshNode*>& theNodesToInsert,
8972 const bool toCreatePoly)
8974 if ( theFace->GetType() != SMDSAbs_Face ) return;
8976 // find indices of 2 link nodes and of the rest nodes
8977 int iNode = 0, il1, il2, i3, i4;
8978 il1 = il2 = i3 = i4 = -1;
8979 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8980 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8982 if(theFace->IsQuadratic()) {
8983 const SMDS_VtkFace* F =
8984 dynamic_cast<const SMDS_VtkFace*>(theFace);
8985 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8986 // use special nodes iterator
8987 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8988 while( anIter->more() ) {
8989 const SMDS_MeshNode* n = cast2Node(anIter->next());
8990 if ( n == theBetweenNode1 )
8992 else if ( n == theBetweenNode2 )
8998 nodes[ iNode++ ] = n;
9002 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9003 while ( nodeIt->more() ) {
9004 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9005 if ( n == theBetweenNode1 )
9007 else if ( n == theBetweenNode2 )
9013 nodes[ iNode++ ] = n;
9016 if ( il1 < 0 || il2 < 0 || i3 < 0 )
9019 // arrange link nodes to go one after another regarding the face orientation
9020 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
9021 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
9026 aNodesToInsert.reverse();
9028 // check that not link nodes of a quadrangles are in good order
9029 int nbFaceNodes = theFace->NbNodes();
9030 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
9036 if (toCreatePoly || theFace->IsPoly()) {
9039 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
9041 // add nodes of face up to first node of link
9044 if(theFace->IsQuadratic()) {
9045 const SMDS_VtkFace* F =
9046 dynamic_cast<const SMDS_VtkFace*>(theFace);
9047 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9048 // use special nodes iterator
9049 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
9050 while( anIter->more() && !isFLN ) {
9051 const SMDS_MeshNode* n = cast2Node(anIter->next());
9052 poly_nodes[iNode++] = n;
9053 if (n == nodes[il1]) {
9057 // add nodes to insert
9058 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9059 for (; nIt != aNodesToInsert.end(); nIt++) {
9060 poly_nodes[iNode++] = *nIt;
9062 // add nodes of face starting from last node of link
9063 while ( anIter->more() ) {
9064 poly_nodes[iNode++] = cast2Node(anIter->next());
9068 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
9069 while ( nodeIt->more() && !isFLN ) {
9070 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9071 poly_nodes[iNode++] = n;
9072 if (n == nodes[il1]) {
9076 // add nodes to insert
9077 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9078 for (; nIt != aNodesToInsert.end(); nIt++) {
9079 poly_nodes[iNode++] = *nIt;
9081 // add nodes of face starting from last node of link
9082 while ( nodeIt->more() ) {
9083 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9084 poly_nodes[iNode++] = n;
9088 // edit or replace the face
9089 SMESHDS_Mesh *aMesh = GetMeshDS();
9091 if (theFace->IsPoly()) {
9092 aMesh->ChangePolygonNodes(theFace, poly_nodes);
9095 int aShapeId = FindShape( theFace );
9097 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
9098 myLastCreatedElems.Append(newElem);
9099 if ( aShapeId && newElem )
9100 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9102 aMesh->RemoveElement(theFace);
9107 SMESHDS_Mesh *aMesh = GetMeshDS();
9108 if( !theFace->IsQuadratic() ) {
9110 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
9111 int nbLinkNodes = 2 + aNodesToInsert.size();
9112 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
9113 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
9114 linkNodes[ 0 ] = nodes[ il1 ];
9115 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
9116 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9117 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9118 linkNodes[ iNode++ ] = *nIt;
9120 // decide how to split a quadrangle: compare possible variants
9121 // and choose which of splits to be a quadrangle
9122 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
9123 if ( nbFaceNodes == 3 ) {
9124 iBestQuad = nbSplits;
9127 else if ( nbFaceNodes == 4 ) {
9128 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
9129 double aBestRate = DBL_MAX;
9130 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
9132 double aBadRate = 0;
9133 // evaluate elements quality
9134 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
9135 if ( iSplit == iQuad ) {
9136 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
9140 aBadRate += getBadRate( &quad, aCrit );
9143 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
9145 nodes[ iSplit < iQuad ? i4 : i3 ]);
9146 aBadRate += getBadRate( &tria, aCrit );
9150 if ( aBadRate < aBestRate ) {
9152 aBestRate = aBadRate;
9157 // create new elements
9158 int aShapeId = FindShape( theFace );
9161 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9162 SMDS_MeshElement* newElem = 0;
9163 if ( iSplit == iBestQuad )
9164 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9169 newElem = aMesh->AddFace (linkNodes[ i1++ ],
9171 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
9172 myLastCreatedElems.Append(newElem);
9173 if ( aShapeId && newElem )
9174 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9177 // change nodes of theFace
9178 const SMDS_MeshNode* newNodes[ 4 ];
9179 newNodes[ 0 ] = linkNodes[ i1 ];
9180 newNodes[ 1 ] = linkNodes[ i2 ];
9181 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9182 newNodes[ 3 ] = nodes[ i4 ];
9183 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
9184 const SMDS_MeshElement* newElem = 0;
9185 if (iSplit == iBestQuad)
9186 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
9188 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
9189 myLastCreatedElems.Append(newElem);
9190 if ( aShapeId && newElem )
9191 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9192 } // end if(!theFace->IsQuadratic())
9193 else { // theFace is quadratic
9194 // we have to split theFace on simple triangles and one simple quadrangle
9196 int nbshift = tmp*2;
9197 // shift nodes in nodes[] by nbshift
9199 for(i=0; i<nbshift; i++) {
9200 const SMDS_MeshNode* n = nodes[0];
9201 for(j=0; j<nbFaceNodes-1; j++) {
9202 nodes[j] = nodes[j+1];
9204 nodes[nbFaceNodes-1] = n;
9206 il1 = il1 - nbshift;
9207 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9208 // n0 n1 n2 n0 n1 n2
9209 // +-----+-----+ +-----+-----+
9218 // create new elements
9219 int aShapeId = FindShape( theFace );
9222 if(nbFaceNodes==6) { // quadratic triangle
9223 SMDS_MeshElement* newElem =
9224 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9225 myLastCreatedElems.Append(newElem);
9226 if ( aShapeId && newElem )
9227 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9228 if(theFace->IsMediumNode(nodes[il1])) {
9229 // create quadrangle
9230 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
9231 myLastCreatedElems.Append(newElem);
9232 if ( aShapeId && newElem )
9233 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9239 // create quadrangle
9240 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
9241 myLastCreatedElems.Append(newElem);
9242 if ( aShapeId && newElem )
9243 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9249 else { // nbFaceNodes==8 - quadratic quadrangle
9250 SMDS_MeshElement* newElem =
9251 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9252 myLastCreatedElems.Append(newElem);
9253 if ( aShapeId && newElem )
9254 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9255 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9256 myLastCreatedElems.Append(newElem);
9257 if ( aShapeId && newElem )
9258 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9259 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9260 myLastCreatedElems.Append(newElem);
9261 if ( aShapeId && newElem )
9262 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9263 if(theFace->IsMediumNode(nodes[il1])) {
9264 // create quadrangle
9265 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9266 myLastCreatedElems.Append(newElem);
9267 if ( aShapeId && newElem )
9268 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9274 // create quadrangle
9275 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9276 myLastCreatedElems.Append(newElem);
9277 if ( aShapeId && newElem )
9278 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9284 // create needed triangles using n1,n2,n3 and inserted nodes
9285 int nbn = 2 + aNodesToInsert.size();
9286 //const SMDS_MeshNode* aNodes[nbn];
9287 vector<const SMDS_MeshNode*> aNodes(nbn);
9288 aNodes[0] = nodes[n1];
9289 aNodes[nbn-1] = nodes[n2];
9290 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9291 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9292 aNodes[iNode++] = *nIt;
9294 for(i=1; i<nbn; i++) {
9295 SMDS_MeshElement* newElem =
9296 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9297 myLastCreatedElems.Append(newElem);
9298 if ( aShapeId && newElem )
9299 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9303 aMesh->RemoveElement(theFace);
9306 //=======================================================================
9307 //function : UpdateVolumes
9309 //=======================================================================
9310 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9311 const SMDS_MeshNode* theBetweenNode2,
9312 list<const SMDS_MeshNode*>& theNodesToInsert)
9314 myLastCreatedElems.Clear();
9315 myLastCreatedNodes.Clear();
9317 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9318 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9319 const SMDS_MeshElement* elem = invElemIt->next();
9321 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9322 SMDS_VolumeTool aVolume (elem);
9323 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9326 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9327 int iface, nbFaces = aVolume.NbFaces();
9328 vector<const SMDS_MeshNode *> poly_nodes;
9329 vector<int> quantities (nbFaces);
9331 for (iface = 0; iface < nbFaces; iface++) {
9332 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9333 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9334 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9336 for (int inode = 0; inode < nbFaceNodes; inode++) {
9337 poly_nodes.push_back(faceNodes[inode]);
9339 if (nbInserted == 0) {
9340 if (faceNodes[inode] == theBetweenNode1) {
9341 if (faceNodes[inode + 1] == theBetweenNode2) {
9342 nbInserted = theNodesToInsert.size();
9344 // add nodes to insert
9345 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9346 for (; nIt != theNodesToInsert.end(); nIt++) {
9347 poly_nodes.push_back(*nIt);
9351 else if (faceNodes[inode] == theBetweenNode2) {
9352 if (faceNodes[inode + 1] == theBetweenNode1) {
9353 nbInserted = theNodesToInsert.size();
9355 // add nodes to insert in reversed order
9356 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9358 for (; nIt != theNodesToInsert.begin(); nIt--) {
9359 poly_nodes.push_back(*nIt);
9361 poly_nodes.push_back(*nIt);
9368 quantities[iface] = nbFaceNodes + nbInserted;
9371 // Replace or update the volume
9372 SMESHDS_Mesh *aMesh = GetMeshDS();
9374 if (elem->IsPoly()) {
9375 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9379 int aShapeId = FindShape( elem );
9381 SMDS_MeshElement* newElem =
9382 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9383 myLastCreatedElems.Append(newElem);
9384 if (aShapeId && newElem)
9385 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9387 aMesh->RemoveElement(elem);
9394 //================================================================================
9396 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9398 //================================================================================
9400 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9401 vector<const SMDS_MeshNode *> & nodes,
9402 vector<int> & nbNodeInFaces )
9405 nbNodeInFaces.clear();
9406 SMDS_VolumeTool vTool ( elem );
9407 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9409 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9410 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9411 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9416 //=======================================================================
9418 * \brief Convert elements contained in a submesh to quadratic
9419 * \return int - nb of checked elements
9421 //=======================================================================
9423 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9424 SMESH_MesherHelper& theHelper,
9425 const bool theForce3d)
9428 if( !theSm ) return nbElem;
9430 vector<int> nbNodeInFaces;
9431 vector<const SMDS_MeshNode *> nodes;
9432 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9433 while(ElemItr->more())
9436 const SMDS_MeshElement* elem = ElemItr->next();
9437 if( !elem || elem->IsQuadratic() ) continue;
9439 // get elem data needed to re-create it
9441 const int id = elem->GetID();
9442 const int nbNodes = elem->NbNodes();
9443 const SMDSAbs_ElementType aType = elem->GetType();
9444 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9445 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9446 if ( aGeomType == SMDSEntity_Polyhedra )
9447 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9448 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9449 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9451 // remove a linear element
9452 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9454 const SMDS_MeshElement* NewElem = 0;
9460 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9468 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9471 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9474 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9479 case SMDSAbs_Volume :
9483 case SMDSEntity_Tetra:
9484 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9486 case SMDSEntity_Pyramid:
9487 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9489 case SMDSEntity_Penta:
9490 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9492 case SMDSEntity_Hexa:
9493 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9494 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9496 case SMDSEntity_Hexagonal_Prism:
9498 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9505 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9507 theSm->AddElement( NewElem );
9512 //=======================================================================
9513 //function : ConvertToQuadratic
9515 //=======================================================================
9517 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d)
9519 SMESHDS_Mesh* meshDS = GetMeshDS();
9521 SMESH_MesherHelper aHelper(*myMesh);
9522 aHelper.SetIsQuadratic( true );
9524 int nbCheckedElems = 0;
9525 if ( myMesh->HasShapeToMesh() )
9527 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9529 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9530 while ( smIt->more() ) {
9531 SMESH_subMesh* sm = smIt->next();
9532 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9533 aHelper.SetSubShape( sm->GetSubShape() );
9534 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9539 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9540 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9542 SMESHDS_SubMesh *smDS = 0;
9543 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9544 while(aEdgeItr->more())
9546 const SMDS_MeshEdge* edge = aEdgeItr->next();
9547 if(edge && !edge->IsQuadratic())
9549 int id = edge->GetID();
9550 //MESSAGE("edge->GetID() " << id);
9551 const SMDS_MeshNode* n1 = edge->GetNode(0);
9552 const SMDS_MeshNode* n2 = edge->GetNode(1);
9554 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9556 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9557 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9560 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9561 while(aFaceItr->more())
9563 const SMDS_MeshFace* face = aFaceItr->next();
9564 if(!face || face->IsQuadratic() ) continue;
9566 const int id = face->GetID();
9567 const SMDSAbs_EntityType type = face->GetEntityType();
9568 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9570 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9572 SMDS_MeshFace * NewFace = 0;
9575 case SMDSEntity_Triangle:
9576 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9578 case SMDSEntity_Quadrangle:
9579 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9582 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9584 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9586 vector<int> nbNodeInFaces;
9587 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9588 while(aVolumeItr->more())
9590 const SMDS_MeshVolume* volume = aVolumeItr->next();
9591 if(!volume || volume->IsQuadratic() ) continue;
9593 const int id = volume->GetID();
9594 const SMDSAbs_EntityType type = volume->GetEntityType();
9595 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9596 if ( type == SMDSEntity_Polyhedra )
9597 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9598 else if ( type == SMDSEntity_Hexagonal_Prism )
9599 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9601 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9603 SMDS_MeshVolume * NewVolume = 0;
9606 case SMDSEntity_Tetra:
9607 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9609 case SMDSEntity_Hexa:
9610 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9611 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9613 case SMDSEntity_Pyramid:
9614 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9615 nodes[3], nodes[4], id, theForce3d);
9617 case SMDSEntity_Penta:
9618 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9619 nodes[3], nodes[4], nodes[5], id, theForce3d);
9621 case SMDSEntity_Hexagonal_Prism:
9623 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9625 ReplaceElemInGroups(volume, NewVolume, meshDS);
9630 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9631 aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9632 aHelper.FixQuadraticElements();
9636 //================================================================================
9638 * \brief Makes given elements quadratic
9639 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9640 * \param theElements - elements to make quadratic
9642 //================================================================================
9644 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9645 TIDSortedElemSet& theElements)
9647 if ( theElements.empty() ) return;
9649 // we believe that all theElements are of the same type
9650 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9652 // get all nodes shared by theElements
9653 TIDSortedNodeSet allNodes;
9654 TIDSortedElemSet::iterator eIt = theElements.begin();
9655 for ( ; eIt != theElements.end(); ++eIt )
9656 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9658 // complete theElements with elements of lower dim whose all nodes are in allNodes
9660 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9661 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9662 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9663 for ( ; nIt != allNodes.end(); ++nIt )
9665 const SMDS_MeshNode* n = *nIt;
9666 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9667 while ( invIt->more() )
9669 const SMDS_MeshElement* e = invIt->next();
9670 if ( e->IsQuadratic() )
9672 quadAdjacentElems[ e->GetType() ].insert( e );
9675 if ( e->GetType() >= elemType )
9677 continue; // same type of more complex linear element
9680 if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second )
9681 continue; // e is already checked
9685 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
9686 while ( nodeIt->more() && allIn )
9687 allIn = allNodes.count( cast2Node( nodeIt->next() ));
9689 theElements.insert(e );
9693 SMESH_MesherHelper helper(*myMesh);
9694 helper.SetIsQuadratic( true );
9696 // add links of quadratic adjacent elements to the helper
9698 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9699 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9700 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9702 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9704 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9705 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9706 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9708 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9710 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9711 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9712 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9714 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9717 // make quadratic elements instead of linear ones
9719 SMESHDS_Mesh* meshDS = GetMeshDS();
9720 SMESHDS_SubMesh* smDS = 0;
9721 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9723 const SMDS_MeshElement* elem = *eIt;
9724 if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() )
9727 const int id = elem->GetID();
9728 const SMDSAbs_ElementType type = elem->GetType();
9729 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9731 if ( !smDS || !smDS->Contains( elem ))
9732 smDS = meshDS->MeshElements( elem->getshapeId() );
9733 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9735 SMDS_MeshElement * newElem = 0;
9736 switch( nodes.size() )
9738 case 4: // cases for most frequently used element types go first (for optimization)
9739 if ( type == SMDSAbs_Volume )
9740 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9742 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9745 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9746 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9749 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9752 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9755 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9756 nodes[4], id, theForce3d);
9759 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9760 nodes[4], nodes[5], id, theForce3d);
9764 ReplaceElemInGroups( elem, newElem, meshDS);
9765 if( newElem && smDS )
9766 smDS->AddElement( newElem );
9769 if ( !theForce3d && !getenv("NO_FixQuadraticElements"))
9770 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9771 helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9772 helper.FixQuadraticElements();
9776 //=======================================================================
9778 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9779 * \return int - nb of checked elements
9781 //=======================================================================
9783 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9784 SMDS_ElemIteratorPtr theItr,
9785 const int theShapeID)
9788 SMESHDS_Mesh* meshDS = GetMeshDS();
9790 while( theItr->more() )
9792 const SMDS_MeshElement* elem = theItr->next();
9794 if( elem && elem->IsQuadratic())
9796 int id = elem->GetID();
9797 int nbCornerNodes = elem->NbCornerNodes();
9798 SMDSAbs_ElementType aType = elem->GetType();
9800 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9802 //remove a quadratic element
9803 if ( !theSm || !theSm->Contains( elem ))
9804 theSm = meshDS->MeshElements( elem->getshapeId() );
9805 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9807 // remove medium nodes
9808 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9809 if ( nodes[i]->NbInverseElements() == 0 )
9810 meshDS->RemoveFreeNode( nodes[i], theSm );
9812 // add a linear element
9813 nodes.resize( nbCornerNodes );
9814 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9815 ReplaceElemInGroups(elem, newElem, meshDS);
9816 if( theSm && newElem )
9817 theSm->AddElement( newElem );
9823 //=======================================================================
9824 //function : ConvertFromQuadratic
9826 //=======================================================================
9828 bool SMESH_MeshEditor::ConvertFromQuadratic()
9830 int nbCheckedElems = 0;
9831 if ( myMesh->HasShapeToMesh() )
9833 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9835 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9836 while ( smIt->more() ) {
9837 SMESH_subMesh* sm = smIt->next();
9838 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9839 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9845 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9846 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9848 SMESHDS_SubMesh *aSM = 0;
9849 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9857 //================================================================================
9859 * \brief Return true if all medium nodes of the element are in the node set
9861 //================================================================================
9863 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9865 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9866 if ( !nodeSet.count( elem->GetNode(i) ))
9872 //================================================================================
9874 * \brief Makes given elements linear
9876 //================================================================================
9878 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9880 if ( theElements.empty() ) return;
9882 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9883 set<int> mediumNodeIDs;
9884 TIDSortedElemSet::iterator eIt = theElements.begin();
9885 for ( ; eIt != theElements.end(); ++eIt )
9887 const SMDS_MeshElement* e = *eIt;
9888 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9889 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9892 // replace given elements by linear ones
9893 typedef SMDS_SetIterator<const SMDS_MeshElement*, TIDSortedElemSet::iterator> TSetIterator;
9894 SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() ));
9895 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9897 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9898 // except those elements sharing medium nodes of quadratic element whose medium nodes
9899 // are not all in mediumNodeIDs
9901 // get remaining medium nodes
9902 TIDSortedNodeSet mediumNodes;
9903 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9904 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9905 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9906 mediumNodes.insert( mediumNodes.end(), n );
9908 // find more quadratic elements to convert
9909 TIDSortedElemSet moreElemsToConvert;
9910 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9911 for ( ; nIt != mediumNodes.end(); ++nIt )
9913 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9914 while ( invIt->more() )
9916 const SMDS_MeshElement* e = invIt->next();
9917 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9919 // find a more complex element including e and
9920 // whose medium nodes are not in mediumNodes
9921 bool complexFound = false;
9922 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9924 SMDS_ElemIteratorPtr invIt2 =
9925 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9926 while ( invIt2->more() )
9928 const SMDS_MeshElement* eComplex = invIt2->next();
9929 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9931 int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size();
9932 if ( nbCommonNodes == e->NbNodes())
9934 complexFound = true;
9935 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9941 if ( !complexFound )
9942 moreElemsToConvert.insert( e );
9946 elemIt = SMDS_ElemIteratorPtr
9947 (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() ));
9948 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9951 //=======================================================================
9952 //function : SewSideElements
9954 //=======================================================================
9956 SMESH_MeshEditor::Sew_Error
9957 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9958 TIDSortedElemSet& theSide2,
9959 const SMDS_MeshNode* theFirstNode1,
9960 const SMDS_MeshNode* theFirstNode2,
9961 const SMDS_MeshNode* theSecondNode1,
9962 const SMDS_MeshNode* theSecondNode2)
9964 myLastCreatedElems.Clear();
9965 myLastCreatedNodes.Clear();
9967 MESSAGE ("::::SewSideElements()");
9968 if ( theSide1.size() != theSide2.size() )
9969 return SEW_DIFF_NB_OF_ELEMENTS;
9971 Sew_Error aResult = SEW_OK;
9973 // 1. Build set of faces representing each side
9974 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9975 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9977 // =======================================================================
9978 // 1. Build set of faces representing each side:
9979 // =======================================================================
9980 // a. build set of nodes belonging to faces
9981 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9982 // c. create temporary faces representing side of volumes if correspondent
9983 // face does not exist
9985 SMESHDS_Mesh* aMesh = GetMeshDS();
9986 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9987 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9988 TIDSortedElemSet faceSet1, faceSet2;
9989 set<const SMDS_MeshElement*> volSet1, volSet2;
9990 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9991 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9992 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9993 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9994 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9995 int iSide, iFace, iNode;
9997 list<const SMDS_MeshElement* > tempFaceList;
9998 for ( iSide = 0; iSide < 2; iSide++ ) {
9999 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
10000 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
10001 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
10002 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
10003 set<const SMDS_MeshElement*>::iterator vIt;
10004 TIDSortedElemSet::iterator eIt;
10005 set<const SMDS_MeshNode*>::iterator nIt;
10007 // check that given nodes belong to given elements
10008 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
10009 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
10010 int firstIndex = -1, secondIndex = -1;
10011 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10012 const SMDS_MeshElement* elem = *eIt;
10013 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
10014 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
10015 if ( firstIndex > -1 && secondIndex > -1 ) break;
10017 if ( firstIndex < 0 || secondIndex < 0 ) {
10018 // we can simply return until temporary faces created
10019 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
10022 // -----------------------------------------------------------
10023 // 1a. Collect nodes of existing faces
10024 // and build set of face nodes in order to detect missing
10025 // faces corresponding to sides of volumes
10026 // -----------------------------------------------------------
10028 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10030 // loop on the given element of a side
10031 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10032 //const SMDS_MeshElement* elem = *eIt;
10033 const SMDS_MeshElement* elem = *eIt;
10034 if ( elem->GetType() == SMDSAbs_Face ) {
10035 faceSet->insert( elem );
10036 set <const SMDS_MeshNode*> faceNodeSet;
10037 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10038 while ( nodeIt->more() ) {
10039 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10040 nodeSet->insert( n );
10041 faceNodeSet.insert( n );
10043 setOfFaceNodeSet.insert( faceNodeSet );
10045 else if ( elem->GetType() == SMDSAbs_Volume )
10046 volSet->insert( elem );
10048 // ------------------------------------------------------------------------------
10049 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10050 // ------------------------------------------------------------------------------
10052 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10053 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10054 while ( fIt->more() ) { // loop on faces sharing a node
10055 const SMDS_MeshElement* f = fIt->next();
10056 if ( faceSet->find( f ) == faceSet->end() ) {
10057 // check if all nodes are in nodeSet and
10058 // complete setOfFaceNodeSet if they are
10059 set <const SMDS_MeshNode*> faceNodeSet;
10060 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10061 bool allInSet = true;
10062 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10063 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10064 if ( nodeSet->find( n ) == nodeSet->end() )
10067 faceNodeSet.insert( n );
10070 faceSet->insert( f );
10071 setOfFaceNodeSet.insert( faceNodeSet );
10077 // -------------------------------------------------------------------------
10078 // 1c. Create temporary faces representing sides of volumes if correspondent
10079 // face does not exist
10080 // -------------------------------------------------------------------------
10082 if ( !volSet->empty() ) {
10083 //int nodeSetSize = nodeSet->size();
10085 // loop on given volumes
10086 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10087 SMDS_VolumeTool vol (*vIt);
10088 // loop on volume faces: find free faces
10089 // --------------------------------------
10090 list<const SMDS_MeshElement* > freeFaceList;
10091 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10092 if ( !vol.IsFreeFace( iFace ))
10094 // check if there is already a face with same nodes in a face set
10095 const SMDS_MeshElement* aFreeFace = 0;
10096 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10097 int nbNodes = vol.NbFaceNodes( iFace );
10098 set <const SMDS_MeshNode*> faceNodeSet;
10099 vol.GetFaceNodes( iFace, faceNodeSet );
10100 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10102 // no such a face is given but it still can exist, check it
10103 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10104 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10106 if ( !aFreeFace ) {
10107 // create a temporary face
10108 if ( nbNodes == 3 ) {
10109 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10110 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10112 else if ( nbNodes == 4 ) {
10113 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10114 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10117 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10118 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10119 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10122 tempFaceList.push_back( aFreeFace );
10126 freeFaceList.push_back( aFreeFace );
10128 } // loop on faces of a volume
10130 // choose one of several free faces of a volume
10131 // --------------------------------------------
10132 if ( freeFaceList.size() > 1 ) {
10133 // choose a face having max nb of nodes shared by other elems of a side
10134 int maxNbNodes = -1;
10135 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10136 while ( fIt != freeFaceList.end() ) { // loop on free faces
10137 int nbSharedNodes = 0;
10138 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10139 while ( nodeIt->more() ) { // loop on free face nodes
10140 const SMDS_MeshNode* n =
10141 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10142 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10143 while ( invElemIt->more() ) {
10144 const SMDS_MeshElement* e = invElemIt->next();
10145 nbSharedNodes += faceSet->count( e );
10146 nbSharedNodes += elemSet->count( e );
10149 if ( nbSharedNodes > maxNbNodes ) {
10150 maxNbNodes = nbSharedNodes;
10151 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10153 else if ( nbSharedNodes == maxNbNodes ) {
10157 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10160 if ( freeFaceList.size() > 1 )
10162 // could not choose one face, use another way
10163 // choose a face most close to the bary center of the opposite side
10164 gp_XYZ aBC( 0., 0., 0. );
10165 set <const SMDS_MeshNode*> addedNodes;
10166 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10167 eIt = elemSet2->begin();
10168 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10169 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10170 while ( nodeIt->more() ) { // loop on free face nodes
10171 const SMDS_MeshNode* n =
10172 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10173 if ( addedNodes.insert( n ).second )
10174 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10177 aBC /= addedNodes.size();
10178 double minDist = DBL_MAX;
10179 fIt = freeFaceList.begin();
10180 while ( fIt != freeFaceList.end() ) { // loop on free faces
10182 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10183 while ( nodeIt->more() ) { // loop on free face nodes
10184 const SMDS_MeshNode* n =
10185 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10186 gp_XYZ p( n->X(),n->Y(),n->Z() );
10187 dist += ( aBC - p ).SquareModulus();
10189 if ( dist < minDist ) {
10191 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10194 fIt = freeFaceList.erase( fIt++ );
10197 } // choose one of several free faces of a volume
10199 if ( freeFaceList.size() == 1 ) {
10200 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10201 faceSet->insert( aFreeFace );
10202 // complete a node set with nodes of a found free face
10203 // for ( iNode = 0; iNode < ; iNode++ )
10204 // nodeSet->insert( fNodes[ iNode ] );
10207 } // loop on volumes of a side
10209 // // complete a set of faces if new nodes in a nodeSet appeared
10210 // // ----------------------------------------------------------
10211 // if ( nodeSetSize != nodeSet->size() ) {
10212 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10213 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10214 // while ( fIt->more() ) { // loop on faces sharing a node
10215 // const SMDS_MeshElement* f = fIt->next();
10216 // if ( faceSet->find( f ) == faceSet->end() ) {
10217 // // check if all nodes are in nodeSet and
10218 // // complete setOfFaceNodeSet if they are
10219 // set <const SMDS_MeshNode*> faceNodeSet;
10220 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10221 // bool allInSet = true;
10222 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10223 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10224 // if ( nodeSet->find( n ) == nodeSet->end() )
10225 // allInSet = false;
10227 // faceNodeSet.insert( n );
10229 // if ( allInSet ) {
10230 // faceSet->insert( f );
10231 // setOfFaceNodeSet.insert( faceNodeSet );
10237 } // Create temporary faces, if there are volumes given
10240 if ( faceSet1.size() != faceSet2.size() ) {
10241 // delete temporary faces: they are in reverseElements of actual nodes
10242 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10243 // while ( tmpFaceIt->more() )
10244 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10245 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10246 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10247 // aMesh->RemoveElement(*tmpFaceIt);
10248 MESSAGE("Diff nb of faces");
10249 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10252 // ============================================================
10253 // 2. Find nodes to merge:
10254 // bind a node to remove to a node to put instead
10255 // ============================================================
10257 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10258 if ( theFirstNode1 != theFirstNode2 )
10259 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10260 if ( theSecondNode1 != theSecondNode2 )
10261 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10263 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10264 set< long > linkIdSet; // links to process
10265 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10267 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10268 list< NLink > linkList[2];
10269 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10270 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10271 // loop on links in linkList; find faces by links and append links
10272 // of the found faces to linkList
10273 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10274 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10276 NLink link[] = { *linkIt[0], *linkIt[1] };
10277 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10278 if ( !linkIdSet.count( linkID ) )
10281 // by links, find faces in the face sets,
10282 // and find indices of link nodes in the found faces;
10283 // in a face set, there is only one or no face sharing a link
10284 // ---------------------------------------------------------------
10286 const SMDS_MeshElement* face[] = { 0, 0 };
10287 vector<const SMDS_MeshNode*> fnodes[2];
10288 int iLinkNode[2][2];
10289 TIDSortedElemSet avoidSet;
10290 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10291 const SMDS_MeshNode* n1 = link[iSide].first;
10292 const SMDS_MeshNode* n2 = link[iSide].second;
10293 //cout << "Side " << iSide << " ";
10294 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10295 // find a face by two link nodes
10296 face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet,
10297 &iLinkNode[iSide][0], &iLinkNode[iSide][1] );
10298 if ( face[ iSide ])
10300 //cout << " F " << face[ iSide]->GetID() <<endl;
10301 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10302 // put face nodes to fnodes
10303 if ( face[ iSide ]->IsQuadratic() )
10305 // use interlaced nodes iterator
10306 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10307 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10308 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10309 while ( nIter->more() )
10310 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10314 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10315 face[ iSide ]->end_nodes() );
10317 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10321 // check similarity of elements of the sides
10322 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10323 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10324 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10325 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10328 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10330 break; // do not return because it's necessary to remove tmp faces
10333 // set nodes to merge
10334 // -------------------
10336 if ( face[0] && face[1] ) {
10337 const int nbNodes = face[0]->NbNodes();
10338 if ( nbNodes != face[1]->NbNodes() ) {
10339 MESSAGE("Diff nb of face nodes");
10340 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10341 break; // do not return because it s necessary to remove tmp faces
10343 bool reverse[] = { false, false }; // order of nodes in the link
10344 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10345 // analyse link orientation in faces
10346 int i1 = iLinkNode[ iSide ][ 0 ];
10347 int i2 = iLinkNode[ iSide ][ 1 ];
10348 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10350 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10351 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10352 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10354 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10355 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10358 // add other links of the faces to linkList
10359 // -----------------------------------------
10361 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10362 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10363 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10364 if ( !iter_isnew.second ) { // already in a set: no need to process
10365 linkIdSet.erase( iter_isnew.first );
10367 else // new in set == encountered for the first time: add
10369 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10370 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10371 linkList[0].push_back ( NLink( n1, n2 ));
10372 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10377 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10380 } // loop on link lists
10382 if ( aResult == SEW_OK &&
10383 ( //linkIt[0] != linkList[0].end() ||
10384 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10385 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10386 " " << (faceSetPtr[1]->empty()));
10387 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10390 // ====================================================================
10391 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10392 // ====================================================================
10394 // delete temporary faces
10395 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10396 // while ( tmpFaceIt->more() )
10397 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10398 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10399 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10400 aMesh->RemoveElement(*tmpFaceIt);
10402 if ( aResult != SEW_OK)
10405 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10406 // loop on nodes replacement map
10407 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10408 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10409 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10410 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10411 nodeIDsToRemove.push_back( nToRemove->GetID() );
10412 // loop on elements sharing nToRemove
10413 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10414 while ( invElemIt->more() ) {
10415 const SMDS_MeshElement* e = invElemIt->next();
10416 // get a new suite of nodes: make replacement
10417 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10418 vector< const SMDS_MeshNode*> nodes( nbNodes );
10419 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10420 while ( nIt->more() ) {
10421 const SMDS_MeshNode* n =
10422 static_cast<const SMDS_MeshNode*>( nIt->next() );
10423 nnIt = nReplaceMap.find( n );
10424 if ( nnIt != nReplaceMap.end() ) {
10426 n = (*nnIt).second;
10430 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10431 // elemIDsToRemove.push_back( e->GetID() );
10435 SMDSAbs_ElementType etyp = e->GetType();
10436 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10439 myLastCreatedElems.Append(newElem);
10440 AddToSameGroups(newElem, e, aMesh);
10441 int aShapeId = e->getshapeId();
10444 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10447 aMesh->RemoveElement(e);
10452 Remove( nodeIDsToRemove, true );
10457 //================================================================================
10459 * \brief Find corresponding nodes in two sets of faces
10460 * \param theSide1 - first face set
10461 * \param theSide2 - second first face
10462 * \param theFirstNode1 - a boundary node of set 1
10463 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10464 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10465 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10466 * \param nReplaceMap - output map of corresponding nodes
10467 * \return bool - is a success or not
10469 //================================================================================
10472 //#define DEBUG_MATCHING_NODES
10475 SMESH_MeshEditor::Sew_Error
10476 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10477 set<const SMDS_MeshElement*>& theSide2,
10478 const SMDS_MeshNode* theFirstNode1,
10479 const SMDS_MeshNode* theFirstNode2,
10480 const SMDS_MeshNode* theSecondNode1,
10481 const SMDS_MeshNode* theSecondNode2,
10482 TNodeNodeMap & nReplaceMap)
10484 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10486 nReplaceMap.clear();
10487 if ( theFirstNode1 != theFirstNode2 )
10488 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10489 if ( theSecondNode1 != theSecondNode2 )
10490 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10492 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10493 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10495 list< NLink > linkList[2];
10496 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10497 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10499 // loop on links in linkList; find faces by links and append links
10500 // of the found faces to linkList
10501 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10502 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10503 NLink link[] = { *linkIt[0], *linkIt[1] };
10504 if ( linkSet.find( link[0] ) == linkSet.end() )
10507 // by links, find faces in the face sets,
10508 // and find indices of link nodes in the found faces;
10509 // in a face set, there is only one or no face sharing a link
10510 // ---------------------------------------------------------------
10512 const SMDS_MeshElement* face[] = { 0, 0 };
10513 list<const SMDS_MeshNode*> notLinkNodes[2];
10514 //bool reverse[] = { false, false }; // order of notLinkNodes
10516 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10518 const SMDS_MeshNode* n1 = link[iSide].first;
10519 const SMDS_MeshNode* n2 = link[iSide].second;
10520 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10521 set< const SMDS_MeshElement* > facesOfNode1;
10522 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10524 // during a loop of the first node, we find all faces around n1,
10525 // during a loop of the second node, we find one face sharing both n1 and n2
10526 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10527 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10528 while ( fIt->more() ) { // loop on faces sharing a node
10529 const SMDS_MeshElement* f = fIt->next();
10530 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10531 ! facesOfNode1.insert( f ).second ) // f encounters twice
10533 if ( face[ iSide ] ) {
10534 MESSAGE( "2 faces per link " );
10535 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10538 faceSet->erase( f );
10540 // get not link nodes
10541 int nbN = f->NbNodes();
10542 if ( f->IsQuadratic() )
10544 nbNodes[ iSide ] = nbN;
10545 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10546 int i1 = f->GetNodeIndex( n1 );
10547 int i2 = f->GetNodeIndex( n2 );
10548 int iEnd = nbN, iBeg = -1, iDelta = 1;
10549 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10551 std::swap( iEnd, iBeg ); iDelta = -1;
10556 if ( i == iEnd ) i = iBeg + iDelta;
10557 if ( i == i1 ) break;
10558 nodes.push_back ( f->GetNode( i ) );
10564 // check similarity of elements of the sides
10565 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10566 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10567 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10568 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10571 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10575 // set nodes to merge
10576 // -------------------
10578 if ( face[0] && face[1] ) {
10579 if ( nbNodes[0] != nbNodes[1] ) {
10580 MESSAGE("Diff nb of face nodes");
10581 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10583 #ifdef DEBUG_MATCHING_NODES
10584 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10585 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10586 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10588 int nbN = nbNodes[0];
10590 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10591 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10592 for ( int i = 0 ; i < nbN - 2; ++i ) {
10593 #ifdef DEBUG_MATCHING_NODES
10594 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10596 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10600 // add other links of the face 1 to linkList
10601 // -----------------------------------------
10603 const SMDS_MeshElement* f0 = face[0];
10604 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10605 for ( int i = 0; i < nbN; i++ )
10607 const SMDS_MeshNode* n2 = f0->GetNode( i );
10608 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10609 linkSet.insert( SMESH_TLink( n1, n2 ));
10610 if ( !iter_isnew.second ) { // already in a set: no need to process
10611 linkSet.erase( iter_isnew.first );
10613 else // new in set == encountered for the first time: add
10615 #ifdef DEBUG_MATCHING_NODES
10616 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10617 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10619 linkList[0].push_back ( NLink( n1, n2 ));
10620 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10625 } // loop on link lists
10630 //================================================================================
10632 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10633 \param theElems - the list of elements (edges or faces) to be replicated
10634 The nodes for duplication could be found from these elements
10635 \param theNodesNot - list of nodes to NOT replicate
10636 \param theAffectedElems - the list of elements (cells and edges) to which the
10637 replicated nodes should be associated to.
10638 \return TRUE if operation has been completed successfully, FALSE otherwise
10640 //================================================================================
10642 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10643 const TIDSortedElemSet& theNodesNot,
10644 const TIDSortedElemSet& theAffectedElems )
10646 myLastCreatedElems.Clear();
10647 myLastCreatedNodes.Clear();
10649 if ( theElems.size() == 0 )
10652 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10657 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10658 // duplicate elements and nodes
10659 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10660 // replce nodes by duplications
10661 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10665 //================================================================================
10667 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10668 \param theMeshDS - mesh instance
10669 \param theElems - the elements replicated or modified (nodes should be changed)
10670 \param theNodesNot - nodes to NOT replicate
10671 \param theNodeNodeMap - relation of old node to new created node
10672 \param theIsDoubleElem - flag os to replicate element or modify
10673 \return TRUE if operation has been completed successfully, FALSE otherwise
10675 //================================================================================
10677 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10678 const TIDSortedElemSet& theElems,
10679 const TIDSortedElemSet& theNodesNot,
10680 std::map< const SMDS_MeshNode*,
10681 const SMDS_MeshNode* >& theNodeNodeMap,
10682 const bool theIsDoubleElem )
10684 MESSAGE("doubleNodes");
10685 // iterate on through element and duplicate them (by nodes duplication)
10687 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10688 for ( ; elemItr != theElems.end(); ++elemItr )
10690 const SMDS_MeshElement* anElem = *elemItr;
10694 bool isDuplicate = false;
10695 // duplicate nodes to duplicate element
10696 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10697 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10699 while ( anIter->more() )
10702 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10703 SMDS_MeshNode* aNewNode = aCurrNode;
10704 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10705 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10706 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10709 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10710 theNodeNodeMap[ aCurrNode ] = aNewNode;
10711 myLastCreatedNodes.Append( aNewNode );
10713 isDuplicate |= (aCurrNode != aNewNode);
10714 newNodes[ ind++ ] = aNewNode;
10716 if ( !isDuplicate )
10719 if ( theIsDoubleElem )
10720 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10723 MESSAGE("ChangeElementNodes");
10724 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10731 //================================================================================
10733 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10734 \param theNodes - identifiers of nodes to be doubled
10735 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10736 nodes. If list of element identifiers is empty then nodes are doubled but
10737 they not assigned to elements
10738 \return TRUE if operation has been completed successfully, FALSE otherwise
10740 //================================================================================
10742 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10743 const std::list< int >& theListOfModifiedElems )
10745 MESSAGE("DoubleNodes");
10746 myLastCreatedElems.Clear();
10747 myLastCreatedNodes.Clear();
10749 if ( theListOfNodes.size() == 0 )
10752 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10756 // iterate through nodes and duplicate them
10758 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10760 std::list< int >::const_iterator aNodeIter;
10761 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10763 int aCurr = *aNodeIter;
10764 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10770 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10773 anOldNodeToNewNode[ aNode ] = aNewNode;
10774 myLastCreatedNodes.Append( aNewNode );
10778 // Create map of new nodes for modified elements
10780 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10782 std::list< int >::const_iterator anElemIter;
10783 for ( anElemIter = theListOfModifiedElems.begin();
10784 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10786 int aCurr = *anElemIter;
10787 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10791 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10793 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10795 while ( anIter->more() )
10797 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10798 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10800 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10801 aNodeArr[ ind++ ] = aNewNode;
10804 aNodeArr[ ind++ ] = aCurrNode;
10806 anElemToNodes[ anElem ] = aNodeArr;
10809 // Change nodes of elements
10811 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10812 anElemToNodesIter = anElemToNodes.begin();
10813 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10815 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10816 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10819 MESSAGE("ChangeElementNodes");
10820 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10829 //================================================================================
10831 \brief Check if element located inside shape
10832 \return TRUE if IN or ON shape, FALSE otherwise
10834 //================================================================================
10836 template<class Classifier>
10837 bool isInside(const SMDS_MeshElement* theElem,
10838 Classifier& theClassifier,
10839 const double theTol)
10841 gp_XYZ centerXYZ (0, 0, 0);
10842 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10843 while (aNodeItr->more())
10844 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10846 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10847 theClassifier.Perform(aPnt, theTol);
10848 TopAbs_State aState = theClassifier.State();
10849 return (aState == TopAbs_IN || aState == TopAbs_ON );
10852 //================================================================================
10854 * \brief Classifier of the 3D point on the TopoDS_Face
10855 * with interaface suitable for isInside()
10857 //================================================================================
10859 struct _FaceClassifier
10861 Extrema_ExtPS _extremum;
10862 BRepAdaptor_Surface _surface;
10863 TopAbs_State _state;
10865 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10867 _extremum.Initialize( _surface,
10868 _surface.FirstUParameter(), _surface.LastUParameter(),
10869 _surface.FirstVParameter(), _surface.LastVParameter(),
10870 _surface.Tolerance(), _surface.Tolerance() );
10872 void Perform(const gp_Pnt& aPnt, double theTol)
10874 _state = TopAbs_OUT;
10875 _extremum.Perform(aPnt);
10876 if ( _extremum.IsDone() )
10877 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10878 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10879 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10881 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10884 TopAbs_State State() const
10891 //================================================================================
10893 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10894 \param theElems - group of of elements (edges or faces) to be replicated
10895 \param theNodesNot - group of nodes not to replicate
10896 \param theShape - shape to detect affected elements (element which geometric center
10897 located on or inside shape).
10898 The replicated nodes should be associated to affected elements.
10899 \return TRUE if operation has been completed successfully, FALSE otherwise
10901 //================================================================================
10903 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10904 const TIDSortedElemSet& theNodesNot,
10905 const TopoDS_Shape& theShape )
10907 if ( theShape.IsNull() )
10910 const double aTol = Precision::Confusion();
10911 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10912 auto_ptr<_FaceClassifier> aFaceClassifier;
10913 if ( theShape.ShapeType() == TopAbs_SOLID )
10915 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10916 bsc3d->PerformInfinitePoint(aTol);
10918 else if (theShape.ShapeType() == TopAbs_FACE )
10920 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10923 // iterates on indicated elements and get elements by back references from their nodes
10924 TIDSortedElemSet anAffected;
10925 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10926 for ( ; elemItr != theElems.end(); ++elemItr )
10928 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10932 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10933 while ( nodeItr->more() )
10935 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10936 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10938 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10939 while ( backElemItr->more() )
10941 const SMDS_MeshElement* curElem = backElemItr->next();
10942 if ( curElem && theElems.find(curElem) == theElems.end() &&
10944 isInside( curElem, *bsc3d, aTol ) :
10945 isInside( curElem, *aFaceClassifier, aTol )))
10946 anAffected.insert( curElem );
10950 return DoubleNodes( theElems, theNodesNot, anAffected );
10954 * \brief compute an oriented angle between two planes defined by four points.
10955 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10956 * @param p0 base of the rotation axe
10957 * @param p1 extremity of the rotation axe
10958 * @param g1 belongs to the first plane
10959 * @param g2 belongs to the second plane
10961 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10963 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10964 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10965 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10966 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10967 gp_Vec vref(p0, p1);
10970 gp_Vec n1 = vref.Crossed(v1);
10971 gp_Vec n2 = vref.Crossed(v2);
10972 return n2.AngleWithRef(n1, vref);
10976 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10977 * The list of groups must describe a partition of the mesh volumes.
10978 * The nodes of the internal faces at the boundaries of the groups are doubled.
10979 * In option, the internal faces are replaced by flat elements.
10980 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10981 * The flat elements are stored in groups of volumes.
10982 * @param theElems - list of groups of volumes, where a group of volume is a set of
10983 * SMDS_MeshElements sorted by Id.
10984 * @param createJointElems - if TRUE, create the elements
10985 * @return TRUE if operation has been completed successfully, FALSE otherwise
10987 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10988 bool createJointElems)
10990 MESSAGE("----------------------------------------------");
10991 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10992 MESSAGE("----------------------------------------------");
10994 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10995 meshDS->BuildDownWardConnectivity(true);
10997 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10999 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11000 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11001 // build the list of nodes shared by 2 or more domains, with their domain indexes
11003 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11004 std::map<int,int>celldom; // cell vtkId --> domain
11005 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11006 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11007 faceDomains.clear();
11009 cellDomains.clear();
11010 nodeDomains.clear();
11011 std::map<int,int> emptyMap;
11012 std::set<int> emptySet;
11015 for (int idom = 0; idom < theElems.size(); idom++)
11018 // --- build a map (face to duplicate --> volume to modify)
11019 // with all the faces shared by 2 domains (group of elements)
11020 // and corresponding volume of this domain, for each shared face.
11021 // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain.
11023 //MESSAGE("Domain " << idom);
11024 const TIDSortedElemSet& domain = theElems[idom];
11025 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11026 for (; elemItr != domain.end(); ++elemItr)
11028 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11031 int vtkId = anElem->getVtkId();
11032 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11033 int neighborsVtkIds[NBMAXNEIGHBORS];
11034 int downIds[NBMAXNEIGHBORS];
11035 unsigned char downTypes[NBMAXNEIGHBORS];
11036 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11037 for (int n = 0; n < nbNeighbors; n++)
11039 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11040 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11041 if (! domain.count(elem)) // neighbor is in another domain : face is shared
11043 DownIdType face(downIds[n], downTypes[n]);
11044 if (!faceDomains.count(face))
11045 faceDomains[face] = emptyMap; // create an empty entry for face
11046 if (!faceDomains[face].count(idom))
11048 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11049 celldom[vtkId] = idom;
11050 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11057 //MESSAGE("Number of shared faces " << faceDomains.size());
11058 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11060 // --- explore the shared faces domain by domain,
11061 // explore the nodes of the face and see if they belong to a cell in the domain,
11062 // which has only a node or an edge on the border (not a shared face)
11064 for (int idomain = 0; idomain < theElems.size(); idomain++)
11066 //MESSAGE("Domain " << idomain);
11067 const TIDSortedElemSet& domain = theElems[idomain];
11068 itface = faceDomains.begin();
11069 for (; itface != faceDomains.end(); ++itface)
11071 std::map<int, int> domvol = itface->second;
11072 if (!domvol.count(idomain))
11074 DownIdType face = itface->first;
11075 //MESSAGE(" --- face " << face.cellId);
11076 std::set<int> oldNodes;
11078 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11079 std::set<int>::iterator itn = oldNodes.begin();
11080 for (; itn != oldNodes.end(); ++itn)
11083 //MESSAGE(" node " << oldId);
11084 std::set<int> cells;
11086 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11087 for (int i=0; i<l.ncells; i++)
11089 int vtkId = l.cells[i];
11090 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11091 if (!domain.count(anElem))
11093 int vtkType = grid->GetCellType(vtkId);
11094 int downId = grid->CellIdToDownId(vtkId);
11097 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11098 continue; // not OK at this stage of the algorithm:
11099 //no cells created after BuildDownWardConnectivity
11101 DownIdType aCell(downId, vtkType);
11102 if (celldom.count(vtkId))
11104 cellDomains[aCell][idomain] = vtkId;
11105 celldom[vtkId] = idomain;
11106 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11112 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11113 // for each shared face, get the nodes
11114 // for each node, for each domain of the face, create a clone of the node
11116 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11117 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11118 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11120 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11121 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11122 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11124 for (int idomain = 0; idomain < theElems.size(); idomain++)
11126 itface = faceDomains.begin();
11127 for (; itface != faceDomains.end(); ++itface)
11129 std::map<int, int> domvol = itface->second;
11130 if (!domvol.count(idomain))
11132 DownIdType face = itface->first;
11133 //MESSAGE(" --- face " << face.cellId);
11134 std::set<int> oldNodes;
11136 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11137 bool isMultipleDetected = false;
11138 std::set<int>::iterator itn = oldNodes.begin();
11139 for (; itn != oldNodes.end(); ++itn)
11142 //MESSAGE(" node " << oldId);
11143 if (!nodeDomains.count(oldId))
11144 nodeDomains[oldId] = emptyMap; // create an empty entry for node
11145 if (nodeDomains[oldId].empty())
11146 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11147 std::map<int, int>::iterator itdom = domvol.begin();
11148 for (; itdom != domvol.end(); ++itdom)
11150 int idom = itdom->first;
11151 //MESSAGE(" domain " << idom);
11152 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11154 if (nodeDomains[oldId].size() >= 2) // a multiple node
11156 vector<int> orderedDoms;
11157 //MESSAGE("multiple node " << oldId);
11158 isMultipleDetected =true;
11159 if (mutipleNodes.count(oldId))
11160 orderedDoms = mutipleNodes[oldId];
11163 map<int,int>::iterator it = nodeDomains[oldId].begin();
11164 for (; it != nodeDomains[oldId].end(); ++it)
11165 orderedDoms.push_back(it->first);
11167 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11168 //stringstream txt;
11169 //for (int i=0; i<orderedDoms.size(); i++)
11170 // txt << orderedDoms[i] << " ";
11171 //MESSAGE("orderedDoms " << txt.str());
11172 mutipleNodes[oldId] = orderedDoms;
11174 double *coords = grid->GetPoint(oldId);
11175 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11176 int newId = newNode->getVtkId();
11177 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11178 //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <<nodeDomains[oldId].size());
11180 if (nodeDomains[oldId].size() >= 3)
11182 //MESSAGE("confirm multiple node " << oldId);
11183 isMultipleDetected =true;
11187 if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains
11189 //MESSAGE("multiple Nodes detected on a shared face");
11190 int downId = itface->first.cellId;
11191 unsigned char cellType = itface->first.cellType;
11192 // --- shared edge or shared face ?
11193 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11196 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11197 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11198 if (mutipleNodes.count(nodes[i]))
11199 if (!mutipleNodesToFace.count(nodes[i]))
11200 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11202 else // shared face (between two volumes)
11204 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11205 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11206 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11207 for (int ie =0; ie < nbEdges; ie++)
11210 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11211 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11213 vector<int> vn0 = mutipleNodes[nodes[0]];
11214 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11215 sort( vn0.begin(), vn0.end() );
11216 sort( vn1.begin(), vn1.end() );
11219 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11220 double *coords = grid->GetPoint(nodes[0]);
11221 gp_Pnt p0(coords[0], coords[1], coords[2]);
11222 coords = grid->GetPoint(nodes[nbNodes - 1]);
11223 gp_Pnt p1(coords[0], coords[1], coords[2]);
11225 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11226 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11227 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11228 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11229 for (int id=0; id < vn0.size(); id++)
11231 int idom = vn0[id];
11232 for (int ivol=0; ivol<nbvol; ivol++)
11234 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11235 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11236 if (theElems[idom].count(elem))
11238 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11239 domvol[idom] = svol;
11240 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11242 vtkIdType npts = 0;
11243 vtkIdType* pts = 0;
11244 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11245 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11248 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11249 angleDom[idom] = 0;
11253 gp_Pnt g(values[0], values[1], values[2]);
11254 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11255 //MESSAGE(" angle=" << angleDom[idom]);
11261 map<double, int> sortedDom; // sort domains by angle
11262 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11263 sortedDom[ia->second] = ia->first;
11264 vector<int> vnodes;
11266 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11268 vdom.push_back(ib->second);
11269 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11271 for (int ino = 0; ino < nbNodes; ino++)
11272 vnodes.push_back(nodes[ino]);
11273 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11282 // --- iterate on shared faces (volumes to modify, face to extrude)
11283 // get node id's of the face (id SMDS = id VTK)
11284 // create flat element with old and new nodes if requested
11286 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11287 // (domain1 X domain2) = domain1 + MAXINT*domain2
11289 std::map<int, std::map<long,int> > nodeQuadDomains;
11290 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11292 if (createJointElems)
11294 itface = faceDomains.begin();
11295 for (; itface != faceDomains.end(); ++itface)
11297 DownIdType face = itface->first;
11298 std::set<int> oldNodes;
11299 std::set<int>::iterator itn;
11301 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11303 std::map<int, int> domvol = itface->second;
11304 std::map<int, int>::iterator itdom = domvol.begin();
11305 int dom1 = itdom->first;
11306 int vtkVolId = itdom->second;
11308 int dom2 = itdom->first;
11309 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11311 stringstream grpname;
11314 grpname << dom1 << "_" << dom2;
11316 grpname << dom2 << "_" << dom1;
11318 string namegrp = grpname.str();
11319 if (!mapOfJunctionGroups.count(namegrp))
11320 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11321 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11323 sgrp->Add(vol->GetID());
11327 // --- create volumes on multiple domain intersection if requested
11328 // iterate on mutipleNodesToFace
11329 // iterate on edgesMultiDomains
11331 if (createJointElems)
11333 // --- iterate on mutipleNodesToFace
11335 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11336 for (; itn != mutipleNodesToFace.end(); ++itn)
11338 int node = itn->first;
11339 vector<int> orderDom = itn->second;
11340 vector<vtkIdType> orderedNodes;
11341 for (int idom = 0; idom <orderDom.size(); idom++)
11342 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11343 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11345 stringstream grpname;
11347 grpname << 0 << "_" << 0;
11349 string namegrp = grpname.str();
11350 if (!mapOfJunctionGroups.count(namegrp))
11351 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11352 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11354 sgrp->Add(face->GetID());
11357 // --- iterate on edgesMultiDomains
11359 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11360 for (; ite != edgesMultiDomains.end(); ++ite)
11362 vector<int> nodes = ite->first;
11363 vector<int> orderDom = ite->second;
11364 vector<vtkIdType> orderedNodes;
11365 if (nodes.size() == 2)
11367 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11368 for (int ino=0; ino < nodes.size(); ino++)
11369 if (orderDom.size() == 3)
11370 for (int idom = 0; idom <orderDom.size(); idom++)
11371 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11373 for (int idom = orderDom.size()-1; idom >=0; idom--)
11374 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11375 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11377 stringstream grpname;
11379 grpname << 0 << "_" << 0;
11381 string namegrp = grpname.str();
11382 if (!mapOfJunctionGroups.count(namegrp))
11383 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11384 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11386 sgrp->Add(vol->GetID());
11390 MESSAGE("Quadratic multiple joints not implemented");
11391 // TODO quadratic nodes
11396 // --- list the explicit faces and edges of the mesh that need to be modified,
11397 // i.e. faces and edges built with one or more duplicated nodes.
11398 // associate these faces or edges to their corresponding domain.
11399 // only the first domain found is kept when a face or edge is shared
11401 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11402 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11403 faceOrEdgeDom.clear();
11406 for (int idomain = 0; idomain < theElems.size(); idomain++)
11408 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11409 for (; itnod != nodeDomains.end(); ++itnod)
11411 int oldId = itnod->first;
11412 //MESSAGE(" node " << oldId);
11413 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11414 for (int i = 0; i < l.ncells; i++)
11416 int vtkId = l.cells[i];
11417 int vtkType = grid->GetCellType(vtkId);
11418 int downId = grid->CellIdToDownId(vtkId);
11420 continue; // new cells: not to be modified
11421 DownIdType aCell(downId, vtkType);
11422 int volParents[1000];
11423 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11424 for (int j = 0; j < nbvol; j++)
11425 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11426 if (!feDom.count(vtkId))
11428 feDom[vtkId] = idomain;
11429 faceOrEdgeDom[aCell] = emptyMap;
11430 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11431 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11432 // << " type " << vtkType << " downId " << downId);
11438 // --- iterate on shared faces (volumes to modify, face to extrude)
11439 // get node id's of the face
11440 // replace old nodes by new nodes in volumes, and update inverse connectivity
11442 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11443 for (int m=0; m<3; m++)
11445 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11446 itface = (*amap).begin();
11447 for (; itface != (*amap).end(); ++itface)
11449 DownIdType face = itface->first;
11450 std::set<int> oldNodes;
11451 std::set<int>::iterator itn;
11453 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11454 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11455 std::map<int, int> localClonedNodeIds;
11457 std::map<int, int> domvol = itface->second;
11458 std::map<int, int>::iterator itdom = domvol.begin();
11459 for (; itdom != domvol.end(); ++itdom)
11461 int idom = itdom->first;
11462 int vtkVolId = itdom->second;
11463 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11464 localClonedNodeIds.clear();
11465 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11468 if (nodeDomains[oldId].count(idom))
11470 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11471 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11474 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11479 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11480 grid->BuildLinks();
11488 * \brief Double nodes on some external faces and create flat elements.
11489 * Flat elements are mainly used by some types of mechanic calculations.
11491 * Each group of the list must be constituted of faces.
11492 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11493 * @param theElems - list of groups of faces, where a group of faces is a set of
11494 * SMDS_MeshElements sorted by Id.
11495 * @return TRUE if operation has been completed successfully, FALSE otherwise
11497 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11499 MESSAGE("-------------------------------------------------");
11500 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11501 MESSAGE("-------------------------------------------------");
11503 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11505 // --- For each group of faces
11506 // duplicate the nodes, create a flat element based on the face
11507 // replace the nodes of the faces by their clones
11509 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11510 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11511 clonedNodes.clear();
11512 intermediateNodes.clear();
11513 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11514 mapOfJunctionGroups.clear();
11516 for (int idom = 0; idom < theElems.size(); idom++)
11518 const TIDSortedElemSet& domain = theElems[idom];
11519 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11520 for (; elemItr != domain.end(); ++elemItr)
11522 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11523 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11526 // MESSAGE("aFace=" << aFace->GetID());
11527 bool isQuad = aFace->IsQuadratic();
11528 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11530 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11532 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11533 while (nodeIt->more())
11535 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11536 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11538 ln2.push_back(node);
11540 ln0.push_back(node);
11542 const SMDS_MeshNode* clone = 0;
11543 if (!clonedNodes.count(node))
11545 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11546 clonedNodes[node] = clone;
11549 clone = clonedNodes[node];
11552 ln3.push_back(clone);
11554 ln1.push_back(clone);
11556 const SMDS_MeshNode* inter = 0;
11557 if (isQuad && (!isMedium))
11559 if (!intermediateNodes.count(node))
11561 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11562 intermediateNodes[node] = inter;
11565 inter = intermediateNodes[node];
11566 ln4.push_back(inter);
11570 // --- extrude the face
11572 vector<const SMDS_MeshNode*> ln;
11573 SMDS_MeshVolume* vol = 0;
11574 vtkIdType aType = aFace->GetVtkType();
11578 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11579 // MESSAGE("vol prism " << vol->GetID());
11580 ln.push_back(ln1[0]);
11581 ln.push_back(ln1[1]);
11582 ln.push_back(ln1[2]);
11585 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11586 // MESSAGE("vol hexa " << vol->GetID());
11587 ln.push_back(ln1[0]);
11588 ln.push_back(ln1[1]);
11589 ln.push_back(ln1[2]);
11590 ln.push_back(ln1[3]);
11592 case VTK_QUADRATIC_TRIANGLE:
11593 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11594 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11595 // MESSAGE("vol quad prism " << vol->GetID());
11596 ln.push_back(ln1[0]);
11597 ln.push_back(ln1[1]);
11598 ln.push_back(ln1[2]);
11599 ln.push_back(ln3[0]);
11600 ln.push_back(ln3[1]);
11601 ln.push_back(ln3[2]);
11603 case VTK_QUADRATIC_QUAD:
11604 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11605 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11606 // ln4[0], ln4[1], ln4[2], ln4[3]);
11607 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11608 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11609 ln4[0], ln4[1], ln4[2], ln4[3]);
11610 // MESSAGE("vol quad hexa " << vol->GetID());
11611 ln.push_back(ln1[0]);
11612 ln.push_back(ln1[1]);
11613 ln.push_back(ln1[2]);
11614 ln.push_back(ln1[3]);
11615 ln.push_back(ln3[0]);
11616 ln.push_back(ln3[1]);
11617 ln.push_back(ln3[2]);
11618 ln.push_back(ln3[3]);
11628 stringstream grpname;
11632 string namegrp = grpname.str();
11633 if (!mapOfJunctionGroups.count(namegrp))
11634 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11635 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11637 sgrp->Add(vol->GetID());
11640 // --- modify the face
11642 aFace->ChangeNodes(&ln[0], ln.size());
11648 //================================================================================
11650 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11651 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11652 * \return TRUE if operation has been completed successfully, FALSE otherwise
11654 //================================================================================
11656 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11658 // iterates on volume elements and detect all free faces on them
11659 SMESHDS_Mesh* aMesh = GetMeshDS();
11662 //bool res = false;
11663 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11664 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11667 const SMDS_MeshVolume* volume = vIt->next();
11668 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11669 vTool.SetExternalNormal();
11670 //const bool isPoly = volume->IsPoly();
11671 const int iQuad = volume->IsQuadratic();
11672 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11674 if (!vTool.IsFreeFace(iface))
11677 vector<const SMDS_MeshNode *> nodes;
11678 int nbFaceNodes = vTool.NbFaceNodes(iface);
11679 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11681 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11682 nodes.push_back(faceNodes[inode]);
11683 if (iQuad) { // add medium nodes
11684 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11685 nodes.push_back(faceNodes[inode]);
11686 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11687 nodes.push_back(faceNodes[8]);
11689 // add new face based on volume nodes
11690 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11692 continue; // face already exsist
11694 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11698 return ( nbFree==(nbExisted+nbCreated) );
11703 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11705 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11707 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11710 //================================================================================
11712 * \brief Creates missing boundary elements
11713 * \param elements - elements whose boundary is to be checked
11714 * \param dimension - defines type of boundary elements to create
11715 * \param group - a group to store created boundary elements in
11716 * \param targetMesh - a mesh to store created boundary elements in
11717 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11718 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11719 * boundary elements will be copied into the targetMesh
11720 * \param toAddExistingBondary - if true, not only new but also pre-existing
11721 * boundary elements will be added into the new group
11722 * \param aroundElements - if true, elements will be created on boundary of given
11723 * elements else, on boundary of the whole mesh.
11724 * \return nb of added boundary elements
11726 //================================================================================
11728 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11729 Bnd_Dimension dimension,
11730 SMESH_Group* group/*=0*/,
11731 SMESH_Mesh* targetMesh/*=0*/,
11732 bool toCopyElements/*=false*/,
11733 bool toCopyExistingBoundary/*=false*/,
11734 bool toAddExistingBondary/*= false*/,
11735 bool aroundElements/*= false*/)
11737 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11738 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11739 // hope that all elements are of the same type, do not check them all
11740 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11741 throw SALOME_Exception(LOCALIZED("wrong element type"));
11744 toCopyElements = toCopyExistingBoundary = false;
11746 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11747 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11748 int nbAddedBnd = 0;
11750 // editor adding present bnd elements and optionally holding elements to add to the group
11751 SMESH_MeshEditor* presentEditor;
11752 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11753 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11755 SMESH_MesherHelper helper( *myMesh );
11756 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11757 SMDS_VolumeTool vTool;
11758 TIDSortedElemSet avoidSet;
11759 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11762 typedef vector<const SMDS_MeshNode*> TConnectivity;
11764 SMDS_ElemIteratorPtr eIt;
11765 if (elements.empty())
11766 eIt = aMesh->elementsIterator(elemType);
11768 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11770 while (eIt->more())
11772 const SMDS_MeshElement* elem = eIt->next();
11773 const int iQuad = elem->IsQuadratic();
11775 // ------------------------------------------------------------------------------------
11776 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11777 // ------------------------------------------------------------------------------------
11778 vector<const SMDS_MeshElement*> presentBndElems;
11779 vector<TConnectivity> missingBndElems;
11780 TConnectivity nodes;
11781 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11783 vTool.SetExternalNormal();
11784 const SMDS_MeshElement* otherVol = 0;
11785 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11787 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11788 ( !aroundElements || elements.count( otherVol )))
11790 const int nbFaceNodes = vTool.NbFaceNodes(iface);
11791 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11792 if ( missType == SMDSAbs_Edge ) // boundary edges
11794 nodes.resize( 2+iQuad );
11795 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11797 for ( int j = 0; j < nodes.size(); ++j )
11799 if ( const SMDS_MeshElement* edge =
11800 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11801 presentBndElems.push_back( edge );
11803 missingBndElems.push_back( nodes );
11806 else // boundary face
11809 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11810 nodes.push_back( nn[inode] );
11811 if (iQuad) // add medium nodes
11812 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11813 nodes.push_back( nn[inode] );
11814 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11816 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11818 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11819 SMDSAbs_Face, /*noMedium=*/false ))
11820 presentBndElems.push_back( f );
11822 missingBndElems.push_back( nodes );
11824 if ( targetMesh != myMesh )
11826 // add 1D elements on face boundary to be added to a new mesh
11827 const SMDS_MeshElement* edge;
11828 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11831 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11833 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11834 if ( edge && avoidSet.insert( edge ).second )
11835 presentBndElems.push_back( edge );
11841 else // elem is a face ------------------------------------------
11843 avoidSet.clear(), avoidSet.insert( elem );
11844 int nbNodes = elem->NbCornerNodes();
11845 nodes.resize( 2 /*+ iQuad*/);
11846 for ( int i = 0; i < nbNodes; i++ )
11848 nodes[0] = elem->GetNode(i);
11849 nodes[1] = elem->GetNode((i+1)%nbNodes);
11850 if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11851 continue; // not free link
11854 //nodes[2] = elem->GetNode( i + nbNodes );
11855 if ( const SMDS_MeshElement* edge =
11856 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true))
11857 presentBndElems.push_back( edge );
11859 missingBndElems.push_back( nodes );
11863 // ---------------------------------
11864 // 2. Add missing boundary elements
11865 // ---------------------------------
11866 if ( targetMesh != myMesh )
11867 // instead of making a map of nodes in this mesh and targetMesh,
11868 // we create nodes with same IDs.
11869 for ( int i = 0; i < missingBndElems.size(); ++i )
11871 TConnectivity& srcNodes = missingBndElems[i];
11872 TConnectivity nodes( srcNodes.size() );
11873 for ( inode = 0; inode < nodes.size(); ++inode )
11874 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11875 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11877 /*noMedium=*/false))
11879 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11883 for ( int i = 0; i < missingBndElems.size(); ++i )
11885 TConnectivity& nodes = missingBndElems[i];
11886 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11888 /*noMedium=*/false))
11890 SMDS_MeshElement* elem =
11891 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11894 // try to set a new element to a shape
11895 if ( myMesh->HasShapeToMesh() )
11898 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11899 const int nbN = nodes.size() / (iQuad+1 );
11900 for ( inode = 0; inode < nbN && ok; ++inode )
11902 pair<int, TopAbs_ShapeEnum> i_stype =
11903 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11904 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11905 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11907 if ( ok && mediumShapes.size() > 1 )
11909 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11910 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11911 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11913 if (( ok = ( stype_i->first != stype_i_0.first )))
11914 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11915 aMesh->IndexToShape( stype_i_0.second ));
11918 if ( ok && mediumShapes.begin()->first == missShapeType )
11919 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11923 // ----------------------------------
11924 // 3. Copy present boundary elements
11925 // ----------------------------------
11926 if ( toCopyExistingBoundary )
11927 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11929 const SMDS_MeshElement* e = presentBndElems[i];
11930 TConnectivity nodes( e->NbNodes() );
11931 for ( inode = 0; inode < nodes.size(); ++inode )
11932 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11933 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11935 else // store present elements to add them to a group
11936 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11938 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11941 } // loop on given elements
11943 // ---------------------------------------------
11944 // 4. Fill group with boundary elements
11945 // ---------------------------------------------
11948 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11949 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11950 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11952 tgtEditor.myLastCreatedElems.Clear();
11953 tgtEditor2.myLastCreatedElems.Clear();
11955 // -----------------------
11956 // 5. Copy given elements
11957 // -----------------------
11958 if ( toCopyElements && targetMesh != myMesh )
11960 if (elements.empty())
11961 eIt = aMesh->elementsIterator(elemType);
11963 eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
11964 while (eIt->more())
11966 const SMDS_MeshElement* elem = eIt->next();
11967 TConnectivity nodes( elem->NbNodes() );
11968 for ( inode = 0; inode < nodes.size(); ++inode )
11969 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11970 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11972 tgtEditor.myLastCreatedElems.Clear();