1 // Copyright (C) 2007-2015 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, or (at your option) any later version.
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_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::CrearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
141 //=======================================================================
145 //=======================================================================
148 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
149 const SMDSAbs_ElementType type,
152 const double ballDiameter)
154 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
155 SMDS_MeshElement* e = 0;
156 int nbnode = node.size();
157 SMESHDS_Mesh* mesh = GetMeshDS();
162 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
163 else e = mesh->AddFace (node[0], node[1], node[2] );
165 else if (nbnode == 4) {
166 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
167 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
169 else if (nbnode == 6) {
170 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171 node[4], node[5], ID);
172 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
175 else if (nbnode == 7) {
176 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177 node[4], node[5], node[6], ID);
178 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
179 node[4], node[5], node[6] );
181 else if (nbnode == 8) {
182 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
183 node[4], node[5], node[6], node[7], ID);
184 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
185 node[4], node[5], node[6], node[7] );
187 else if (nbnode == 9) {
188 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
189 node[4], node[5], node[6], node[7], node[8], ID);
190 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
191 node[4], node[5], node[6], node[7], node[8] );
194 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
195 else e = mesh->AddPolygonalFace (node );
202 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
203 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
205 else if (nbnode == 5) {
206 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
211 else if (nbnode == 6) {
212 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213 node[4], node[5], ID);
214 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
217 else if (nbnode == 8) {
218 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7], ID);
220 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7] );
223 else if (nbnode == 10) {
224 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
226 node[8], node[9], ID);
227 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
228 node[4], node[5], node[6], node[7],
231 else if (nbnode == 12) {
232 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7],
234 node[8], node[9], node[10], node[11], ID);
235 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7],
237 node[8], node[9], node[10], node[11] );
239 else if (nbnode == 13) {
240 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241 node[4], node[5], node[6], node[7],
242 node[8], node[9], node[10],node[11],
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
245 node[4], node[5], node[6], node[7],
246 node[8], node[9], node[10],node[11],
249 else if (nbnode == 15) {
250 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251 node[4], node[5], node[6], node[7],
252 node[8], node[9], node[10],node[11],
253 node[12],node[13],node[14],ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
255 node[4], node[5], node[6], node[7],
256 node[8], node[9], node[10],node[11],
257 node[12],node[13],node[14] );
259 else if (nbnode == 20) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7],
262 node[8], node[9], node[10],node[11],
263 node[12],node[13],node[14],node[15],
264 node[16],node[17],node[18],node[19],ID);
265 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7],
267 node[8], node[9], node[10],node[11],
268 node[12],node[13],node[14],node[15],
269 node[16],node[17],node[18],node[19] );
271 else if (nbnode == 27) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
274 node[8], node[9], node[10],node[11],
275 node[12],node[13],node[14],node[15],
276 node[16],node[17],node[18],node[19],
277 node[20],node[21],node[22],node[23],
278 node[24],node[25],node[26], ID);
279 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
282 node[12],node[13],node[14],node[15],
283 node[16],node[17],node[18],node[19],
284 node[20],node[21],node[22],node[23],
285 node[24],node[25],node[26] );
292 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
293 else e = mesh->AddEdge (node[0], node[1] );
295 else if ( nbnode == 3 ) {
296 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
297 else e = mesh->AddEdge (node[0], node[1], node[2] );
301 case SMDSAbs_0DElement:
303 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
304 else e = mesh->Add0DElement (node[0] );
309 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
310 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
314 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
315 else e = mesh->AddBall (node[0], ballDiameter);
320 if ( e ) myLastCreatedElems.Append( e );
324 //=======================================================================
328 //=======================================================================
330 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
331 const SMDSAbs_ElementType type,
335 vector<const SMDS_MeshNode*> nodes;
336 nodes.reserve( nodeIDs.size() );
337 vector<int>::const_iterator id = nodeIDs.begin();
338 while ( id != nodeIDs.end() ) {
339 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
340 nodes.push_back( node );
344 return AddElement( nodes, type, isPoly, ID );
347 //=======================================================================
349 //purpose : Remove a node or an element.
350 // Modify a compute state of sub-meshes which become empty
351 //=======================================================================
353 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
356 myLastCreatedElems.Clear();
357 myLastCreatedNodes.Clear();
359 SMESHDS_Mesh* aMesh = GetMeshDS();
360 set< SMESH_subMesh *> smmap;
363 list<int>::const_iterator it = theIDs.begin();
364 for ( ; it != theIDs.end(); it++ ) {
365 const SMDS_MeshElement * elem;
367 elem = aMesh->FindNode( *it );
369 elem = aMesh->FindElement( *it );
373 // Notify VERTEX sub-meshes about modification
375 const SMDS_MeshNode* node = cast2Node( elem );
376 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
377 if ( int aShapeID = node->getshapeId() )
378 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
381 // Find sub-meshes to notify about modification
382 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
383 // while ( nodeIt->more() ) {
384 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
385 // const SMDS_PositionPtr& aPosition = node->GetPosition();
386 // if ( aPosition.get() ) {
387 // if ( int aShapeID = aPosition->GetShapeId() ) {
388 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
389 // smmap.insert( sm );
396 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
398 aMesh->RemoveElement( elem );
402 // Notify sub-meshes about modification
403 if ( !smmap.empty() ) {
404 set< SMESH_subMesh *>::iterator smIt;
405 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
406 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
409 // // Check if the whole mesh becomes empty
410 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
411 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
416 //================================================================================
418 * \brief Create 0D elements on all nodes of the given object except those
419 * nodes on which a 0D element already exists.
420 * \param elements - Elements on whose nodes to create 0D elements; if empty,
421 * the all mesh is treated
422 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
424 //================================================================================
426 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
427 TIDSortedElemSet& all0DElems )
429 SMDS_ElemIteratorPtr elemIt;
430 vector< const SMDS_MeshElement* > allNodes;
431 if ( elements.empty() )
433 allNodes.reserve( GetMeshDS()->NbNodes() );
434 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
435 while ( elemIt->more() )
436 allNodes.push_back( elemIt->next() );
438 elemIt = elemSetIterator( allNodes );
442 elemIt = elemSetIterator( elements );
445 while ( elemIt->more() )
447 const SMDS_MeshElement* e = elemIt->next();
448 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
449 while ( nodeIt->more() )
451 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
452 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
454 all0DElems.insert( it0D->next() );
456 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
457 all0DElems.insert( myLastCreatedElems.Last() );
463 //=======================================================================
464 //function : FindShape
465 //purpose : Return an index of the shape theElem is on
466 // or zero if a shape not found
467 //=======================================================================
469 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
471 myLastCreatedElems.Clear();
472 myLastCreatedNodes.Clear();
474 SMESHDS_Mesh * aMesh = GetMeshDS();
475 if ( aMesh->ShapeToMesh().IsNull() )
478 int aShapeID = theElem->getshapeId();
482 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
483 if ( sm->Contains( theElem ))
486 if ( theElem->GetType() == SMDSAbs_Node ) {
487 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
490 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
493 TopoDS_Shape aShape; // the shape a node of theElem is on
494 if ( theElem->GetType() != SMDSAbs_Node )
496 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
497 while ( nodeIt->more() ) {
498 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
499 if ((aShapeID = node->getshapeId()) > 0) {
500 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
501 if ( sm->Contains( theElem ))
503 if ( aShape.IsNull() )
504 aShape = aMesh->IndexToShape( aShapeID );
510 // None of nodes is on a proper shape,
511 // find the shape among ancestors of aShape on which a node is
512 if ( !aShape.IsNull() ) {
513 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
514 for ( ; ancIt.More(); ancIt.Next() ) {
515 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
516 if ( sm && sm->Contains( theElem ))
517 return aMesh->ShapeToIndex( ancIt.Value() );
522 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
523 while ( const SMESHDS_SubMesh* sm = smIt->next() )
524 if ( sm->Contains( theElem ))
531 //=======================================================================
532 //function : IsMedium
534 //=======================================================================
536 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
537 const SMDSAbs_ElementType typeToCheck)
539 bool isMedium = false;
540 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
541 while (it->more() && !isMedium ) {
542 const SMDS_MeshElement* elem = it->next();
543 isMedium = elem->IsMediumNode(node);
548 //=======================================================================
549 //function : shiftNodesQuadTria
550 //purpose : Shift nodes in the array corresponded to quadratic triangle
551 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
552 //=======================================================================
554 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 const SMDS_MeshNode* nd1 = aNodes[0];
557 aNodes[0] = aNodes[1];
558 aNodes[1] = aNodes[2];
560 const SMDS_MeshNode* nd2 = aNodes[3];
561 aNodes[3] = aNodes[4];
562 aNodes[4] = aNodes[5];
566 //=======================================================================
567 //function : nbEdgeConnectivity
568 //purpose : return number of the edges connected with the theNode.
569 // if theEdges has connections with the other type of the
570 // elements, return -1
571 //=======================================================================
573 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577 // while(elemIt->more()) {
582 return theNode->NbInverseElements();
585 //=======================================================================
586 //function : getNodesFromTwoTria
588 //=======================================================================
590 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
591 const SMDS_MeshElement * theTria2,
592 vector< const SMDS_MeshNode*>& N1,
593 vector< const SMDS_MeshNode*>& N2)
595 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
596 if ( N1.size() < 6 ) return false;
597 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
598 if ( N2.size() < 6 ) return false;
600 int sames[3] = {-1,-1,-1};
612 if(nbsames!=2) return false;
614 shiftNodesQuadTria(N1);
616 shiftNodesQuadTria(N1);
619 i = sames[0] + sames[1] + sames[2];
621 shiftNodesQuadTria(N2);
623 // now we receive following N1 and N2 (using numeration as in the image below)
624 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
625 // i.e. first nodes from both arrays form a new diagonal
629 //=======================================================================
630 //function : InverseDiag
631 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
632 // but having other common link.
633 // Return False if args are improper
634 //=======================================================================
636 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
637 const SMDS_MeshElement * theTria2 )
639 MESSAGE("InverseDiag");
640 myLastCreatedElems.Clear();
641 myLastCreatedNodes.Clear();
643 if (!theTria1 || !theTria2)
646 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
647 if (!F1) return false;
648 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
649 if (!F2) return false;
650 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
651 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
654 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
658 // put nodes in array and find out indices of the same ones
659 const SMDS_MeshNode* aNodes [6];
660 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
662 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
663 while ( it->more() ) {
664 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666 if ( i > 2 ) // theTria2
667 // find same node of theTria1
668 for ( int j = 0; j < 3; j++ )
669 if ( aNodes[ i ] == aNodes[ j ]) {
678 return false; // theTria1 is not a triangle
679 it = theTria2->nodesIterator();
681 if ( i == 6 && it->more() )
682 return false; // theTria2 is not a triangle
685 // find indices of 1,2 and of A,B in theTria1
686 int iA = -1, iB = 0, i1 = 0, i2 = 0;
687 for ( i = 0; i < 6; i++ ) {
688 if ( sameInd [ i ] == -1 ) {
693 if ( iA >= 0) iB = i;
697 // nodes 1 and 2 should not be the same
698 if ( aNodes[ i1 ] == aNodes[ i2 ] )
702 aNodes[ iA ] = aNodes[ i2 ];
704 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
707 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
711 } // end if(F1 && F2)
713 // check case of quadratic faces
714 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
715 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
718 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
722 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
723 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
731 vector< const SMDS_MeshNode* > N1;
732 vector< const SMDS_MeshNode* > N2;
733 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735 // now we receive following N1 and N2 (using numeration as above image)
736 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
737 // i.e. first nodes from both arrays determ new diagonal
739 vector< const SMDS_MeshNode*> N1new( N1.size() );
740 vector< const SMDS_MeshNode*> N2new( N2.size() );
741 N1new.back() = N1.back(); // central node of biquadratic
742 N2new.back() = N2.back();
743 N1new[0] = N1[0]; N2new[0] = N1[0];
744 N1new[1] = N2[0]; N2new[1] = N1[1];
745 N1new[2] = N2[1]; N2new[2] = N2[0];
746 N1new[3] = N1[4]; N2new[3] = N1[3];
747 N1new[4] = N2[3]; N2new[4] = N2[5];
748 N1new[5] = N1[5]; N2new[5] = N1[4];
749 // change nodes in faces
750 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
751 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753 // move the central node of biquadratic triangle
754 SMESH_MesherHelper helper( *GetMesh() );
755 for ( int is2nd = 0; is2nd < 2; ++is2nd )
757 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
758 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
759 if ( nodes.size() < 7 )
761 helper.SetSubShape( tria->getshapeId() );
762 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
766 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
767 SMESH_TNodeXYZ( nodes[4] ) +
768 SMESH_TNodeXYZ( nodes[5] )) / 3.;
773 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
774 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
775 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
778 xyz = S->Value( uv.X(), uv.Y() );
779 xyz.Transform( loc );
780 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
781 nodes[6]->getshapeId() > 0 )
782 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
789 //=======================================================================
790 //function : findTriangles
791 //purpose : find triangles sharing theNode1-theNode2 link
792 //=======================================================================
794 static bool findTriangles(const SMDS_MeshNode * theNode1,
795 const SMDS_MeshNode * theNode2,
796 const SMDS_MeshElement*& theTria1,
797 const SMDS_MeshElement*& theTria2)
799 if ( !theNode1 || !theNode2 ) return false;
801 theTria1 = theTria2 = 0;
803 set< const SMDS_MeshElement* > emap;
804 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806 const SMDS_MeshElement* elem = it->next();
807 if ( elem->NbCornerNodes() == 3 )
810 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812 const SMDS_MeshElement* elem = it->next();
813 if ( emap.count( elem )) {
821 // theTria1 must be element with minimum ID
822 if ( theTria2->GetID() < theTria1->GetID() )
823 std::swap( theTria2, theTria1 );
831 //=======================================================================
832 //function : InverseDiag
833 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
834 // with ones built on the same 4 nodes but having other common link.
835 // Return false if proper faces not found
836 //=======================================================================
838 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
839 const SMDS_MeshNode * theNode2)
841 myLastCreatedElems.Clear();
842 myLastCreatedNodes.Clear();
844 MESSAGE( "::InverseDiag()" );
846 const SMDS_MeshElement *tr1, *tr2;
847 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
850 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
851 if (!F1) return false;
852 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
853 if (!F2) return false;
854 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
855 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
858 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
862 // put nodes in array
863 // and find indices of 1,2 and of A in tr1 and of B in tr2
864 int i, iA1 = 0, i1 = 0;
865 const SMDS_MeshNode* aNodes1 [3];
866 SMDS_ElemIteratorPtr it;
867 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
868 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
869 if ( aNodes1[ i ] == theNode1 )
870 iA1 = i; // node A in tr1
871 else if ( aNodes1[ i ] != theNode2 )
875 const SMDS_MeshNode* aNodes2 [3];
876 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
877 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
878 if ( aNodes2[ i ] == theNode2 )
879 iB2 = i; // node B in tr2
880 else if ( aNodes2[ i ] != theNode1 )
884 // nodes 1 and 2 should not be the same
885 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
889 aNodes1[ iA1 ] = aNodes2[ i2 ];
891 aNodes2[ iB2 ] = aNodes1[ i1 ];
893 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
894 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
899 // check case of quadratic faces
900 return InverseDiag(tr1,tr2);
903 //=======================================================================
904 //function : getQuadrangleNodes
905 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
906 // fusion of triangles tr1 and tr2 having shared link on
907 // theNode1 and theNode2
908 //=======================================================================
910 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
911 const SMDS_MeshNode * theNode1,
912 const SMDS_MeshNode * theNode2,
913 const SMDS_MeshElement * tr1,
914 const SMDS_MeshElement * tr2 )
916 if( tr1->NbNodes() != tr2->NbNodes() )
918 // find the 4-th node to insert into tr1
919 const SMDS_MeshNode* n4 = 0;
920 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922 while ( !n4 && i<3 ) {
923 const SMDS_MeshNode * n = cast2Node( it->next() );
925 bool isDiag = ( n == theNode1 || n == theNode2 );
929 // Make an array of nodes to be in a quadrangle
930 int iNode = 0, iFirstDiag = -1;
931 it = tr1->nodesIterator();
934 const SMDS_MeshNode * n = cast2Node( it->next() );
936 bool isDiag = ( n == theNode1 || n == theNode2 );
938 if ( iFirstDiag < 0 )
940 else if ( iNode - iFirstDiag == 1 )
941 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943 else if ( n == n4 ) {
944 return false; // tr1 and tr2 should not have all the same nodes
946 theQuadNodes[ iNode++ ] = n;
948 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
949 theQuadNodes[ iNode ] = n4;
954 //=======================================================================
955 //function : DeleteDiag
956 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
957 // with a quadrangle built on the same 4 nodes.
958 // Return false if proper faces not found
959 //=======================================================================
961 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
962 const SMDS_MeshNode * theNode2)
964 myLastCreatedElems.Clear();
965 myLastCreatedNodes.Clear();
967 MESSAGE( "::DeleteDiag()" );
969 const SMDS_MeshElement *tr1, *tr2;
970 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
973 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
974 if (!F1) return false;
975 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
976 if (!F2) return false;
977 SMESHDS_Mesh * aMesh = GetMeshDS();
979 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
980 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982 const SMDS_MeshNode* aNodes [ 4 ];
983 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
986 const SMDS_MeshElement* newElem = 0;
987 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
988 myLastCreatedElems.Append(newElem);
989 AddToSameGroups( newElem, tr1, aMesh );
990 int aShapeId = tr1->getshapeId();
993 aMesh->SetMeshElementOnShape( newElem, aShapeId );
995 aMesh->RemoveElement( tr1 );
996 aMesh->RemoveElement( tr2 );
1001 // check case of quadratic faces
1002 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1008 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1009 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1017 vector< const SMDS_MeshNode* > N1;
1018 vector< const SMDS_MeshNode* > N2;
1019 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021 // now we receive following N1 and N2 (using numeration as above image)
1022 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1023 // i.e. first nodes from both arrays determ new diagonal
1025 const SMDS_MeshNode* aNodes[8];
1035 const SMDS_MeshElement* newElem = 0;
1036 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1037 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1038 myLastCreatedElems.Append(newElem);
1039 AddToSameGroups( newElem, tr1, aMesh );
1040 int aShapeId = tr1->getshapeId();
1043 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045 aMesh->RemoveElement( tr1 );
1046 aMesh->RemoveElement( tr2 );
1048 // remove middle node (9)
1049 GetMeshDS()->RemoveNode( N1[4] );
1054 //=======================================================================
1055 //function : Reorient
1056 //purpose : Reverse theElement orientation
1057 //=======================================================================
1059 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 MESSAGE("Reorient");
1062 myLastCreatedElems.Clear();
1063 myLastCreatedNodes.Clear();
1067 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1068 if ( !it || !it->more() )
1071 const SMDSAbs_ElementType type = theElem->GetType();
1072 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1075 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1076 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078 const SMDS_VtkVolume* aPolyedre =
1079 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081 MESSAGE("Warning: bad volumic element");
1084 const int nbFaces = aPolyedre->NbFaces();
1085 vector<const SMDS_MeshNode *> poly_nodes;
1086 vector<int> quantities (nbFaces);
1088 // reverse each face of the polyedre
1089 for (int iface = 1; iface <= nbFaces; iface++) {
1090 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1091 quantities[iface - 1] = nbFaceNodes;
1093 for (inode = nbFaceNodes; inode >= 1; inode--) {
1094 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1095 poly_nodes.push_back(curNode);
1098 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100 else // other elements
1102 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1103 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1104 if ( interlace.empty() )
1106 std::reverse( nodes.begin(), nodes.end() ); // polygon
1108 else if ( interlace.size() > 1 )
1110 SMDS_MeshCell::applyInterlace( interlace, nodes );
1112 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1117 //================================================================================
1119 * \brief Reorient faces.
1120 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1121 * \param theDirection - desired direction of normal of \a theFace
1122 * \param theFace - one of \a theFaces that sould be oriented according to
1123 * \a theDirection and whose orientation defines orientation of other faces
1124 * \return number of reoriented faces.
1126 //================================================================================
1128 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1129 const gp_Dir& theDirection,
1130 const SMDS_MeshElement * theFace)
1133 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135 if ( theFaces.empty() )
1137 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1138 while ( fIt->more() )
1139 theFaces.insert( theFaces.end(), fIt->next() );
1142 // orient theFace according to theDirection
1144 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1145 if ( normal * theDirection.XYZ() < 0 )
1146 nbReori += Reorient( theFace );
1148 // Orient other faces
1150 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1151 TIDSortedElemSet avoidSet;
1152 set< SMESH_TLink > checkedLinks;
1153 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1156 theFaces.erase( theFace );
1157 startFaces.insert( theFace );
1159 int nodeInd1, nodeInd2;
1160 const SMDS_MeshElement* otherFace;
1161 vector< const SMDS_MeshElement* > facesNearLink;
1162 vector< std::pair< int, int > > nodeIndsOfFace;
1164 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1165 while ( !startFaces.empty() )
1167 startFace = startFaces.begin();
1168 theFace = *startFace;
1169 startFaces.erase( startFace );
1170 if ( !visitedFaces.insert( theFace ).second )
1174 avoidSet.insert(theFace);
1176 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1178 const int nbNodes = theFace->NbCornerNodes();
1179 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1182 linkIt_isNew = checkedLinks.insert( link );
1183 if ( !linkIt_isNew.second )
1185 // link has already been checked and won't be encountered more
1186 // if the group (theFaces) is manifold
1187 //checkedLinks.erase( linkIt_isNew.first );
1191 facesNearLink.clear();
1192 nodeIndsOfFace.clear();
1193 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195 &nodeInd1, &nodeInd2 )))
1196 if ( otherFace != theFace)
1198 facesNearLink.push_back( otherFace );
1199 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1200 avoidSet.insert( otherFace );
1202 if ( facesNearLink.size() > 1 )
1204 // NON-MANIFOLD mesh shell !
1205 // select a face most co-directed with theFace,
1206 // other faces won't be visited this time
1208 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1209 double proj, maxProj = -1;
1210 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1211 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1212 if (( proj = Abs( NF * NOF )) > maxProj ) {
1214 otherFace = facesNearLink[i];
1215 nodeInd1 = nodeIndsOfFace[i].first;
1216 nodeInd2 = nodeIndsOfFace[i].second;
1219 // not to visit rejected faces
1220 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1221 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1222 visitedFaces.insert( facesNearLink[i] );
1224 else if ( facesNearLink.size() == 1 )
1226 otherFace = facesNearLink[0];
1227 nodeInd1 = nodeIndsOfFace.back().first;
1228 nodeInd2 = nodeIndsOfFace.back().second;
1230 if ( otherFace && otherFace != theFace)
1232 // link must be reverse in otherFace if orientation ot otherFace
1233 // is same as that of theFace
1234 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236 nbReori += Reorient( otherFace );
1238 startFaces.insert( otherFace );
1241 std::swap( link.first, link.second ); // reverse the link
1247 //================================================================================
1249 * \brief Reorient faces basing on orientation of adjacent volumes.
1250 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1251 * \param theVolumes - reference volumes.
1252 * \param theOutsideNormal - to orient faces to have their normal
1253 * pointing either \a outside or \a inside the adjacent volumes.
1254 * \return number of reoriented faces.
1256 //================================================================================
1258 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1259 TIDSortedElemSet & theVolumes,
1260 const bool theOutsideNormal)
1264 SMDS_ElemIteratorPtr faceIt;
1265 if ( theFaces.empty() )
1266 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1268 faceIt = elemSetIterator( theFaces );
1270 vector< const SMDS_MeshNode* > faceNodes;
1271 TIDSortedElemSet checkedVolumes;
1272 set< const SMDS_MeshNode* > faceNodesSet;
1273 SMDS_VolumeTool volumeTool;
1275 while ( faceIt->more() ) // loop on given faces
1277 const SMDS_MeshElement* face = faceIt->next();
1278 if ( face->GetType() != SMDSAbs_Face )
1281 const int nbCornersNodes = face->NbCornerNodes();
1282 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1284 checkedVolumes.clear();
1285 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1286 while ( vIt->more() )
1288 const SMDS_MeshElement* volume = vIt->next();
1290 if ( !checkedVolumes.insert( volume ).second )
1292 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1295 // is volume adjacent?
1296 bool allNodesCommon = true;
1297 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1298 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1299 if ( !allNodesCommon )
1302 // get nodes of a corresponding volume facet
1303 faceNodesSet.clear();
1304 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1305 volumeTool.Set( volume );
1306 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1307 if ( facetID < 0 ) continue;
1308 volumeTool.SetExternalNormal();
1309 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1311 // compare order of faceNodes and facetNodes
1312 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1314 for ( int i = 0; i < 2; ++i )
1316 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1317 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1318 if ( faceNodes[ iN ] == n )
1324 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1325 if ( isOutside != theOutsideNormal )
1326 nbReori += Reorient( face );
1328 } // loop on given faces
1333 //=======================================================================
1334 //function : getBadRate
1336 //=======================================================================
1338 static double getBadRate (const SMDS_MeshElement* theElem,
1339 SMESH::Controls::NumericalFunctorPtr& theCrit)
1341 SMESH::Controls::TSequenceOfXYZ P;
1342 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1344 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1345 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1348 //=======================================================================
1349 //function : QuadToTri
1350 //purpose : Cut quadrangles into triangles.
1351 // theCrit is used to select a diagonal to cut
1352 //=======================================================================
1354 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1355 SMESH::Controls::NumericalFunctorPtr theCrit)
1357 myLastCreatedElems.Clear();
1358 myLastCreatedNodes.Clear();
1360 if ( !theCrit.get() )
1363 SMESHDS_Mesh * aMesh = GetMeshDS();
1365 Handle(Geom_Surface) surface;
1366 SMESH_MesherHelper helper( *GetMesh() );
1368 TIDSortedElemSet::iterator itElem;
1369 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1371 const SMDS_MeshElement* elem = *itElem;
1372 if ( !elem || elem->GetType() != SMDSAbs_Face )
1374 if ( elem->NbCornerNodes() != 4 )
1377 // retrieve element nodes
1378 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1380 // compare two sets of possible triangles
1381 double aBadRate1, aBadRate2; // to what extent a set is bad
1382 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1383 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1384 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1386 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1387 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1388 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1390 const int aShapeId = FindShape( elem );
1391 const SMDS_MeshElement* newElem1 = 0;
1392 const SMDS_MeshElement* newElem2 = 0;
1394 if ( !elem->IsQuadratic() ) // split liner quadrangle
1396 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1397 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1398 if ( aBadRate1 <= aBadRate2 ) {
1399 // tr1 + tr2 is better
1400 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1401 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1404 // tr3 + tr4 is better
1405 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1406 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1409 else // split quadratic quadrangle
1411 helper.SetIsQuadratic( true );
1412 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1414 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1415 if ( aNodes.size() == 9 )
1417 helper.SetIsBiQuadratic( true );
1418 if ( aBadRate1 <= aBadRate2 )
1419 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1421 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1423 // create a new element
1424 if ( aBadRate1 <= aBadRate2 ) {
1425 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1426 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1429 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1430 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1434 // care of a new element
1436 myLastCreatedElems.Append(newElem1);
1437 myLastCreatedElems.Append(newElem2);
1438 AddToSameGroups( newElem1, elem, aMesh );
1439 AddToSameGroups( newElem2, elem, aMesh );
1441 // put a new triangle on the same shape
1443 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1444 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1446 aMesh->RemoveElement( elem );
1451 //=======================================================================
1453 * \brief Split each of given quadrangles into 4 triangles.
1454 * \param theElems - The faces to be splitted. If empty all faces are split.
1456 //=======================================================================
1458 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1460 myLastCreatedElems.Clear();
1461 myLastCreatedNodes.Clear();
1463 SMESH_MesherHelper helper( *GetMesh() );
1464 helper.SetElementsOnShape( true );
1466 SMDS_ElemIteratorPtr faceIt;
1467 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1468 else faceIt = elemSetIterator( theElems );
1471 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1473 vector< const SMDS_MeshNode* > nodes;
1474 SMESHDS_SubMesh* subMeshDS;
1476 Handle(Geom_Surface) surface;
1477 TopLoc_Location loc;
1479 while ( faceIt->more() )
1481 const SMDS_MeshElement* quad = faceIt->next();
1482 if ( !quad || quad->NbCornerNodes() != 4 )
1485 // get a surface the quad is on
1487 if ( quad->getshapeId() < 1 )
1490 helper.SetSubShape( 0 );
1493 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1495 helper.SetSubShape( quad->getshapeId() );
1496 if ( !helper.GetSubShape().IsNull() &&
1497 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1499 F = TopoDS::Face( helper.GetSubShape() );
1500 surface = BRep_Tool::Surface( F, loc );
1501 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1505 helper.SetSubShape( 0 );
1510 // create a central node
1512 const SMDS_MeshNode* nCentral;
1513 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1515 if ( nodes.size() == 9 )
1517 nCentral = nodes.back();
1524 for ( ; iN < nodes.size(); ++iN )
1525 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1527 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1528 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1530 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1531 xyz[0], xyz[1], xyz[2], xyz[3],
1532 xyz[4], xyz[5], xyz[6], xyz[7] );
1536 for ( ; iN < nodes.size(); ++iN )
1537 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1539 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1540 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1542 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543 uv[0], uv[1], uv[2], uv[3],
1544 uv[4], uv[5], uv[6], uv[7] );
1546 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1550 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1551 uv[8].X(), uv[8].Y() );
1552 myLastCreatedNodes.Append( nCentral );
1555 // create 4 triangles
1557 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1559 helper.SetIsQuadratic ( nodes.size() > 4 );
1560 helper.SetIsBiQuadratic( nodes.size() == 9 );
1561 if ( helper.GetIsQuadratic() )
1562 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1564 for ( int i = 0; i < 4; ++i )
1566 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1569 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1570 myLastCreatedElems.Append( tria );
1575 //=======================================================================
1576 //function : BestSplit
1577 //purpose : Find better diagonal for cutting.
1578 //=======================================================================
1580 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1581 SMESH::Controls::NumericalFunctorPtr theCrit)
1583 myLastCreatedElems.Clear();
1584 myLastCreatedNodes.Clear();
1589 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1592 if( theQuad->NbNodes()==4 ||
1593 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1595 // retrieve element nodes
1596 const SMDS_MeshNode* aNodes [4];
1597 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1599 //while (itN->more())
1601 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1603 // compare two sets of possible triangles
1604 double aBadRate1, aBadRate2; // to what extent a set is bad
1605 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1606 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1607 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1609 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1610 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1611 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1612 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1613 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1614 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1615 return 1; // diagonal 1-3
1617 return 2; // diagonal 2-4
1624 // Methods of splitting volumes into tetra
1626 const int theHexTo5_1[5*4+1] =
1628 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1630 const int theHexTo5_2[5*4+1] =
1632 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1634 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1636 const int theHexTo6_1[6*4+1] =
1638 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
1640 const int theHexTo6_2[6*4+1] =
1642 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
1644 const int theHexTo6_3[6*4+1] =
1646 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
1648 const int theHexTo6_4[6*4+1] =
1650 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
1652 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1654 const int thePyraTo2_1[2*4+1] =
1656 0, 1, 2, 4, 0, 2, 3, 4, -1
1658 const int thePyraTo2_2[2*4+1] =
1660 1, 2, 3, 4, 1, 3, 0, 4, -1
1662 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1664 const int thePentaTo3_1[3*4+1] =
1666 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1668 const int thePentaTo3_2[3*4+1] =
1670 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1672 const int thePentaTo3_3[3*4+1] =
1674 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1676 const int thePentaTo3_4[3*4+1] =
1678 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1680 const int thePentaTo3_5[3*4+1] =
1682 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1684 const int thePentaTo3_6[3*4+1] =
1686 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1688 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1689 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1691 // Methods of splitting hexahedron into prisms
1693 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1695 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1697 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1699 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1701 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1703 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1706 const int theHexTo2Prisms_BT_1[6*2+1] =
1708 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1710 const int theHexTo2Prisms_BT_2[6*2+1] =
1712 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1714 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1716 const int theHexTo2Prisms_LR_1[6*2+1] =
1718 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1720 const int theHexTo2Prisms_LR_2[6*2+1] =
1722 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1724 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1726 const int theHexTo2Prisms_FB_1[6*2+1] =
1728 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1730 const int theHexTo2Prisms_FB_2[6*2+1] =
1732 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1734 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1737 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1740 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1741 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1742 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1743 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1749 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1750 bool _baryNode; //!< additional node is to be created at cell barycenter
1751 bool _ownConn; //!< to delete _connectivity in destructor
1752 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1754 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1755 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1756 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1757 bool hasFacet( const TTriangleFacet& facet ) const
1759 if ( _nbCorners == 4 )
1761 const int* tetConn = _connectivity;
1762 for ( ; tetConn[0] >= 0; tetConn += 4 )
1763 if (( facet.contains( tetConn[0] ) +
1764 facet.contains( tetConn[1] ) +
1765 facet.contains( tetConn[2] ) +
1766 facet.contains( tetConn[3] )) == 3 )
1769 else // prism, _nbCorners == 6
1771 const int* prismConn = _connectivity;
1772 for ( ; prismConn[0] >= 0; prismConn += 6 )
1774 if (( facet.contains( prismConn[0] ) &&
1775 facet.contains( prismConn[1] ) &&
1776 facet.contains( prismConn[2] ))
1778 ( facet.contains( prismConn[3] ) &&
1779 facet.contains( prismConn[4] ) &&
1780 facet.contains( prismConn[5] )))
1788 //=======================================================================
1790 * \brief return TSplitMethod for the given element to split into tetrahedra
1792 //=======================================================================
1794 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1796 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1798 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1799 // an edge and a face barycenter; tertaherdons are based on triangles and
1800 // a volume barycenter
1801 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1803 // Find out how adjacent volumes are split
1805 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1806 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1807 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1809 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1810 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1811 if ( nbNodes < 4 ) continue;
1813 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1814 const int* nInd = vol.GetFaceNodesIndices( iF );
1817 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1818 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1819 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1820 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1824 int iCom = 0; // common node of triangle faces to split into
1825 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1827 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1828 nInd[ iQ * ( (iCom+1)%nbNodes )],
1829 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1830 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1831 nInd[ iQ * ( (iCom+2)%nbNodes )],
1832 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1833 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1835 triaSplits.push_back( t012 );
1836 triaSplits.push_back( t023 );
1841 if ( !triaSplits.empty() )
1842 hasAdjacentSplits = true;
1845 // Among variants of split method select one compliant with adjacent volumes
1847 TSplitMethod method;
1848 if ( !vol.Element()->IsPoly() && !is24TetMode )
1850 int nbVariants = 2, nbTet = 0;
1851 const int** connVariants = 0;
1852 switch ( vol.Element()->GetEntityType() )
1854 case SMDSEntity_Hexa:
1855 case SMDSEntity_Quad_Hexa:
1856 case SMDSEntity_TriQuad_Hexa:
1857 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1858 connVariants = theHexTo5, nbTet = 5;
1860 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1862 case SMDSEntity_Pyramid:
1863 case SMDSEntity_Quad_Pyramid:
1864 connVariants = thePyraTo2; nbTet = 2;
1866 case SMDSEntity_Penta:
1867 case SMDSEntity_Quad_Penta:
1868 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1873 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1875 // check method compliancy with adjacent tetras,
1876 // all found splits must be among facets of tetras described by this method
1877 method = TSplitMethod( nbTet, connVariants[variant] );
1878 if ( hasAdjacentSplits && method._nbSplits > 0 )
1880 bool facetCreated = true;
1881 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1883 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1884 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1885 facetCreated = method.hasFacet( *facet );
1887 if ( !facetCreated )
1888 method = TSplitMethod(0); // incompatible method
1892 if ( method._nbSplits < 1 )
1894 // No standard method is applicable, use a generic solution:
1895 // each facet of a volume is split into triangles and
1896 // each of triangles and a volume barycenter form a tetrahedron.
1898 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1900 int* connectivity = new int[ maxTetConnSize + 1 ];
1901 method._connectivity = connectivity;
1902 method._ownConn = true;
1903 method._baryNode = !isHex27; // to create central node or not
1906 int baryCenInd = vol.NbNodes() - int( isHex27 );
1907 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1909 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1910 const int* nInd = vol.GetFaceNodesIndices( iF );
1911 // find common node of triangle facets of tetra to create
1912 int iCommon = 0; // index in linear numeration
1913 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1914 if ( !triaSplits.empty() )
1917 const TTriangleFacet* facet = &triaSplits.front();
1918 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1919 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1920 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1923 else if ( nbNodes > 3 && !is24TetMode )
1925 // find the best method of splitting into triangles by aspect ratio
1926 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1927 map< double, int > badness2iCommon;
1928 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1929 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1930 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1933 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1935 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1936 nodes[ iQ*((iLast-1)%nbNodes)],
1937 nodes[ iQ*((iLast )%nbNodes)]);
1938 badness += getBadRate( &tria, aspectRatio );
1940 badness2iCommon.insert( make_pair( badness, iCommon ));
1942 // use iCommon with lowest badness
1943 iCommon = badness2iCommon.begin()->second;
1945 if ( iCommon >= nbNodes )
1946 iCommon = 0; // something wrong
1948 // fill connectivity of tetrahedra based on a current face
1949 int nbTet = nbNodes - 2;
1950 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1955 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1956 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1960 method._faceBaryNode[ iF ] = 0;
1961 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1964 for ( int i = 0; i < nbTet; ++i )
1966 int i1 = i, i2 = (i+1) % nbNodes;
1967 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1968 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1969 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1970 connectivity[ connSize++ ] = faceBaryCenInd;
1971 connectivity[ connSize++ ] = baryCenInd;
1976 for ( int i = 0; i < nbTet; ++i )
1978 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1979 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1981 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1982 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1983 connectivity[ connSize++ ] = baryCenInd;
1986 method._nbSplits += nbTet;
1988 } // loop on volume faces
1990 connectivity[ connSize++ ] = -1;
1992 } // end of generic solution
1996 //=======================================================================
1998 * \brief return TSplitMethod to split haxhedron into prisms
2000 //=======================================================================
2002 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2003 const int methodFlags,
2004 const int facetToSplit)
2006 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2008 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2010 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2012 static TSplitMethod to4methods[4]; // order BT, LR, FB
2013 if ( to4methods[iF]._nbSplits == 0 )
2017 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2018 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2019 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2022 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2023 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2024 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2027 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2028 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2029 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2031 default: return to4methods[3];
2033 to4methods[iF]._nbSplits = 4;
2034 to4methods[iF]._nbCorners = 6;
2036 return to4methods[iF];
2038 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2040 TSplitMethod method;
2042 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2044 const int nbVariants = 2, nbSplits = 2;
2045 const int** connVariants = 0;
2047 case 0: connVariants = theHexTo2Prisms_BT; break;
2048 case 1: connVariants = theHexTo2Prisms_LR; break;
2049 case 2: connVariants = theHexTo2Prisms_FB; break;
2050 default: return method;
2053 // look for prisms adjacent via facetToSplit and an opposite one
2054 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2056 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2057 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2058 if ( nbNodes != 4 ) return method;
2060 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2061 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2062 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2064 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2066 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2071 // there are adjacent prism
2072 for ( int variant = 0; variant < nbVariants; ++variant )
2074 // check method compliancy with adjacent prisms,
2075 // the found prism facets must be among facets of prisms described by current method
2076 method._nbSplits = nbSplits;
2077 method._nbCorners = 6;
2078 method._connectivity = connVariants[ variant ];
2079 if ( method.hasFacet( *t ))
2084 // No adjacent prisms. Select a variant with a best aspect ratio.
2086 double badness[2] = { 0, 0 };
2087 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2088 const SMDS_MeshNode** nodes = vol.GetNodes();
2089 for ( int variant = 0; variant < nbVariants; ++variant )
2090 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2092 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2093 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2095 method._connectivity = connVariants[ variant ];
2096 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2097 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2100 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2103 badness[ variant ] += getBadRate( &tria, aspectRatio );
2105 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2107 method._nbSplits = nbSplits;
2108 method._nbCorners = 6;
2109 method._connectivity = connVariants[ iBetter ];
2114 //================================================================================
2116 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2118 //================================================================================
2120 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2121 const SMDSAbs_GeometryType geom ) const
2123 // find the tetrahedron including the three nodes of facet
2124 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2125 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2126 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2127 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2128 while ( volIt1->more() )
2130 const SMDS_MeshElement* v = volIt1->next();
2131 if ( v->GetGeomType() != geom )
2133 const int lastCornerInd = v->NbCornerNodes() - 1;
2134 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2135 continue; // medium node not allowed
2136 const int ind2 = v->GetNodeIndex( n2 );
2137 if ( ind2 < 0 || lastCornerInd < ind2 )
2139 const int ind3 = v->GetNodeIndex( n3 );
2140 if ( ind3 < 0 || lastCornerInd < ind3 )
2147 //=======================================================================
2149 * \brief A key of a face of volume
2151 //=======================================================================
2153 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2155 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2157 TIDSortedNodeSet sortedNodes;
2158 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2159 int nbNodes = vol.NbFaceNodes( iF );
2160 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2161 for ( int i = 0; i < nbNodes; i += iQ )
2162 sortedNodes.insert( fNodes[i] );
2163 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2164 first.first = (*(n++))->GetID();
2165 first.second = (*(n++))->GetID();
2166 second.first = (*(n++))->GetID();
2167 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2172 //=======================================================================
2173 //function : SplitVolumes
2174 //purpose : Split volume elements into tetrahedra or prisms.
2175 // If facet ID < 0, element is split into tetrahedra,
2176 // else a hexahedron is split into prisms so that the given facet is
2177 // split into triangles
2178 //=======================================================================
2180 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2181 const int theMethodFlags)
2183 SMDS_VolumeTool volTool;
2184 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2185 fHelper.ToFixNodeParameters( true );
2187 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2188 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2190 SMESH_SequenceOfElemPtr newNodes, newElems;
2192 // map face of volume to it's baricenrtic node
2193 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2196 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2197 for ( ; elem2facet != theElems.end(); ++elem2facet )
2199 const SMDS_MeshElement* elem = elem2facet->first;
2200 const int facetToSplit = elem2facet->second;
2201 if ( elem->GetType() != SMDSAbs_Volume )
2203 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2204 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2207 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2209 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2210 getTetraSplitMethod( volTool, theMethodFlags ) :
2211 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2212 if ( splitMethod._nbSplits < 1 ) continue;
2214 // find submesh to add new tetras to
2215 if ( !subMesh || !subMesh->Contains( elem ))
2217 int shapeID = FindShape( elem );
2218 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2219 subMesh = GetMeshDS()->MeshElements( shapeID );
2222 if ( elem->IsQuadratic() )
2225 // add quadratic links to the helper
2226 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2228 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2229 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2230 for ( int iN = 0; iN < nbN; iN += iQ )
2231 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2233 helper.SetIsQuadratic( true );
2238 helper.SetIsQuadratic( false );
2240 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2241 volTool.GetNodes() + elem->NbNodes() );
2242 helper.SetElementsOnShape( true );
2243 if ( splitMethod._baryNode )
2245 // make a node at barycenter
2246 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2247 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2248 nodes.push_back( gcNode );
2249 newNodes.Append( gcNode );
2251 if ( !splitMethod._faceBaryNode.empty() )
2253 // make or find baricentric nodes of faces
2254 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2255 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2257 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2258 volFace2BaryNode.insert
2259 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2262 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2263 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2265 nodes.push_back( iF_n->second = f_n->second );
2270 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2271 const int* volConn = splitMethod._connectivity;
2272 if ( splitMethod._nbCorners == 4 ) // tetra
2273 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2274 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2275 nodes[ volConn[1] ],
2276 nodes[ volConn[2] ],
2277 nodes[ volConn[3] ]));
2279 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2280 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2281 nodes[ volConn[1] ],
2282 nodes[ volConn[2] ],
2283 nodes[ volConn[3] ],
2284 nodes[ volConn[4] ],
2285 nodes[ volConn[5] ]));
2287 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2289 // Split faces on sides of the split volume
2291 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2292 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2294 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2295 if ( nbNodes < 4 ) continue;
2297 // find an existing face
2298 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2299 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2300 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2301 /*noMedium=*/false))
2304 helper.SetElementsOnShape( false );
2305 vector< const SMDS_MeshElement* > triangles;
2307 // find submesh to add new triangles in
2308 if ( !fSubMesh || !fSubMesh->Contains( face ))
2310 int shapeID = FindShape( face );
2311 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2313 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2314 if ( iF_n != splitMethod._faceBaryNode.end() )
2316 const SMDS_MeshNode *baryNode = iF_n->second;
2317 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2319 const SMDS_MeshNode* n1 = fNodes[iN];
2320 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2321 const SMDS_MeshNode *n3 = baryNode;
2322 if ( !volTool.IsFaceExternal( iF ))
2324 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2326 if ( fSubMesh ) // update position of the bary node on geometry
2329 subMesh->RemoveNode( baryNode, false );
2330 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2331 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2332 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2334 fHelper.SetSubShape( s );
2335 gp_XY uv( 1e100, 1e100 );
2337 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2338 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2341 // node is too far from the surface
2342 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2343 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2344 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2351 // among possible triangles create ones discribed by split method
2352 const int* nInd = volTool.GetFaceNodesIndices( iF );
2353 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2354 int iCom = 0; // common node of triangle faces to split into
2355 list< TTriangleFacet > facets;
2356 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2358 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2359 nInd[ iQ * ( (iCom+1)%nbNodes )],
2360 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2361 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2362 nInd[ iQ * ( (iCom+2)%nbNodes )],
2363 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2364 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2366 facets.push_back( t012 );
2367 facets.push_back( t023 );
2368 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2369 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2370 nInd[ iQ * ((iLast-1)%nbNodes )],
2371 nInd[ iQ * ((iLast )%nbNodes )]));
2375 list< TTriangleFacet >::iterator facet = facets.begin();
2376 if ( facet == facets.end() )
2378 for ( ; facet != facets.end(); ++facet )
2380 if ( !volTool.IsFaceExternal( iF ))
2381 swap( facet->_n2, facet->_n3 );
2382 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2383 volNodes[ facet->_n2 ],
2384 volNodes[ facet->_n3 ]));
2387 for ( int i = 0; i < triangles.size(); ++i )
2389 if ( !triangles[i] ) continue;
2391 fSubMesh->AddElement( triangles[i]);
2392 newElems.Append( triangles[i] );
2394 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2395 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2397 } // while a face based on facet nodes exists
2398 } // loop on volume faces to split them into triangles
2400 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2402 if ( geomType == SMDSEntity_TriQuad_Hexa )
2404 // remove medium nodes that could become free
2405 for ( int i = 20; i < volTool.NbNodes(); ++i )
2406 if ( volNodes[i]->NbInverseElements() == 0 )
2407 GetMeshDS()->RemoveNode( volNodes[i] );
2409 } // loop on volumes to split
2411 myLastCreatedNodes = newNodes;
2412 myLastCreatedElems = newElems;
2415 //=======================================================================
2416 //function : GetHexaFacetsToSplit
2417 //purpose : For hexahedra that will be split into prisms, finds facets to
2418 // split into triangles. Only hexahedra adjacent to the one closest
2419 // to theFacetNormal.Location() are returned.
2420 //param [in,out] theHexas - the hexahedra
2421 //param [in] theFacetNormal - facet normal
2422 //param [out] theFacets - the hexahedra and found facet IDs
2423 //=======================================================================
2425 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2426 const gp_Ax1& theFacetNormal,
2427 TFacetOfElem & theFacets)
2429 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2431 // Find a hexa closest to the location of theFacetNormal
2433 const SMDS_MeshElement* startHex;
2435 // get SMDS_ElemIteratorPtr on theHexas
2436 typedef const SMDS_MeshElement* TValue;
2437 typedef TIDSortedElemSet::iterator TSetIterator;
2438 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2439 typedef SMDS_MeshElement::GeomFilter TFilter;
2440 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2441 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2442 ( new TElemSetIter( theHexas.begin(),
2444 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2446 SMESH_ElementSearcher* searcher =
2447 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2449 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2454 throw SALOME_Exception( THIS_METHOD "startHex not found");
2457 // Select a facet of startHex by theFacetNormal
2459 SMDS_VolumeTool vTool( startHex );
2460 double norm[3], dot, maxDot = 0;
2462 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2463 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2465 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2473 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2475 // Fill theFacets starting from facetID of startHex
2477 // facets used for seach of volumes adjacent to already treated ones
2478 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2479 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2480 TFacetMap facetsToCheck;
2482 set<const SMDS_MeshNode*> facetNodes;
2483 const SMDS_MeshElement* curHex;
2485 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2489 // move in two directions from startHex via facetID
2490 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2493 int curFacet = facetID;
2494 if ( is2nd ) // do not treat startHex twice
2496 vTool.Set( curHex );
2497 if ( vTool.IsFreeFace( curFacet, &curHex ))
2503 vTool.GetFaceNodes( curFacet, facetNodes );
2504 vTool.Set( curHex );
2505 curFacet = vTool.GetFaceIndex( facetNodes );
2510 // store a facet to split
2511 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2513 theFacets.insert( make_pair( curHex, -1 ));
2516 if ( !allHex && !theHexas.count( curHex ))
2519 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2520 theFacets.insert( make_pair( curHex, curFacet ));
2521 if ( !facetIt2isNew.second )
2524 // remember not-to-split facets in facetsToCheck
2525 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2526 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2528 if ( iF == curFacet && iF == oppFacet )
2530 TVolumeFaceKey facetKey ( vTool, iF );
2531 TElemFacets elemFacet( facetIt2isNew.first, iF );
2532 pair< TFacetMap::iterator, bool > it2isnew =
2533 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2534 if ( !it2isnew.second )
2535 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2537 // pass to a volume adjacent via oppFacet
2538 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2544 // get a new curFacet
2545 vTool.GetFaceNodes( oppFacet, facetNodes );
2546 vTool.Set( curHex );
2547 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2550 } // move in two directions from startHex via facetID
2552 // Find a new startHex by facetsToCheck
2556 TFacetMap::iterator fIt = facetsToCheck.begin();
2557 while ( !startHex && fIt != facetsToCheck.end() )
2559 const TElemFacets& elemFacets = fIt->second;
2560 const SMDS_MeshElement* hex = elemFacets.first->first;
2561 int splitFacet = elemFacets.first->second;
2562 int lateralFacet = elemFacets.second;
2563 facetsToCheck.erase( fIt );
2564 fIt = facetsToCheck.begin();
2567 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2568 curHex->GetGeomType() != SMDSGeom_HEXA )
2570 if ( !allHex && !theHexas.count( curHex ))
2575 // find a facet of startHex to split
2577 set<const SMDS_MeshNode*> lateralNodes;
2578 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2579 vTool.GetFaceNodes( splitFacet, facetNodes );
2580 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2581 vTool.Set( startHex );
2582 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2584 // look for a facet of startHex having common nodes with facetNodes
2585 // but not lateralFacet
2586 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2588 if ( iF == lateralFacet )
2590 int nbCommonNodes = 0;
2591 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2592 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2593 nbCommonNodes += facetNodes.count( nn[ iN ]);
2595 if ( nbCommonNodes >= 2 )
2602 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2604 } // while ( startHex )
2607 //=======================================================================
2608 //function : AddToSameGroups
2609 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2610 //=======================================================================
2612 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2613 const SMDS_MeshElement* elemInGroups,
2614 SMESHDS_Mesh * aMesh)
2616 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2617 if (!groups.empty()) {
2618 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2619 for ( ; grIt != groups.end(); grIt++ ) {
2620 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2621 if ( group && group->Contains( elemInGroups ))
2622 group->SMDSGroup().Add( elemToAdd );
2628 //=======================================================================
2629 //function : RemoveElemFromGroups
2630 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2631 //=======================================================================
2632 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2633 SMESHDS_Mesh * aMesh)
2635 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2636 if (!groups.empty())
2638 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2639 for (; GrIt != groups.end(); GrIt++)
2641 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2642 if (!grp || grp->IsEmpty()) continue;
2643 grp->SMDSGroup().Remove(removeelem);
2648 //================================================================================
2650 * \brief Replace elemToRm by elemToAdd in the all groups
2652 //================================================================================
2654 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2655 const SMDS_MeshElement* elemToAdd,
2656 SMESHDS_Mesh * aMesh)
2658 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2659 if (!groups.empty()) {
2660 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2661 for ( ; grIt != groups.end(); grIt++ ) {
2662 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2663 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2664 group->SMDSGroup().Add( elemToAdd );
2669 //================================================================================
2671 * \brief Replace elemToRm by elemToAdd in the all groups
2673 //================================================================================
2675 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2676 const vector<const SMDS_MeshElement*>& elemToAdd,
2677 SMESHDS_Mesh * aMesh)
2679 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2680 if (!groups.empty())
2682 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2683 for ( ; grIt != groups.end(); grIt++ ) {
2684 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2685 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2686 for ( int i = 0; i < elemToAdd.size(); ++i )
2687 group->SMDSGroup().Add( elemToAdd[ i ] );
2692 //=======================================================================
2693 //function : QuadToTri
2694 //purpose : Cut quadrangles into triangles.
2695 // theCrit is used to select a diagonal to cut
2696 //=======================================================================
2698 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2699 const bool the13Diag)
2701 myLastCreatedElems.Clear();
2702 myLastCreatedNodes.Clear();
2704 MESSAGE( "::QuadToTri()" );
2706 SMESHDS_Mesh * aMesh = GetMeshDS();
2708 Handle(Geom_Surface) surface;
2709 SMESH_MesherHelper helper( *GetMesh() );
2711 TIDSortedElemSet::iterator itElem;
2712 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2713 const SMDS_MeshElement* elem = *itElem;
2714 if ( !elem || elem->GetType() != SMDSAbs_Face )
2716 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2717 if(!isquad) continue;
2719 if(elem->NbNodes()==4) {
2720 // retrieve element nodes
2721 const SMDS_MeshNode* aNodes [4];
2722 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2724 while ( itN->more() )
2725 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2727 int aShapeId = FindShape( elem );
2728 const SMDS_MeshElement* newElem1 = 0;
2729 const SMDS_MeshElement* newElem2 = 0;
2731 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2732 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2735 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2736 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2738 myLastCreatedElems.Append(newElem1);
2739 myLastCreatedElems.Append(newElem2);
2740 // put a new triangle on the same shape and add to the same groups
2743 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2744 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2746 AddToSameGroups( newElem1, elem, aMesh );
2747 AddToSameGroups( newElem2, elem, aMesh );
2748 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2749 aMesh->RemoveElement( elem );
2752 // Quadratic quadrangle
2754 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2756 // get surface elem is on
2757 int aShapeId = FindShape( elem );
2758 if ( aShapeId != helper.GetSubShapeID() ) {
2762 shape = aMesh->IndexToShape( aShapeId );
2763 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2764 TopoDS_Face face = TopoDS::Face( shape );
2765 surface = BRep_Tool::Surface( face );
2766 if ( !surface.IsNull() )
2767 helper.SetSubShape( shape );
2771 const SMDS_MeshNode* aNodes [8];
2772 const SMDS_MeshNode* inFaceNode = 0;
2773 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2775 while ( itN->more() ) {
2776 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2777 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2778 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2780 inFaceNode = aNodes[ i-1 ];
2784 // find middle point for (0,1,2,3)
2785 // and create a node in this point;
2787 if ( surface.IsNull() ) {
2789 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2793 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2796 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2798 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2800 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2801 myLastCreatedNodes.Append(newN);
2803 // create a new element
2804 const SMDS_MeshElement* newElem1 = 0;
2805 const SMDS_MeshElement* newElem2 = 0;
2807 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2808 aNodes[6], aNodes[7], newN );
2809 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2810 newN, aNodes[4], aNodes[5] );
2813 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2814 aNodes[7], aNodes[4], newN );
2815 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2816 newN, aNodes[5], aNodes[6] );
2818 myLastCreatedElems.Append(newElem1);
2819 myLastCreatedElems.Append(newElem2);
2820 // put a new triangle on the same shape and add to the same groups
2823 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2824 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2826 AddToSameGroups( newElem1, elem, aMesh );
2827 AddToSameGroups( newElem2, elem, aMesh );
2828 aMesh->RemoveElement( elem );
2835 //=======================================================================
2836 //function : getAngle
2838 //=======================================================================
2840 double getAngle(const SMDS_MeshElement * tr1,
2841 const SMDS_MeshElement * tr2,
2842 const SMDS_MeshNode * n1,
2843 const SMDS_MeshNode * n2)
2845 double angle = 2. * M_PI; // bad angle
2848 SMESH::Controls::TSequenceOfXYZ P1, P2;
2849 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2850 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2853 if(!tr1->IsQuadratic())
2854 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2856 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2857 if ( N1.SquareMagnitude() <= gp::Resolution() )
2859 if(!tr2->IsQuadratic())
2860 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2862 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2863 if ( N2.SquareMagnitude() <= gp::Resolution() )
2866 // find the first diagonal node n1 in the triangles:
2867 // take in account a diagonal link orientation
2868 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2869 for ( int t = 0; t < 2; t++ ) {
2870 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2871 int i = 0, iDiag = -1;
2872 while ( it->more()) {
2873 const SMDS_MeshElement *n = it->next();
2874 if ( n == n1 || n == n2 ) {
2878 if ( i - iDiag == 1 )
2879 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2888 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2891 angle = N1.Angle( N2 );
2896 // =================================================
2897 // class generating a unique ID for a pair of nodes
2898 // and able to return nodes by that ID
2899 // =================================================
2903 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2904 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2907 long GetLinkID (const SMDS_MeshNode * n1,
2908 const SMDS_MeshNode * n2) const
2910 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2913 bool GetNodes (const long theLinkID,
2914 const SMDS_MeshNode* & theNode1,
2915 const SMDS_MeshNode* & theNode2) const
2917 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2918 if ( !theNode1 ) return false;
2919 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2920 if ( !theNode2 ) return false;
2926 const SMESHDS_Mesh* myMesh;
2931 //=======================================================================
2932 //function : TriToQuad
2933 //purpose : Fuse neighbour triangles into quadrangles.
2934 // theCrit is used to select a neighbour to fuse with.
2935 // theMaxAngle is a max angle between element normals at which
2936 // fusion is still performed.
2937 //=======================================================================
2939 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2940 SMESH::Controls::NumericalFunctorPtr theCrit,
2941 const double theMaxAngle)
2943 myLastCreatedElems.Clear();
2944 myLastCreatedNodes.Clear();
2946 MESSAGE( "::TriToQuad()" );
2948 if ( !theCrit.get() )
2951 SMESHDS_Mesh * aMesh = GetMeshDS();
2953 // Prepare data for algo: build
2954 // 1. map of elements with their linkIDs
2955 // 2. map of linkIDs with their elements
2957 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2958 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2959 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2960 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2962 TIDSortedElemSet::iterator itElem;
2963 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2965 const SMDS_MeshElement* elem = *itElem;
2966 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2967 bool IsTria = ( elem->NbCornerNodes()==3 );
2968 if (!IsTria) continue;
2970 // retrieve element nodes
2971 const SMDS_MeshNode* aNodes [4];
2972 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2975 aNodes[ i++ ] = itN->next();
2976 aNodes[ 3 ] = aNodes[ 0 ];
2979 for ( i = 0; i < 3; i++ ) {
2980 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2981 // check if elements sharing a link can be fused
2982 itLE = mapLi_listEl.find( link );
2983 if ( itLE != mapLi_listEl.end() ) {
2984 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2986 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2987 //if ( FindShape( elem ) != FindShape( elem2 ))
2988 // continue; // do not fuse triangles laying on different shapes
2989 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2990 continue; // avoid making badly shaped quads
2991 (*itLE).second.push_back( elem );
2994 mapLi_listEl[ link ].push_back( elem );
2996 mapEl_setLi [ elem ].insert( link );
2999 // Clean the maps from the links shared by a sole element, ie
3000 // links to which only one element is bound in mapLi_listEl
3002 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3003 int nbElems = (*itLE).second.size();
3004 if ( nbElems < 2 ) {
3005 const SMDS_MeshElement* elem = (*itLE).second.front();
3006 SMESH_TLink link = (*itLE).first;
3007 mapEl_setLi[ elem ].erase( link );
3008 if ( mapEl_setLi[ elem ].empty() )
3009 mapEl_setLi.erase( elem );
3013 // Algo: fuse triangles into quadrangles
3015 while ( ! mapEl_setLi.empty() ) {
3016 // Look for the start element:
3017 // the element having the least nb of shared links
3018 const SMDS_MeshElement* startElem = 0;
3020 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3021 int nbLinks = (*itEL).second.size();
3022 if ( nbLinks < minNbLinks ) {
3023 startElem = (*itEL).first;
3024 minNbLinks = nbLinks;
3025 if ( minNbLinks == 1 )
3030 // search elements to fuse starting from startElem or links of elements
3031 // fused earlyer - startLinks
3032 list< SMESH_TLink > startLinks;
3033 while ( startElem || !startLinks.empty() ) {
3034 while ( !startElem && !startLinks.empty() ) {
3035 // Get an element to start, by a link
3036 SMESH_TLink linkId = startLinks.front();
3037 startLinks.pop_front();
3038 itLE = mapLi_listEl.find( linkId );
3039 if ( itLE != mapLi_listEl.end() ) {
3040 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3041 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3042 for ( ; itE != listElem.end() ; itE++ )
3043 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3045 mapLi_listEl.erase( itLE );
3050 // Get candidates to be fused
3051 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3052 const SMESH_TLink *link12, *link13;
3054 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3055 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3056 ASSERT( !setLi.empty() );
3057 set< SMESH_TLink >::iterator itLi;
3058 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3060 const SMESH_TLink & link = (*itLi);
3061 itLE = mapLi_listEl.find( link );
3062 if ( itLE == mapLi_listEl.end() )
3065 const SMDS_MeshElement* elem = (*itLE).second.front();
3067 elem = (*itLE).second.back();
3068 mapLi_listEl.erase( itLE );
3069 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3080 // add other links of elem to list of links to re-start from
3081 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3082 set< SMESH_TLink >::iterator it;
3083 for ( it = links.begin(); it != links.end(); it++ ) {
3084 const SMESH_TLink& link2 = (*it);
3085 if ( link2 != link )
3086 startLinks.push_back( link2 );
3090 // Get nodes of possible quadrangles
3091 const SMDS_MeshNode *n12 [4], *n13 [4];
3092 bool Ok12 = false, Ok13 = false;
3093 const SMDS_MeshNode *linkNode1, *linkNode2;
3095 linkNode1 = link12->first;
3096 linkNode2 = link12->second;
3097 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3101 linkNode1 = link13->first;
3102 linkNode2 = link13->second;
3103 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3107 // Choose a pair to fuse
3108 if ( Ok12 && Ok13 ) {
3109 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3110 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3111 double aBadRate12 = getBadRate( &quad12, theCrit );
3112 double aBadRate13 = getBadRate( &quad13, theCrit );
3113 if ( aBadRate13 < aBadRate12 )
3120 // and remove fused elems and remove links from the maps
3121 mapEl_setLi.erase( tr1 );
3124 mapEl_setLi.erase( tr2 );
3125 mapLi_listEl.erase( *link12 );
3126 if ( tr1->NbNodes() == 3 )
3128 const SMDS_MeshElement* newElem = 0;
3129 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3130 myLastCreatedElems.Append(newElem);
3131 AddToSameGroups( newElem, tr1, aMesh );
3132 int aShapeId = tr1->getshapeId();
3134 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3135 aMesh->RemoveElement( tr1 );
3136 aMesh->RemoveElement( tr2 );
3139 vector< const SMDS_MeshNode* > N1;
3140 vector< const SMDS_MeshNode* > N2;
3141 getNodesFromTwoTria(tr1,tr2,N1,N2);
3142 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3143 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3144 // i.e. first nodes from both arrays form a new diagonal
3145 const SMDS_MeshNode* aNodes[8];
3154 const SMDS_MeshElement* newElem = 0;
3155 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3156 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3157 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3159 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3161 myLastCreatedElems.Append(newElem);
3162 AddToSameGroups( newElem, tr1, aMesh );
3163 int aShapeId = tr1->getshapeId();
3165 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3166 aMesh->RemoveElement( tr1 );
3167 aMesh->RemoveElement( tr2 );
3168 // remove middle node (9)
3169 if ( N1[4]->NbInverseElements() == 0 )
3170 aMesh->RemoveNode( N1[4] );
3171 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3172 aMesh->RemoveNode( N1[6] );
3173 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3174 aMesh->RemoveNode( N2[6] );
3179 mapEl_setLi.erase( tr3 );
3180 mapLi_listEl.erase( *link13 );
3181 if ( tr1->NbNodes() == 3 ) {
3182 const SMDS_MeshElement* newElem = 0;
3183 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3184 myLastCreatedElems.Append(newElem);
3185 AddToSameGroups( newElem, tr1, aMesh );
3186 int aShapeId = tr1->getshapeId();
3188 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3189 aMesh->RemoveElement( tr1 );
3190 aMesh->RemoveElement( tr3 );
3193 vector< const SMDS_MeshNode* > N1;
3194 vector< const SMDS_MeshNode* > N2;
3195 getNodesFromTwoTria(tr1,tr3,N1,N2);
3196 // now we receive following N1 and N2 (using numeration as above image)
3197 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3198 // i.e. first nodes from both arrays form a new diagonal
3199 const SMDS_MeshNode* aNodes[8];
3208 const SMDS_MeshElement* newElem = 0;
3209 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3210 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3211 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3213 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3215 myLastCreatedElems.Append(newElem);
3216 AddToSameGroups( newElem, tr1, aMesh );
3217 int aShapeId = tr1->getshapeId();
3219 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3220 aMesh->RemoveElement( tr1 );
3221 aMesh->RemoveElement( tr3 );
3222 // remove middle node (9)
3223 if ( N1[4]->NbInverseElements() == 0 )
3224 aMesh->RemoveNode( N1[4] );
3225 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3226 aMesh->RemoveNode( N1[6] );
3227 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3228 aMesh->RemoveNode( N2[6] );
3232 // Next element to fuse: the rejected one
3234 startElem = Ok12 ? tr3 : tr2;
3236 } // if ( startElem )
3237 } // while ( startElem || !startLinks.empty() )
3238 } // while ( ! mapEl_setLi.empty() )
3244 /*#define DUMPSO(txt) \
3245 // cout << txt << endl;
3246 //=============================================================================
3250 //=============================================================================
3251 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3255 int tmp = idNodes[ i1 ];
3256 idNodes[ i1 ] = idNodes[ i2 ];
3257 idNodes[ i2 ] = tmp;
3258 gp_Pnt Ptmp = P[ i1 ];
3261 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3264 //=======================================================================
3265 //function : SortQuadNodes
3266 //purpose : Set 4 nodes of a quadrangle face in a good order.
3267 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3269 //=======================================================================
3271 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3276 for ( i = 0; i < 4; i++ ) {
3277 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3279 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3282 gp_Vec V1(P[0], P[1]);
3283 gp_Vec V2(P[0], P[2]);
3284 gp_Vec V3(P[0], P[3]);
3286 gp_Vec Cross1 = V1 ^ V2;
3287 gp_Vec Cross2 = V2 ^ V3;
3290 if (Cross1.Dot(Cross2) < 0)
3295 if (Cross1.Dot(Cross2) < 0)
3299 swap ( i, i + 1, idNodes, P );
3301 // for ( int ii = 0; ii < 4; ii++ ) {
3302 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3303 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3309 //=======================================================================
3310 //function : SortHexaNodes
3311 //purpose : Set 8 nodes of a hexahedron in a good order.
3312 // Return success status
3313 //=======================================================================
3315 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3320 DUMPSO( "INPUT: ========================================");
3321 for ( i = 0; i < 8; i++ ) {
3322 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3323 if ( !n ) return false;
3324 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3325 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3327 DUMPSO( "========================================");
3330 set<int> faceNodes; // ids of bottom face nodes, to be found
3331 set<int> checkedId1; // ids of tried 2-nd nodes
3332 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3333 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3334 int iMin, iLoop1 = 0;
3336 // Loop to try the 2-nd nodes
3338 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3340 // Find not checked 2-nd node
3341 for ( i = 1; i < 8; i++ )
3342 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3343 int id1 = idNodes[i];
3344 swap ( 1, i, idNodes, P );
3345 checkedId1.insert ( id1 );
3349 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3350 // ie that all but meybe one (id3 which is on the same face) nodes
3351 // lay on the same side from the triangle plane.
3353 bool manyInPlane = false; // more than 4 nodes lay in plane
3355 while ( ++iLoop2 < 6 ) {
3357 // get 1-2-3 plane coeffs
3358 Standard_Real A, B, C, D;
3359 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3360 if ( N.SquareMagnitude() > gp::Resolution() )
3362 gp_Pln pln ( P[0], N );
3363 pln.Coefficients( A, B, C, D );
3365 // find the node (iMin) closest to pln
3366 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3368 for ( i = 3; i < 8; i++ ) {
3369 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3370 if ( fabs( dist[i] ) < minDist ) {
3371 minDist = fabs( dist[i] );
3374 if ( fabs( dist[i] ) <= tol )
3375 idInPln.insert( idNodes[i] );
3378 // there should not be more than 4 nodes in bottom plane
3379 if ( idInPln.size() > 1 )
3381 DUMPSO( "### idInPln.size() = " << idInPln.size());
3382 // idInPlane does not contain the first 3 nodes
3383 if ( manyInPlane || idInPln.size() == 5)
3384 return false; // all nodes in one plane
3387 // set the 1-st node to be not in plane
3388 for ( i = 3; i < 8; i++ ) {
3389 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3390 DUMPSO( "### Reset 0-th node");
3391 swap( 0, i, idNodes, P );
3396 // reset to re-check second nodes
3397 leastDist = DBL_MAX;
3401 break; // from iLoop2;
3404 // check that the other 4 nodes are on the same side
3405 bool sameSide = true;
3406 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3407 for ( i = 3; sameSide && i < 8; i++ ) {
3409 sameSide = ( isNeg == dist[i] <= 0.);
3412 // keep best solution
3413 if ( sameSide && minDist < leastDist ) {
3414 leastDist = minDist;
3416 faceNodes.insert( idNodes[ 1 ] );
3417 faceNodes.insert( idNodes[ 2 ] );
3418 faceNodes.insert( idNodes[ iMin ] );
3419 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3420 << " leastDist = " << leastDist);
3421 if ( leastDist <= DBL_MIN )
3426 // set next 3-d node to check
3427 int iNext = 2 + iLoop2;
3429 DUMPSO( "Try 2-nd");
3430 swap ( 2, iNext, idNodes, P );
3432 } // while ( iLoop2 < 6 )
3435 if ( faceNodes.empty() ) return false;
3437 // Put the faceNodes in proper places
3438 for ( i = 4; i < 8; i++ ) {
3439 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3440 // find a place to put
3442 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3444 DUMPSO( "Set faceNodes");
3445 swap ( iTo, i, idNodes, P );
3450 // Set nodes of the found bottom face in good order
3451 DUMPSO( " Found bottom face: ");
3452 i = SortQuadNodes( theMesh, idNodes );
3454 gp_Pnt Ptmp = P[ i ];
3459 // for ( int ii = 0; ii < 4; ii++ ) {
3460 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3461 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3464 // Gravity center of the top and bottom faces
3465 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3466 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3468 // Get direction from the bottom to the top face
3469 gp_Vec upDir ( aGCb, aGCt );
3470 Standard_Real upDirSize = upDir.Magnitude();
3471 if ( upDirSize <= gp::Resolution() ) return false;
3474 // Assure that the bottom face normal points up
3475 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3476 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3477 if ( Nb.Dot( upDir ) < 0 ) {
3478 DUMPSO( "Reverse bottom face");
3479 swap( 1, 3, idNodes, P );
3482 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3483 Standard_Real minDist = DBL_MAX;
3484 for ( i = 4; i < 8; i++ ) {
3485 // projection of P[i] to the plane defined by P[0] and upDir
3486 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3487 Standard_Real sqDist = P[0].SquareDistance( Pp );
3488 if ( sqDist < minDist ) {
3493 DUMPSO( "Set 4-th");
3494 swap ( 4, iMin, idNodes, P );
3496 // Set nodes of the top face in good order
3497 DUMPSO( "Sort top face");
3498 i = SortQuadNodes( theMesh, &idNodes[4] );
3501 gp_Pnt Ptmp = P[ i ];
3506 // Assure that direction of the top face normal is from the bottom face
3507 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3508 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3509 if ( Nt.Dot( upDir ) < 0 ) {
3510 DUMPSO( "Reverse top face");
3511 swap( 5, 7, idNodes, P );
3514 // DUMPSO( "OUTPUT: ========================================");
3515 // for ( i = 0; i < 8; i++ ) {
3516 // float *p = ugrid->GetPoint(idNodes[i]);
3517 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3523 //================================================================================
3525 * \brief Return nodes linked to the given one
3526 * \param theNode - the node
3527 * \param linkedNodes - the found nodes
3528 * \param type - the type of elements to check
3530 * Medium nodes are ignored
3532 //================================================================================
3534 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3535 TIDSortedElemSet & linkedNodes,
3536 SMDSAbs_ElementType type )
3538 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3539 while ( elemIt->more() )
3541 const SMDS_MeshElement* elem = elemIt->next();
3542 if(elem->GetType() == SMDSAbs_0DElement)
3545 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3546 if ( elem->GetType() == SMDSAbs_Volume )
3548 SMDS_VolumeTool vol( elem );
3549 while ( nodeIt->more() ) {
3550 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3551 if ( theNode != n && vol.IsLinked( theNode, n ))
3552 linkedNodes.insert( n );
3557 for ( int i = 0; nodeIt->more(); ++i ) {
3558 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3559 if ( n == theNode ) {
3560 int iBefore = i - 1;
3562 if ( elem->IsQuadratic() ) {
3563 int nb = elem->NbNodes() / 2;
3564 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3565 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3567 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3568 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3575 //=======================================================================
3576 //function : laplacianSmooth
3577 //purpose : pulls theNode toward the center of surrounding nodes directly
3578 // connected to that node along an element edge
3579 //=======================================================================
3581 void laplacianSmooth(const SMDS_MeshNode* theNode,
3582 const Handle(Geom_Surface)& theSurface,
3583 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3585 // find surrounding nodes
3587 TIDSortedElemSet nodeSet;
3588 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3590 // compute new coodrs
3592 double coord[] = { 0., 0., 0. };
3593 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3594 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3595 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3596 if ( theSurface.IsNull() ) { // smooth in 3D
3597 coord[0] += node->X();
3598 coord[1] += node->Y();
3599 coord[2] += node->Z();
3601 else { // smooth in 2D
3602 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3603 gp_XY* uv = theUVMap[ node ];
3604 coord[0] += uv->X();
3605 coord[1] += uv->Y();
3608 int nbNodes = nodeSet.size();
3611 coord[0] /= nbNodes;
3612 coord[1] /= nbNodes;
3614 if ( !theSurface.IsNull() ) {
3615 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3616 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3617 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3623 coord[2] /= nbNodes;
3627 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3630 //=======================================================================
3631 //function : centroidalSmooth
3632 //purpose : pulls theNode toward the element-area-weighted centroid of the
3633 // surrounding elements
3634 //=======================================================================
3636 void centroidalSmooth(const SMDS_MeshNode* theNode,
3637 const Handle(Geom_Surface)& theSurface,
3638 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3640 gp_XYZ aNewXYZ(0.,0.,0.);
3641 SMESH::Controls::Area anAreaFunc;
3642 double totalArea = 0.;
3647 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3648 while ( elemIt->more() )
3650 const SMDS_MeshElement* elem = elemIt->next();
3653 gp_XYZ elemCenter(0.,0.,0.);
3654 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3655 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3656 int nn = elem->NbNodes();
3657 if(elem->IsQuadratic()) nn = nn/2;
3659 //while ( itN->more() ) {
3661 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3663 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3664 aNodePoints.push_back( aP );
3665 if ( !theSurface.IsNull() ) { // smooth in 2D
3666 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3667 gp_XY* uv = theUVMap[ aNode ];
3668 aP.SetCoord( uv->X(), uv->Y(), 0. );
3672 double elemArea = anAreaFunc.GetValue( aNodePoints );
3673 totalArea += elemArea;
3675 aNewXYZ += elemCenter * elemArea;
3677 aNewXYZ /= totalArea;
3678 if ( !theSurface.IsNull() ) {
3679 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3680 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3685 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3688 //=======================================================================
3689 //function : getClosestUV
3690 //purpose : return UV of closest projection
3691 //=======================================================================
3693 static bool getClosestUV (Extrema_GenExtPS& projector,
3694 const gp_Pnt& point,
3697 projector.Perform( point );
3698 if ( projector.IsDone() ) {
3699 double u, v, minVal = DBL_MAX;
3700 for ( int i = projector.NbExt(); i > 0; i-- )
3701 if ( projector.SquareDistance( i ) < minVal ) {
3702 minVal = projector.SquareDistance( i );
3703 projector.Point( i ).Parameter( u, v );
3705 result.SetCoord( u, v );
3711 //=======================================================================
3713 //purpose : Smooth theElements during theNbIterations or until a worst
3714 // element has aspect ratio <= theTgtAspectRatio.
3715 // Aspect Ratio varies in range [1.0, inf].
3716 // If theElements is empty, the whole mesh is smoothed.
3717 // theFixedNodes contains additionally fixed nodes. Nodes built
3718 // on edges and boundary nodes are always fixed.
3719 //=======================================================================
3721 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3722 set<const SMDS_MeshNode*> & theFixedNodes,
3723 const SmoothMethod theSmoothMethod,
3724 const int theNbIterations,
3725 double theTgtAspectRatio,
3728 myLastCreatedElems.Clear();
3729 myLastCreatedNodes.Clear();
3731 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3733 if ( theTgtAspectRatio < 1.0 )
3734 theTgtAspectRatio = 1.0;
3736 const double disttol = 1.e-16;
3738 SMESH::Controls::AspectRatio aQualityFunc;
3740 SMESHDS_Mesh* aMesh = GetMeshDS();
3742 if ( theElems.empty() ) {
3743 // add all faces to theElems
3744 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3745 while ( fIt->more() ) {
3746 const SMDS_MeshElement* face = fIt->next();
3747 theElems.insert( theElems.end(), face );
3750 // get all face ids theElems are on
3751 set< int > faceIdSet;
3752 TIDSortedElemSet::iterator itElem;
3754 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3755 int fId = FindShape( *itElem );
3756 // check that corresponding submesh exists and a shape is face
3758 faceIdSet.find( fId ) == faceIdSet.end() &&
3759 aMesh->MeshElements( fId )) {
3760 TopoDS_Shape F = aMesh->IndexToShape( fId );
3761 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3762 faceIdSet.insert( fId );
3765 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3767 // ===============================================
3768 // smooth elements on each TopoDS_Face separately
3769 // ===============================================
3771 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3772 for ( ; fId != faceIdSet.rend(); ++fId ) {
3773 // get face surface and submesh
3774 Handle(Geom_Surface) surface;
3775 SMESHDS_SubMesh* faceSubMesh = 0;
3777 double fToler2 = 0, f,l;
3778 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3779 bool isUPeriodic = false, isVPeriodic = false;
3781 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3782 surface = BRep_Tool::Surface( face );
3783 faceSubMesh = aMesh->MeshElements( *fId );
3784 fToler2 = BRep_Tool::Tolerance( face );
3785 fToler2 *= fToler2 * 10.;
3786 isUPeriodic = surface->IsUPeriodic();
3789 isVPeriodic = surface->IsVPeriodic();
3792 surface->Bounds( u1, u2, v1, v2 );
3794 // ---------------------------------------------------------
3795 // for elements on a face, find movable and fixed nodes and
3796 // compute UV for them
3797 // ---------------------------------------------------------
3798 bool checkBoundaryNodes = false;
3799 bool isQuadratic = false;
3800 set<const SMDS_MeshNode*> setMovableNodes;
3801 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3802 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3803 list< const SMDS_MeshElement* > elemsOnFace;
3805 Extrema_GenExtPS projector;
3806 GeomAdaptor_Surface surfAdaptor;
3807 if ( !surface.IsNull() ) {
3808 surfAdaptor.Load( surface );
3809 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3811 int nbElemOnFace = 0;
3812 itElem = theElems.begin();
3813 // loop on not yet smoothed elements: look for elems on a face
3814 while ( itElem != theElems.end() ) {
3815 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3816 break; // all elements found
3818 const SMDS_MeshElement* elem = *itElem;
3819 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3820 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3824 elemsOnFace.push_back( elem );
3825 theElems.erase( itElem++ );
3829 isQuadratic = elem->IsQuadratic();
3831 // get movable nodes of elem
3832 const SMDS_MeshNode* node;
3833 SMDS_TypeOfPosition posType;
3834 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3835 int nn = 0, nbn = elem->NbNodes();
3836 if(elem->IsQuadratic())
3838 while ( nn++ < nbn ) {
3839 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3840 const SMDS_PositionPtr& pos = node->GetPosition();
3841 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3842 if (posType != SMDS_TOP_EDGE &&
3843 posType != SMDS_TOP_VERTEX &&
3844 theFixedNodes.find( node ) == theFixedNodes.end())
3846 // check if all faces around the node are on faceSubMesh
3847 // because a node on edge may be bound to face
3848 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3850 if ( faceSubMesh ) {
3851 while ( eIt->more() && all ) {
3852 const SMDS_MeshElement* e = eIt->next();
3853 all = faceSubMesh->Contains( e );
3857 setMovableNodes.insert( node );
3859 checkBoundaryNodes = true;
3861 if ( posType == SMDS_TOP_3DSPACE )
3862 checkBoundaryNodes = true;
3865 if ( surface.IsNull() )
3868 // get nodes to check UV
3869 list< const SMDS_MeshNode* > uvCheckNodes;
3870 itN = elem->nodesIterator();
3871 nn = 0; nbn = elem->NbNodes();
3872 if(elem->IsQuadratic())
3874 while ( nn++ < nbn ) {
3875 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3876 if ( uvMap.find( node ) == uvMap.end() )
3877 uvCheckNodes.push_back( node );
3878 // add nodes of elems sharing node
3879 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3880 // while ( eIt->more() ) {
3881 // const SMDS_MeshElement* e = eIt->next();
3882 // if ( e != elem ) {
3883 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3884 // while ( nIt->more() ) {
3885 // const SMDS_MeshNode* n =
3886 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3887 // if ( uvMap.find( n ) == uvMap.end() )
3888 // uvCheckNodes.push_back( n );
3894 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3895 for ( ; n != uvCheckNodes.end(); ++n ) {
3898 const SMDS_PositionPtr& pos = node->GetPosition();
3899 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3901 switch ( posType ) {
3902 case SMDS_TOP_FACE: {
3903 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3904 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3907 case SMDS_TOP_EDGE: {
3908 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3909 Handle(Geom2d_Curve) pcurve;
3910 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3911 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3912 if ( !pcurve.IsNull() ) {
3913 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3914 uv = pcurve->Value( u ).XY();
3918 case SMDS_TOP_VERTEX: {
3919 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3920 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3921 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3926 // check existing UV
3927 bool project = true;
3928 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3929 double dist1 = DBL_MAX, dist2 = 0;
3930 if ( posType != SMDS_TOP_3DSPACE ) {
3931 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3932 project = dist1 > fToler2;
3934 if ( project ) { // compute new UV
3936 if ( !getClosestUV( projector, pNode, newUV )) {
3937 MESSAGE("Node Projection Failed " << node);
3941 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3943 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3945 if ( posType != SMDS_TOP_3DSPACE )
3946 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3947 if ( dist2 < dist1 )
3951 // store UV in the map
3952 listUV.push_back( uv );
3953 uvMap.insert( make_pair( node, &listUV.back() ));
3955 } // loop on not yet smoothed elements
3957 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3958 checkBoundaryNodes = true;
3960 // fix nodes on mesh boundary
3962 if ( checkBoundaryNodes ) {
3963 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3964 map< SMESH_TLink, int >::iterator link_nb;
3965 // put all elements links to linkNbMap
3966 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3967 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3968 const SMDS_MeshElement* elem = (*elemIt);
3969 int nbn = elem->NbCornerNodes();
3970 // loop on elem links: insert them in linkNbMap
3971 for ( int iN = 0; iN < nbn; ++iN ) {
3972 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3973 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3974 SMESH_TLink link( n1, n2 );
3975 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3979 // remove nodes that are in links encountered only once from setMovableNodes
3980 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3981 if ( link_nb->second == 1 ) {
3982 setMovableNodes.erase( link_nb->first.node1() );
3983 setMovableNodes.erase( link_nb->first.node2() );
3988 // -----------------------------------------------------
3989 // for nodes on seam edge, compute one more UV ( uvMap2 );
3990 // find movable nodes linked to nodes on seam and which
3991 // are to be smoothed using the second UV ( uvMap2 )
3992 // -----------------------------------------------------
3994 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3995 if ( !surface.IsNull() ) {
3996 TopExp_Explorer eExp( face, TopAbs_EDGE );
3997 for ( ; eExp.More(); eExp.Next() ) {
3998 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3999 if ( !BRep_Tool::IsClosed( edge, face ))
4001 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4002 if ( !sm ) continue;
4003 // find out which parameter varies for a node on seam
4006 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4007 if ( pcurve.IsNull() ) continue;
4008 uv1 = pcurve->Value( f );
4010 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4011 if ( pcurve.IsNull() ) continue;
4012 uv2 = pcurve->Value( f );
4013 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4015 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4016 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4018 // get nodes on seam and its vertices
4019 list< const SMDS_MeshNode* > seamNodes;
4020 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4021 while ( nSeamIt->more() ) {
4022 const SMDS_MeshNode* node = nSeamIt->next();
4023 if ( !isQuadratic || !IsMedium( node ))
4024 seamNodes.push_back( node );
4026 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4027 for ( ; vExp.More(); vExp.Next() ) {
4028 sm = aMesh->MeshElements( vExp.Current() );
4030 nSeamIt = sm->GetNodes();
4031 while ( nSeamIt->more() )
4032 seamNodes.push_back( nSeamIt->next() );
4035 // loop on nodes on seam
4036 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4037 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4038 const SMDS_MeshNode* nSeam = *noSeIt;
4039 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4040 if ( n_uv == uvMap.end() )
4043 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4044 // set the second UV
4045 listUV.push_back( *n_uv->second );
4046 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4047 if ( uvMap2.empty() )
4048 uvMap2 = uvMap; // copy the uvMap contents
4049 uvMap2[ nSeam ] = &listUV.back();
4051 // collect movable nodes linked to ones on seam in nodesNearSeam
4052 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4053 while ( eIt->more() ) {
4054 const SMDS_MeshElement* e = eIt->next();
4055 int nbUseMap1 = 0, nbUseMap2 = 0;
4056 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4057 int nn = 0, nbn = e->NbNodes();
4058 if(e->IsQuadratic()) nbn = nbn/2;
4059 while ( nn++ < nbn )
4061 const SMDS_MeshNode* n =
4062 static_cast<const SMDS_MeshNode*>( nIt->next() );
4064 setMovableNodes.find( n ) == setMovableNodes.end() )
4066 // add only nodes being closer to uv2 than to uv1
4067 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4068 0.5 * ( n->Y() + nSeam->Y() ),
4069 0.5 * ( n->Z() + nSeam->Z() ));
4071 getClosestUV( projector, pMid, uv );
4072 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4073 nodesNearSeam.insert( n );
4079 // for centroidalSmooth all element nodes must
4080 // be on one side of a seam
4081 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4082 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4084 while ( nn++ < nbn ) {
4085 const SMDS_MeshNode* n =
4086 static_cast<const SMDS_MeshNode*>( nIt->next() );
4087 setMovableNodes.erase( n );
4091 } // loop on nodes on seam
4092 } // loop on edge of a face
4093 } // if ( !face.IsNull() )
4095 if ( setMovableNodes.empty() ) {
4096 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4097 continue; // goto next face
4105 double maxRatio = -1., maxDisplacement = -1.;
4106 set<const SMDS_MeshNode*>::iterator nodeToMove;
4107 for ( it = 0; it < theNbIterations; it++ ) {
4108 maxDisplacement = 0.;
4109 nodeToMove = setMovableNodes.begin();
4110 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4111 const SMDS_MeshNode* node = (*nodeToMove);
4112 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4115 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4116 if ( theSmoothMethod == LAPLACIAN )
4117 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4119 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4121 // node displacement
4122 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4123 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4124 if ( aDispl > maxDisplacement )
4125 maxDisplacement = aDispl;
4127 // no node movement => exit
4128 //if ( maxDisplacement < 1.e-16 ) {
4129 if ( maxDisplacement < disttol ) {
4130 MESSAGE("-- no node movement --");
4134 // check elements quality
4136 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4137 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4138 const SMDS_MeshElement* elem = (*elemIt);
4139 if ( !elem || elem->GetType() != SMDSAbs_Face )
4141 SMESH::Controls::TSequenceOfXYZ aPoints;
4142 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4143 double aValue = aQualityFunc.GetValue( aPoints );
4144 if ( aValue > maxRatio )
4148 if ( maxRatio <= theTgtAspectRatio ) {
4149 MESSAGE("-- quality achived --");
4152 if (it+1 == theNbIterations) {
4153 MESSAGE("-- Iteration limit exceeded --");
4155 } // smoothing iterations
4157 MESSAGE(" Face id: " << *fId <<
4158 " Nb iterstions: " << it <<
4159 " Displacement: " << maxDisplacement <<
4160 " Aspect Ratio " << maxRatio);
4162 // ---------------------------------------
4163 // new nodes positions are computed,
4164 // record movement in DS and set new UV
4165 // ---------------------------------------
4166 nodeToMove = setMovableNodes.begin();
4167 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4168 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4169 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4170 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4171 if ( node_uv != uvMap.end() ) {
4172 gp_XY* uv = node_uv->second;
4174 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4178 // move medium nodes of quadratic elements
4181 SMESH_MesherHelper helper( *GetMesh() );
4182 helper.SetSubShape( face );
4183 vector<const SMDS_MeshNode*> nodes;
4185 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4186 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4188 const SMDS_MeshElement* QF = *elemIt;
4189 if ( QF->IsQuadratic() )
4191 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4192 SMDS_MeshElement::iterator() );
4193 nodes.push_back( nodes[0] );
4195 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4197 if ( !surface.IsNull() )
4199 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4200 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4201 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4202 xyz = surface->Value( uv.X(), uv.Y() );
4205 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4207 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4208 // we have to move a medium node
4209 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4215 } // loop on face ids
4219 //=======================================================================
4220 //function : isReverse
4221 //purpose : Return true if normal of prevNodes is not co-directied with
4222 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4223 // iNotSame is where prevNodes and nextNodes are different.
4224 // If result is true then future volume orientation is OK
4225 //=======================================================================
4227 static bool isReverse(const SMDS_MeshElement* face,
4228 const vector<const SMDS_MeshNode*>& prevNodes,
4229 const vector<const SMDS_MeshNode*>& nextNodes,
4233 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4234 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4235 gp_XYZ extrDir( pN - pP ), faceNorm;
4236 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4238 return faceNorm * extrDir < 0.0;
4241 //=======================================================================
4243 * \brief Create elements by sweeping an element
4244 * \param elem - element to sweep
4245 * \param newNodesItVec - nodes generated from each node of the element
4246 * \param newElems - generated elements
4247 * \param nbSteps - number of sweeping steps
4248 * \param srcElements - to append elem for each generated element
4250 //=======================================================================
4252 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4253 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4254 list<const SMDS_MeshElement*>& newElems,
4256 SMESH_SequenceOfElemPtr& srcElements)
4258 //MESSAGE("sweepElement " << nbSteps);
4259 SMESHDS_Mesh* aMesh = GetMeshDS();
4261 const int nbNodes = elem->NbNodes();
4262 const int nbCorners = elem->NbCornerNodes();
4263 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4264 polyhedron creation !!! */
4265 // Loop on elem nodes:
4266 // find new nodes and detect same nodes indices
4267 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4268 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4269 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4270 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4272 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4273 vector<int> sames(nbNodes);
4274 vector<bool> isSingleNode(nbNodes);
4276 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4277 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4278 const SMDS_MeshNode* node = nnIt->first;
4279 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4280 if ( listNewNodes.empty() )
4283 itNN [ iNode ] = listNewNodes.begin();
4284 prevNod[ iNode ] = node;
4285 nextNod[ iNode ] = listNewNodes.front();
4287 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4288 corner node of linear */
4289 if ( prevNod[ iNode ] != nextNod [ iNode ])
4290 nbDouble += !isSingleNode[iNode];
4292 if( iNode < nbCorners ) { // check corners only
4293 if ( prevNod[ iNode ] == nextNod [ iNode ])
4294 sames[nbSame++] = iNode;
4296 iNotSameNode = iNode;
4300 if ( nbSame == nbNodes || nbSame > 2) {
4301 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4305 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4307 // fix nodes order to have bottom normal external
4308 if ( baseType == SMDSEntity_Polygon )
4310 std::reverse( itNN.begin(), itNN.end() );
4311 std::reverse( prevNod.begin(), prevNod.end() );
4312 std::reverse( midlNod.begin(), midlNod.end() );
4313 std::reverse( nextNod.begin(), nextNod.end() );
4314 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4318 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4319 SMDS_MeshCell::applyInterlace( ind, itNN );
4320 SMDS_MeshCell::applyInterlace( ind, prevNod );
4321 SMDS_MeshCell::applyInterlace( ind, nextNod );
4322 SMDS_MeshCell::applyInterlace( ind, midlNod );
4323 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4326 sames[nbSame] = iNotSameNode;
4327 for ( int j = 0; j <= nbSame; ++j )
4328 for ( size_t i = 0; i < ind.size(); ++i )
4329 if ( ind[i] == sames[j] )
4334 iNotSameNode = sames[nbSame];
4338 else if ( elem->GetType() == SMDSAbs_Edge )
4340 // orient a new face same as adjacent one
4342 const SMDS_MeshElement* e;
4343 TIDSortedElemSet dummy;
4344 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4345 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4346 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4348 // there is an adjacent face, check order of nodes in it
4349 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4352 std::swap( itNN[0], itNN[1] );
4353 std::swap( prevNod[0], prevNod[1] );
4354 std::swap( nextNod[0], nextNod[1] );
4355 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4357 sames[0] = 1 - sames[0];
4358 iNotSameNode = 1 - iNotSameNode;
4363 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4365 iSameNode = sames[ nbSame-1 ];
4366 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4367 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4368 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4371 // make new elements
4372 for (int iStep = 0; iStep < nbSteps; iStep++ )
4375 for ( iNode = 0; iNode < nbNodes; iNode++ )
4377 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4378 nextNod[ iNode ] = *itNN[ iNode ]++;
4381 SMDS_MeshElement* aNewElem = 0;
4382 /*if(!elem->IsPoly())*/ {
4383 switch ( baseType ) {
4385 case SMDSEntity_Node: { // sweep NODE
4386 if ( nbSame == 0 ) {
4387 if ( isSingleNode[0] )
4388 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4390 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4396 case SMDSEntity_Edge: { // sweep EDGE
4397 if ( nbDouble == 0 )
4399 if ( nbSame == 0 ) // ---> quadrangle
4400 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4401 nextNod[ 1 ], nextNod[ 0 ] );
4402 else // ---> triangle
4403 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4404 nextNod[ iNotSameNode ] );
4406 else // ---> polygon
4408 vector<const SMDS_MeshNode*> poly_nodes;
4409 poly_nodes.push_back( prevNod[0] );
4410 poly_nodes.push_back( prevNod[1] );
4411 if ( prevNod[1] != nextNod[1] )
4413 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4414 poly_nodes.push_back( nextNod[1] );
4416 if ( prevNod[0] != nextNod[0] )
4418 poly_nodes.push_back( nextNod[0] );
4419 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4421 switch ( poly_nodes.size() ) {
4423 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4426 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4427 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4430 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4435 case SMDSEntity_Triangle: // TRIANGLE --->
4437 if ( nbDouble > 0 ) break;
4438 if ( nbSame == 0 ) // ---> pentahedron
4439 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4440 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4442 else if ( nbSame == 1 ) // ---> pyramid
4443 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4444 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4445 nextNod[ iSameNode ]);
4447 else // 2 same nodes: ---> tetrahedron
4448 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4449 nextNod[ iNotSameNode ]);
4452 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4456 if ( nbDouble+nbSame == 2 )
4458 if(nbSame==0) { // ---> quadratic quadrangle
4459 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4460 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4462 else { //(nbSame==1) // ---> quadratic triangle
4464 return; // medium node on axis
4466 else if(sames[0]==0)
4467 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4468 prevNod[2], midlNod[1], nextNod[2] );
4470 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4471 prevNod[2], nextNod[2], midlNod[0]);
4474 else if ( nbDouble == 3 )
4476 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4477 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4478 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4485 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4486 if ( nbDouble > 0 ) break;
4488 if ( nbSame == 0 ) // ---> hexahedron
4489 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4490 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4492 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4493 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4494 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4495 nextNod[ iSameNode ]);
4496 newElems.push_back( aNewElem );
4497 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4498 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4499 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4501 else if ( nbSame == 2 ) { // ---> pentahedron
4502 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4503 // iBeforeSame is same too
4504 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4505 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4506 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4508 // iAfterSame is same too
4509 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4510 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4511 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4515 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4516 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4517 if ( nbDouble+nbSame != 3 ) break;
4519 // ---> pentahedron with 15 nodes
4520 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4521 nextNod[0], nextNod[1], nextNod[2],
4522 prevNod[3], prevNod[4], prevNod[5],
4523 nextNod[3], nextNod[4], nextNod[5],
4524 midlNod[0], midlNod[1], midlNod[2]);
4526 else if(nbSame==1) {
4527 // ---> 2d order pyramid of 13 nodes
4528 int apex = iSameNode;
4529 int i0 = ( apex + 1 ) % nbCorners;
4530 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4534 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4535 nextNod[i0], nextNod[i1], prevNod[apex],
4536 prevNod[i01], midlNod[i0],
4537 nextNod[i01], midlNod[i1],
4538 prevNod[i1a], prevNod[i0a],
4539 nextNod[i0a], nextNod[i1a]);
4541 else if(nbSame==2) {
4542 // ---> 2d order tetrahedron of 10 nodes
4543 int n1 = iNotSameNode;
4544 int n2 = ( n1 + 1 ) % nbCorners;
4545 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4549 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4550 prevNod[n12], prevNod[n23], prevNod[n31],
4551 midlNod[n1], nextNod[n12], nextNod[n31]);
4555 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4557 if ( nbDouble != 4 ) break;
4558 // ---> hexahedron with 20 nodes
4559 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4560 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4561 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4562 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4563 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4565 else if(nbSame==1) {
4566 // ---> pyramid + pentahedron - can not be created since it is needed
4567 // additional middle node at the center of face
4568 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4571 else if( nbSame == 2 ) {
4572 if ( nbDouble != 2 ) break;
4573 // ---> 2d order Pentahedron with 15 nodes
4575 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4576 // iBeforeSame is same too
4583 // iAfterSame is same too
4593 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4594 prevNod[n4], prevNod[n5], nextNod[n5],
4595 prevNod[n12], midlNod[n2], nextNod[n12],
4596 prevNod[n45], midlNod[n5], nextNod[n45],
4597 prevNod[n14], prevNod[n25], nextNod[n25]);
4601 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4603 if( nbSame == 0 && nbDouble == 9 ) {
4604 // ---> tri-quadratic hexahedron with 27 nodes
4605 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4606 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4607 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4608 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4609 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4610 prevNod[8], // bottom center
4611 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4612 nextNod[8], // top center
4613 midlNod[8]);// elem center
4621 case SMDSEntity_Polygon: { // sweep POLYGON
4623 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4624 // ---> hexagonal prism
4625 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4626 prevNod[3], prevNod[4], prevNod[5],
4627 nextNod[0], nextNod[1], nextNod[2],
4628 nextNod[3], nextNod[4], nextNod[5]);
4632 case SMDSEntity_Ball:
4637 } // switch ( baseType )
4640 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4642 if ( baseType != SMDSEntity_Polygon )
4644 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4645 SMDS_MeshCell::applyInterlace( ind, prevNod );
4646 SMDS_MeshCell::applyInterlace( ind, nextNod );
4647 SMDS_MeshCell::applyInterlace( ind, midlNod );
4648 SMDS_MeshCell::applyInterlace( ind, itNN );
4649 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4650 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4652 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4653 vector<int> quantities (nbNodes + 2);
4654 polyedre_nodes.clear();
4658 for (int inode = 0; inode < nbNodes; inode++)
4659 polyedre_nodes.push_back( prevNod[inode] );
4660 quantities.push_back( nbNodes );
4663 polyedre_nodes.push_back( nextNod[0] );
4664 for (int inode = nbNodes; inode-1; --inode )
4665 polyedre_nodes.push_back( nextNod[inode-1] );
4666 quantities.push_back( nbNodes );
4669 for (int iface = 0; iface < nbNodes; iface++)
4671 const int prevNbNodes = polyedre_nodes.size();
4672 int inextface = (iface+1) % nbNodes;
4673 polyedre_nodes.push_back( prevNod[inextface] );
4674 polyedre_nodes.push_back( prevNod[iface] );
4675 if ( prevNod[iface] != nextNod[iface] )
4677 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4678 polyedre_nodes.push_back( nextNod[iface] );
4680 if ( prevNod[inextface] != nextNod[inextface] )
4682 polyedre_nodes.push_back( nextNod[inextface] );
4683 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4685 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4686 if ( nbFaceNodes > 2 )
4687 quantities.push_back( nbFaceNodes );
4688 else // degenerated face
4689 polyedre_nodes.resize( prevNbNodes );
4691 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4693 } // // try to create a polyherdal prism
4696 newElems.push_back( aNewElem );
4697 myLastCreatedElems.Append(aNewElem);
4698 srcElements.Append( elem );
4701 // set new prev nodes
4702 for ( iNode = 0; iNode < nbNodes; iNode++ )
4703 prevNod[ iNode ] = nextNod[ iNode ];
4708 //=======================================================================
4710 * \brief Create 1D and 2D elements around swept elements
4711 * \param mapNewNodes - source nodes and ones generated from them
4712 * \param newElemsMap - source elements and ones generated from them
4713 * \param elemNewNodesMap - nodes generated from each node of each element
4714 * \param elemSet - all swept elements
4715 * \param nbSteps - number of sweeping steps
4716 * \param srcElements - to append elem for each generated element
4718 //=======================================================================
4720 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4721 TTElemOfElemListMap & newElemsMap,
4722 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4723 TIDSortedElemSet& elemSet,
4725 SMESH_SequenceOfElemPtr& srcElements)
4727 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4728 SMESHDS_Mesh* aMesh = GetMeshDS();
4730 // Find nodes belonging to only one initial element - sweep them into edges.
4732 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4733 for ( ; nList != mapNewNodes.end(); nList++ )
4735 const SMDS_MeshNode* node =
4736 static_cast<const SMDS_MeshNode*>( nList->first );
4737 if ( newElemsMap.count( node ))
4738 continue; // node was extruded into edge
4739 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4740 int nbInitElems = 0;
4741 const SMDS_MeshElement* el = 0;
4742 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4743 while ( eIt->more() && nbInitElems < 2 ) {
4745 SMDSAbs_ElementType type = el->GetType();
4746 if ( type == SMDSAbs_Volume || type < highType ) continue;
4747 if ( type > highType ) {
4751 nbInitElems += elemSet.count(el);
4753 if ( nbInitElems < 2 ) {
4754 bool NotCreateEdge = el && el->IsMediumNode(node);
4755 if(!NotCreateEdge) {
4756 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4757 list<const SMDS_MeshElement*> newEdges;
4758 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4763 // Make a ceiling for each element ie an equal element of last new nodes.
4764 // Find free links of faces - make edges and sweep them into faces.
4766 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4767 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4768 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4770 const SMDS_MeshElement* elem = itElem->first;
4771 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4773 if(itElem->second.size()==0) continue;
4775 const bool isQuadratic = elem->IsQuadratic();
4777 if ( elem->GetType() == SMDSAbs_Edge ) {
4778 // create a ceiling edge
4779 if ( !isQuadratic ) {
4780 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4781 vecNewNodes[ 1 ]->second.back())) {
4782 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4783 vecNewNodes[ 1 ]->second.back()));
4784 srcElements.Append( elem );
4788 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4789 vecNewNodes[ 1 ]->second.back(),
4790 vecNewNodes[ 2 ]->second.back())) {
4791 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4792 vecNewNodes[ 1 ]->second.back(),
4793 vecNewNodes[ 2 ]->second.back()));
4794 srcElements.Append( elem );
4798 if ( elem->GetType() != SMDSAbs_Face )
4801 bool hasFreeLinks = false;
4803 TIDSortedElemSet avoidSet;
4804 avoidSet.insert( elem );
4806 set<const SMDS_MeshNode*> aFaceLastNodes;
4807 int iNode, nbNodes = vecNewNodes.size();
4808 if ( !isQuadratic ) {
4809 // loop on the face nodes
4810 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4811 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4812 // look for free links of the face
4813 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4814 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4815 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4816 // check if a link n1-n2 is free
4817 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4818 hasFreeLinks = true;
4819 // make a new edge and a ceiling for a new edge
4820 const SMDS_MeshElement* edge;
4821 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4822 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4823 srcElements.Append( myLastCreatedElems.Last() );
4825 n1 = vecNewNodes[ iNode ]->second.back();
4826 n2 = vecNewNodes[ iNext ]->second.back();
4827 if ( !aMesh->FindEdge( n1, n2 )) {
4828 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4829 srcElements.Append( edge );
4834 else { // elem is quadratic face
4835 int nbn = nbNodes/2;
4836 for ( iNode = 0; iNode < nbn; iNode++ ) {
4837 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4838 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4839 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4840 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4841 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4842 // check if a link is free
4843 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4844 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4845 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4846 hasFreeLinks = true;
4847 // make an edge and a ceiling for a new edge
4849 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4850 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4851 srcElements.Append( elem );
4853 n1 = vecNewNodes[ iNode ]->second.back();
4854 n2 = vecNewNodes[ iNext ]->second.back();
4855 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4856 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4857 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4858 srcElements.Append( elem );
4862 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4863 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4867 // sweep free links into faces
4869 if ( hasFreeLinks ) {
4870 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4871 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4873 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4874 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4875 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4876 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4877 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4879 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4880 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4881 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4883 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4884 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4885 std::advance( v, volNb );
4886 // find indices of free faces of a volume and their source edges
4887 list< int > freeInd;
4888 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4889 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4890 int iF, nbF = vTool.NbFaces();
4891 for ( iF = 0; iF < nbF; iF ++ ) {
4892 if (vTool.IsFreeFace( iF ) &&
4893 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4894 initNodeSet != faceNodeSet) // except an initial face
4896 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4898 if ( faceNodeSet == initNodeSetNoCenter )
4900 freeInd.push_back( iF );
4901 // find source edge of a free face iF
4902 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4903 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4904 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4905 initNodeSet.begin(), initNodeSet.end(),
4906 commonNodes.begin());
4907 if ( (*v)->IsQuadratic() )
4908 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4910 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4912 if ( !srcEdges.back() )
4914 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4915 << iF << " of volume #" << vTool.ID() << endl;
4920 if ( freeInd.empty() )
4923 // create faces for all steps;
4924 // if such a face has been already created by sweep of edge,
4925 // assure that its orientation is OK
4926 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4927 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4928 vTool.SetExternalNormal();
4929 const int nextShift = vTool.IsForward() ? +1 : -1;
4930 list< int >::iterator ind = freeInd.begin();
4931 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4932 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4934 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4935 int nbn = vTool.NbFaceNodes( *ind );
4936 const SMDS_MeshElement * f = 0;
4937 if ( nbn == 3 ) ///// triangle
4939 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4941 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4943 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4945 nodes[ 1 + nextShift ] };
4947 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4949 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4953 else if ( nbn == 4 ) ///// quadrangle
4955 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4957 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4959 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4960 nodes[ 2 ], nodes[ 2+nextShift ] };
4962 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4964 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4965 newOrder[ 2 ], newOrder[ 3 ]));
4968 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4970 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4972 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4974 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4976 nodes[2 + 2*nextShift],
4977 nodes[3 - 2*nextShift],
4979 nodes[3 + 2*nextShift]};
4981 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4983 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4991 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4993 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4994 nodes[1], nodes[3], nodes[5], nodes[7] );
4996 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4998 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4999 nodes[4 - 2*nextShift],
5001 nodes[4 + 2*nextShift],
5003 nodes[5 - 2*nextShift],
5005 nodes[5 + 2*nextShift] };
5007 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5009 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5010 newOrder[ 2 ], newOrder[ 3 ],
5011 newOrder[ 4 ], newOrder[ 5 ],
5012 newOrder[ 6 ], newOrder[ 7 ]));
5015 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5017 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5018 SMDSAbs_Face, /*noMedium=*/false);
5020 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5022 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5023 nodes[4 - 2*nextShift],
5025 nodes[4 + 2*nextShift],
5027 nodes[5 - 2*nextShift],
5029 nodes[5 + 2*nextShift],
5032 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5034 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5035 newOrder[ 2 ], newOrder[ 3 ],
5036 newOrder[ 4 ], newOrder[ 5 ],
5037 newOrder[ 6 ], newOrder[ 7 ],
5041 else //////// polygon
5043 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5044 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5046 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5048 if ( !vTool.IsForward() )
5049 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5051 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5053 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5057 while ( srcElements.Length() < myLastCreatedElems.Length() )
5058 srcElements.Append( *srcEdge );
5060 } // loop on free faces
5062 // go to the next volume
5064 while ( iVol++ < nbVolumesByStep ) v++;
5067 } // loop on volumes of one step
5068 } // sweep free links into faces
5070 // Make a ceiling face with a normal external to a volume
5072 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5073 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5074 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5076 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5077 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5078 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5081 lastVol.SetExternalNormal();
5082 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5083 int nbn = lastVol.NbFaceNodes( iF );
5084 // we do not use this->AddElement() because nodes are interlaced
5085 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5086 if ( !hasFreeLinks ||
5087 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5090 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5092 else if ( nbn == 4 )
5093 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5095 else if ( nbn == 6 && isQuadratic )
5096 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5097 nodes[1], nodes[3], nodes[5]));
5098 else if ( nbn == 7 && isQuadratic )
5099 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5100 nodes[1], nodes[3], nodes[5], nodes[6]));
5101 else if ( nbn == 8 && isQuadratic )
5102 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5103 nodes[1], nodes[3], nodes[5], nodes[7]));
5104 else if ( nbn == 9 && isQuadratic )
5105 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5106 nodes[1], nodes[3], nodes[5], nodes[7],
5109 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5111 while ( srcElements.Length() < myLastCreatedElems.Length() )
5112 srcElements.Append( elem );
5115 } // loop on swept elements
5118 //=======================================================================
5119 //function : RotationSweep
5121 //=======================================================================
5123 SMESH_MeshEditor::PGroupIDs
5124 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5125 const gp_Ax1& theAxis,
5126 const double theAngle,
5127 const int theNbSteps,
5128 const double theTol,
5129 const bool theMakeGroups,
5130 const bool theMakeWalls)
5132 myLastCreatedElems.Clear();
5133 myLastCreatedNodes.Clear();
5135 // source elements for each generated one
5136 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5138 MESSAGE( "RotationSweep()");
5140 aTrsf.SetRotation( theAxis, theAngle );
5142 aTrsf2.SetRotation( theAxis, theAngle/2. );
5144 gp_Lin aLine( theAxis );
5145 double aSqTol = theTol * theTol;
5147 SMESHDS_Mesh* aMesh = GetMeshDS();
5149 TNodeOfNodeListMap mapNewNodes;
5150 TElemOfVecOfNnlmiMap mapElemNewNodes;
5151 TTElemOfElemListMap newElemsMap;
5153 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5154 myMesh->NbFaces(ORDER_QUADRATIC) +
5155 myMesh->NbVolumes(ORDER_QUADRATIC) );
5157 TIDSortedElemSet::iterator itElem;
5158 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5159 const SMDS_MeshElement* elem = *itElem;
5160 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5162 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5163 newNodesItVec.reserve( elem->NbNodes() );
5165 // loop on elem nodes
5166 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5167 while ( itN->more() )
5169 // check if a node has been already sweeped
5170 const SMDS_MeshNode* node = cast2Node( itN->next() );
5172 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5174 aXYZ.Coord( coord[0], coord[1], coord[2] );
5175 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5177 TNodeOfNodeListMapItr nIt =
5178 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5179 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5180 if ( listNewNodes.empty() )
5182 // check if we are to create medium nodes between corner ones
5183 bool needMediumNodes = false;
5184 if ( isQuadraticMesh )
5186 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5187 while (it->more() && !needMediumNodes )
5189 const SMDS_MeshElement* invElem = it->next();
5190 if ( invElem != elem && !theElems.count( invElem )) continue;
5191 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5192 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5193 needMediumNodes = true;
5198 const SMDS_MeshNode * newNode = node;
5199 for ( int i = 0; i < theNbSteps; i++ ) {
5201 if ( needMediumNodes ) // create a medium node
5203 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5204 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5205 myLastCreatedNodes.Append(newNode);
5206 srcNodes.Append( node );
5207 listNewNodes.push_back( newNode );
5208 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5211 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5213 // create a corner node
5214 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5215 myLastCreatedNodes.Append(newNode);
5216 srcNodes.Append( node );
5217 listNewNodes.push_back( newNode );
5220 listNewNodes.push_back( newNode );
5221 // if ( needMediumNodes )
5222 // listNewNodes.push_back( newNode );
5226 newNodesItVec.push_back( nIt );
5228 // make new elements
5229 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5233 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5235 PGroupIDs newGroupIDs;
5236 if ( theMakeGroups )
5237 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5242 //=======================================================================
5243 //function : ExtrusParam
5244 //purpose : standard construction
5245 //=======================================================================
5247 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5248 const int theNbSteps,
5250 const double theTolerance):
5252 myFlags( theFlags ),
5253 myTolerance( theTolerance ),
5254 myElemsToUse( NULL )
5256 mySteps = new TColStd_HSequenceOfReal;
5257 const double stepSize = theStep.Magnitude();
5258 for (int i=1; i<=theNbSteps; i++ )
5259 mySteps->Append( stepSize );
5261 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5262 ( theTolerance > 0 ))
5264 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5268 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5272 //=======================================================================
5273 //function : ExtrusParam
5274 //purpose : steps are given explicitly
5275 //=======================================================================
5277 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5278 Handle(TColStd_HSequenceOfReal) theSteps,
5280 const double theTolerance):
5282 mySteps( theSteps ),
5283 myFlags( theFlags ),
5284 myTolerance( theTolerance ),
5285 myElemsToUse( NULL )
5287 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5288 ( theTolerance > 0 ))
5290 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5294 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose : for extrusion by normal
5301 //=======================================================================
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5304 const int theNbSteps,
5308 mySteps( new TColStd_HSequenceOfReal ),
5309 myFlags( theFlags ),
5311 myElemsToUse( NULL )
5313 for (int i = 0; i < theNbSteps; i++ )
5314 mySteps->Append( theStepSize );
5318 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5322 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5326 //=======================================================================
5327 //function : ExtrusParam::SetElementsToUse
5328 //purpose : stores elements to use for extrusion by normal, depending on
5329 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5330 //=======================================================================
5332 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5334 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5337 //=======================================================================
5338 //function : ExtrusParam::beginStepIter
5339 //purpose : prepare iteration on steps
5340 //=======================================================================
5342 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5344 myWithMediumNodes = withMediumNodes;
5348 //=======================================================================
5349 //function : ExtrusParam::moreSteps
5350 //purpose : are there more steps?
5351 //=======================================================================
5353 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5355 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5357 //=======================================================================
5358 //function : ExtrusParam::nextStep
5359 //purpose : returns the next step
5360 //=======================================================================
5362 double SMESH_MeshEditor::ExtrusParam::nextStep()
5365 if ( !myCurSteps.empty() )
5367 res = myCurSteps.back();
5368 myCurSteps.pop_back();
5370 else if ( myNextStep <= mySteps->Length() )
5372 myCurSteps.push_back( mySteps->Value( myNextStep ));
5374 if ( myWithMediumNodes )
5376 myCurSteps.back() /= 2.;
5377 myCurSteps.push_back( myCurSteps.back() );
5384 //=======================================================================
5385 //function : ExtrusParam::makeNodesByDir
5386 //purpose : create nodes for standard extrusion
5387 //=======================================================================
5389 int SMESH_MeshEditor::ExtrusParam::
5390 makeNodesByDir( SMESHDS_Mesh* mesh,
5391 const SMDS_MeshNode* srcNode,
5392 std::list<const SMDS_MeshNode*> & newNodes,
5393 const bool makeMediumNodes)
5395 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5398 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5400 p += myDir.XYZ() * nextStep();
5401 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5402 newNodes.push_back( newNode );
5407 //=======================================================================
5408 //function : ExtrusParam::makeNodesByDirAndSew
5409 //purpose : create nodes for standard extrusion with sewing
5410 //=======================================================================
5412 int SMESH_MeshEditor::ExtrusParam::
5413 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5414 const SMDS_MeshNode* srcNode,
5415 std::list<const SMDS_MeshNode*> & newNodes,
5416 const bool makeMediumNodes)
5418 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5421 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5423 P1 += myDir.XYZ() * nextStep();
5425 // try to search in sequence of existing nodes
5426 // if myNodes.Length()>0 we 'nave to use given sequence
5427 // else - use all nodes of mesh
5428 const SMDS_MeshNode * node = 0;
5429 if ( myNodes.Length() > 0 ) {
5431 for(i=1; i<=myNodes.Length(); i++) {
5432 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5433 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5435 node = myNodes.Value(i);
5441 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5442 while(itn->more()) {
5443 SMESH_TNodeXYZ P2( itn->next() );
5444 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5453 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5455 newNodes.push_back( node );
5462 //=======================================================================
5463 //function : ExtrusParam::makeNodesByNormal2D
5464 //purpose : create nodes for extrusion using normals of faces
5465 //=======================================================================
5467 int SMESH_MeshEditor::ExtrusParam::
5468 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5469 const SMDS_MeshNode* srcNode,
5470 std::list<const SMDS_MeshNode*> & newNodes,
5471 const bool makeMediumNodes)
5473 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5475 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5477 // get normals to faces sharing srcNode
5478 vector< gp_XYZ > norms, baryCenters;
5479 gp_XYZ norm, avgNorm( 0,0,0 );
5480 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5481 while ( faceIt->more() )
5483 const SMDS_MeshElement* face = faceIt->next();
5484 if ( myElemsToUse && !myElemsToUse->count( face ))
5486 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5488 norms.push_back( norm );
5490 if ( !alongAvgNorm )
5494 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5495 bc += SMESH_TNodeXYZ( nIt->next() );
5496 baryCenters.push_back( bc / nbN );
5501 if ( norms.empty() ) return 0;
5503 double normSize = avgNorm.Modulus();
5504 if ( normSize < std::numeric_limits<double>::min() )
5507 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5510 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5513 avgNorm /= normSize;
5516 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5519 double stepSize = nextStep();
5521 if ( norms.size() > 1 )
5523 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5525 // translate plane of a face
5526 baryCenters[ iF ] += norms[ iF ] * stepSize;
5528 // find point of intersection of the face plane located at baryCenters[ iF ]
5529 // and avgNorm located at pNew
5530 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5531 double dot = ( norms[ iF ] * avgNorm );
5532 if ( dot < std::numeric_limits<double>::min() )
5533 dot = stepSize * 1e-3;
5534 double step = -( norms[ iF ] * pNew + d ) / dot;
5535 pNew += step * avgNorm;
5540 pNew += stepSize * avgNorm;
5544 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5545 newNodes.push_back( newNode );
5550 //=======================================================================
5551 //function : ExtrusParam::makeNodesByNormal1D
5552 //purpose : create nodes for extrusion using normals of edges
5553 //=======================================================================
5555 int SMESH_MeshEditor::ExtrusParam::
5556 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5557 const SMDS_MeshNode* srcNode,
5558 std::list<const SMDS_MeshNode*> & newNodes,
5559 const bool makeMediumNodes)
5561 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5565 //=======================================================================
5566 //function : ExtrusionSweep
5568 //=======================================================================
5570 SMESH_MeshEditor::PGroupIDs
5571 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5572 const gp_Vec& theStep,
5573 const int theNbSteps,
5574 TTElemOfElemListMap& newElemsMap,
5576 const double theTolerance)
5578 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5579 return ExtrusionSweep( theElems, aParams, newElemsMap );
5583 //=======================================================================
5584 //function : ExtrusionSweep
5586 //=======================================================================
5588 SMESH_MeshEditor::PGroupIDs
5589 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5590 ExtrusParam& theParams,
5591 TTElemOfElemListMap& newElemsMap)
5593 myLastCreatedElems.Clear();
5594 myLastCreatedNodes.Clear();
5596 // source elements for each generated one
5597 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5599 SMESHDS_Mesh* aMesh = GetMeshDS();
5601 const int nbSteps = theParams.NbSteps();
5602 theParams.SetElementsToUse( theElems );
5604 TNodeOfNodeListMap mapNewNodes;
5605 //TNodeOfNodeVecMap mapNewNodes;
5606 TElemOfVecOfNnlmiMap mapElemNewNodes;
5607 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5609 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5610 myMesh->NbFaces(ORDER_QUADRATIC) +
5611 myMesh->NbVolumes(ORDER_QUADRATIC) );
5613 TIDSortedElemSet::iterator itElem;
5614 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5616 // check element type
5617 const SMDS_MeshElement* elem = *itElem;
5618 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5621 const size_t nbNodes = elem->NbNodes();
5622 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5623 newNodesItVec.reserve( nbNodes );
5625 // loop on elem nodes
5626 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5627 while ( itN->more() )
5629 // check if a node has been already sweeped
5630 const SMDS_MeshNode* node = cast2Node( itN->next() );
5631 TNodeOfNodeListMap::iterator nIt =
5632 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5633 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5634 if ( listNewNodes.empty() )
5638 // check if we are to create medium nodes between corner ones
5639 bool needMediumNodes = false;
5640 if ( isQuadraticMesh )
5642 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5643 while (it->more() && !needMediumNodes )
5645 const SMDS_MeshElement* invElem = it->next();
5646 if ( invElem != elem && !theElems.count( invElem )) continue;
5647 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5648 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5649 needMediumNodes = true;
5652 // create nodes for all steps
5653 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5655 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5656 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5658 myLastCreatedNodes.Append( *newNodesIt );
5659 srcNodes.Append( node );
5664 break; // newNodesItVec will be shorter than nbNodes
5667 newNodesItVec.push_back( nIt );
5669 // make new elements
5670 if ( newNodesItVec.size() == nbNodes )
5671 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5674 if ( theParams.ToMakeBoundary() ) {
5675 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbSteps, srcElems );
5677 PGroupIDs newGroupIDs;
5678 if ( theParams.ToMakeGroups() )
5679 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5684 //=======================================================================
5685 //function : ExtrusionAlongTrack
5687 //=======================================================================
5688 SMESH_MeshEditor::Extrusion_Error
5689 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5690 SMESH_subMesh* theTrack,
5691 const SMDS_MeshNode* theN1,
5692 const bool theHasAngles,
5693 list<double>& theAngles,
5694 const bool theLinearVariation,
5695 const bool theHasRefPoint,
5696 const gp_Pnt& theRefPoint,
5697 const bool theMakeGroups)
5699 MESSAGE("ExtrusionAlongTrack");
5700 myLastCreatedElems.Clear();
5701 myLastCreatedNodes.Clear();
5704 std::list<double> aPrms;
5705 TIDSortedElemSet::iterator itElem;
5708 TopoDS_Edge aTrackEdge;
5709 TopoDS_Vertex aV1, aV2;
5711 SMDS_ElemIteratorPtr aItE;
5712 SMDS_NodeIteratorPtr aItN;
5713 SMDSAbs_ElementType aTypeE;
5715 TNodeOfNodeListMap mapNewNodes;
5718 aNbE = theElements.size();
5721 return EXTR_NO_ELEMENTS;
5723 // 1.1 Track Pattern
5726 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5728 aItE = pSubMeshDS->GetElements();
5729 while ( aItE->more() ) {
5730 const SMDS_MeshElement* pE = aItE->next();
5731 aTypeE = pE->GetType();
5732 // Pattern must contain links only
5733 if ( aTypeE != SMDSAbs_Edge )
5734 return EXTR_PATH_NOT_EDGE;
5737 list<SMESH_MeshEditor_PathPoint> fullList;
5739 const TopoDS_Shape& aS = theTrack->GetSubShape();
5740 // Sub-shape for the Pattern must be an Edge or Wire
5741 if( aS.ShapeType() == TopAbs_EDGE ) {
5742 aTrackEdge = TopoDS::Edge( aS );
5743 // the Edge must not be degenerated
5744 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5745 return EXTR_BAD_PATH_SHAPE;
5746 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5747 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5748 const SMDS_MeshNode* aN1 = aItN->next();
5749 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5750 const SMDS_MeshNode* aN2 = aItN->next();
5751 // starting node must be aN1 or aN2
5752 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5753 return EXTR_BAD_STARTING_NODE;
5754 aItN = pSubMeshDS->GetNodes();
5755 while ( aItN->more() ) {
5756 const SMDS_MeshNode* pNode = aItN->next();
5757 const SMDS_EdgePosition* pEPos =
5758 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5759 double aT = pEPos->GetUParameter();
5760 aPrms.push_back( aT );
5762 //Extrusion_Error err =
5763 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5764 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5765 list< SMESH_subMesh* > LSM;
5766 TopTools_SequenceOfShape Edges;
5767 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5768 while(itSM->more()) {
5769 SMESH_subMesh* SM = itSM->next();
5771 const TopoDS_Shape& aS = SM->GetSubShape();
5774 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5775 int startNid = theN1->GetID();
5776 TColStd_MapOfInteger UsedNums;
5778 int NbEdges = Edges.Length();
5780 for(; i<=NbEdges; i++) {
5782 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5783 for(; itLSM!=LSM.end(); itLSM++) {
5785 if(UsedNums.Contains(k)) continue;
5786 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5787 SMESH_subMesh* locTrack = *itLSM;
5788 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5789 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5790 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5791 const SMDS_MeshNode* aN1 = aItN->next();
5792 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5793 const SMDS_MeshNode* aN2 = aItN->next();
5794 // starting node must be aN1 or aN2
5795 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5796 // 2. Collect parameters on the track edge
5798 aItN = locMeshDS->GetNodes();
5799 while ( aItN->more() ) {
5800 const SMDS_MeshNode* pNode = aItN->next();
5801 const SMDS_EdgePosition* pEPos =
5802 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5803 double aT = pEPos->GetUParameter();
5804 aPrms.push_back( aT );
5806 list<SMESH_MeshEditor_PathPoint> LPP;
5807 //Extrusion_Error err =
5808 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5809 LLPPs.push_back(LPP);
5811 // update startN for search following egde
5812 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5813 else startNid = aN1->GetID();
5817 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5818 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5819 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5820 for(; itPP!=firstList.end(); itPP++) {
5821 fullList.push_back( *itPP );
5823 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5824 fullList.pop_back();
5826 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5827 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5828 itPP = currList.begin();
5829 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5830 gp_Dir D1 = PP1.Tangent();
5831 gp_Dir D2 = PP2.Tangent();
5832 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5833 (D1.Z()+D2.Z())/2 ) );
5834 PP1.SetTangent(Dnew);
5835 fullList.push_back(PP1);
5837 for(; itPP!=firstList.end(); itPP++) {
5838 fullList.push_back( *itPP );
5840 PP1 = fullList.back();
5841 fullList.pop_back();
5843 // if wire not closed
5844 fullList.push_back(PP1);
5848 return EXTR_BAD_PATH_SHAPE;
5851 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5852 theHasRefPoint, theRefPoint, theMakeGroups);
5856 //=======================================================================
5857 //function : ExtrusionAlongTrack
5859 //=======================================================================
5860 SMESH_MeshEditor::Extrusion_Error
5861 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5862 SMESH_Mesh* theTrack,
5863 const SMDS_MeshNode* theN1,
5864 const bool theHasAngles,
5865 list<double>& theAngles,
5866 const bool theLinearVariation,
5867 const bool theHasRefPoint,
5868 const gp_Pnt& theRefPoint,
5869 const bool theMakeGroups)
5871 myLastCreatedElems.Clear();
5872 myLastCreatedNodes.Clear();
5875 std::list<double> aPrms;
5876 TIDSortedElemSet::iterator itElem;
5879 TopoDS_Edge aTrackEdge;
5880 TopoDS_Vertex aV1, aV2;
5882 SMDS_ElemIteratorPtr aItE;
5883 SMDS_NodeIteratorPtr aItN;
5884 SMDSAbs_ElementType aTypeE;
5886 TNodeOfNodeListMap mapNewNodes;
5889 aNbE = theElements.size();
5892 return EXTR_NO_ELEMENTS;
5894 // 1.1 Track Pattern
5897 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5899 aItE = pMeshDS->elementsIterator();
5900 while ( aItE->more() ) {
5901 const SMDS_MeshElement* pE = aItE->next();
5902 aTypeE = pE->GetType();
5903 // Pattern must contain links only
5904 if ( aTypeE != SMDSAbs_Edge )
5905 return EXTR_PATH_NOT_EDGE;
5908 list<SMESH_MeshEditor_PathPoint> fullList;
5910 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5912 if ( !theTrack->HasShapeToMesh() ) {
5913 //Mesh without shape
5914 const SMDS_MeshNode* currentNode = NULL;
5915 const SMDS_MeshNode* prevNode = theN1;
5916 std::vector<const SMDS_MeshNode*> aNodesList;
5917 aNodesList.push_back(theN1);
5918 int nbEdges = 0, conn=0;
5919 const SMDS_MeshElement* prevElem = NULL;
5920 const SMDS_MeshElement* currentElem = NULL;
5921 int totalNbEdges = theTrack->NbEdges();
5922 SMDS_ElemIteratorPtr nIt;
5925 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5926 return EXTR_BAD_STARTING_NODE;
5929 conn = nbEdgeConnectivity(theN1);
5931 return EXTR_PATH_NOT_EDGE;
5933 aItE = theN1->GetInverseElementIterator();
5934 prevElem = aItE->next();
5935 currentElem = prevElem;
5937 if(totalNbEdges == 1 ) {
5938 nIt = currentElem->nodesIterator();
5939 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5940 if(currentNode == prevNode)
5941 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5942 aNodesList.push_back(currentNode);
5944 nIt = currentElem->nodesIterator();
5945 while( nIt->more() ) {
5946 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5947 if(currentNode == prevNode)
5948 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5949 aNodesList.push_back(currentNode);
5951 //case of the closed mesh
5952 if(currentNode == theN1) {
5957 conn = nbEdgeConnectivity(currentNode);
5959 return EXTR_PATH_NOT_EDGE;
5960 }else if( conn == 1 && nbEdges > 0 ) {
5965 prevNode = currentNode;
5966 aItE = currentNode->GetInverseElementIterator();
5967 currentElem = aItE->next();
5968 if( currentElem == prevElem)
5969 currentElem = aItE->next();
5970 nIt = currentElem->nodesIterator();
5971 prevElem = currentElem;
5977 if(nbEdges != totalNbEdges)
5978 return EXTR_PATH_NOT_EDGE;
5980 TopTools_SequenceOfShape Edges;
5981 double x1,x2,y1,y2,z1,z2;
5982 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5983 int startNid = theN1->GetID();
5984 for(int i = 1; i < aNodesList.size(); i++) {
5985 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5986 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5987 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5988 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5989 list<SMESH_MeshEditor_PathPoint> LPP;
5991 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5992 LLPPs.push_back(LPP);
5993 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5994 else startNid = aNodesList[i-1]->GetID();
5998 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5999 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6000 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6001 for(; itPP!=firstList.end(); itPP++) {
6002 fullList.push_back( *itPP );
6005 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6006 SMESH_MeshEditor_PathPoint PP2;
6007 fullList.pop_back();
6009 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6010 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6011 itPP = currList.begin();
6012 PP2 = currList.front();
6013 gp_Dir D1 = PP1.Tangent();
6014 gp_Dir D2 = PP2.Tangent();
6015 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6016 (D1.Z()+D2.Z())/2 ) );
6017 PP1.SetTangent(Dnew);
6018 fullList.push_back(PP1);
6020 for(; itPP!=currList.end(); itPP++) {
6021 fullList.push_back( *itPP );
6023 PP1 = fullList.back();
6024 fullList.pop_back();
6026 fullList.push_back(PP1);
6028 } // Sub-shape for the Pattern must be an Edge or Wire
6029 else if( aS.ShapeType() == TopAbs_EDGE ) {
6030 aTrackEdge = TopoDS::Edge( aS );
6031 // the Edge must not be degenerated
6032 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6033 return EXTR_BAD_PATH_SHAPE;
6034 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6035 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6036 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6037 // starting node must be aN1 or aN2
6038 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6039 return EXTR_BAD_STARTING_NODE;
6040 aItN = pMeshDS->nodesIterator();
6041 while ( aItN->more() ) {
6042 const SMDS_MeshNode* pNode = aItN->next();
6043 if( pNode==aN1 || pNode==aN2 ) continue;
6044 const SMDS_EdgePosition* pEPos =
6045 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6046 double aT = pEPos->GetUParameter();
6047 aPrms.push_back( aT );
6049 //Extrusion_Error err =
6050 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6052 else if( aS.ShapeType() == TopAbs_WIRE ) {
6053 list< SMESH_subMesh* > LSM;
6054 TopTools_SequenceOfShape Edges;
6055 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6056 for(; eExp.More(); eExp.Next()) {
6057 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6058 if( SMESH_Algo::isDegenerated(E) ) continue;
6059 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6065 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6066 TopoDS_Vertex aVprev;
6067 TColStd_MapOfInteger UsedNums;
6068 int NbEdges = Edges.Length();
6070 for(; i<=NbEdges; i++) {
6072 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6073 for(; itLSM!=LSM.end(); itLSM++) {
6075 if(UsedNums.Contains(k)) continue;
6076 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6077 SMESH_subMesh* locTrack = *itLSM;
6078 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6079 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6080 bool aN1isOK = false, aN2isOK = false;
6081 if ( aVprev.IsNull() ) {
6082 // if previous vertex is not yet defined, it means that we in the beginning of wire
6083 // and we have to find initial vertex corresponding to starting node theN1
6084 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6085 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6086 // starting node must be aN1 or aN2
6087 aN1isOK = ( aN1 && aN1 == theN1 );
6088 aN2isOK = ( aN2 && aN2 == theN1 );
6091 // we have specified ending vertex of the previous edge on the previous iteration
6092 // and we have just to check that it corresponds to any vertex in current segment
6093 aN1isOK = aVprev.IsSame( aV1 );
6094 aN2isOK = aVprev.IsSame( aV2 );
6096 if ( !aN1isOK && !aN2isOK ) continue;
6097 // 2. Collect parameters on the track edge
6099 aItN = locMeshDS->GetNodes();
6100 while ( aItN->more() ) {
6101 const SMDS_MeshNode* pNode = aItN->next();
6102 const SMDS_EdgePosition* pEPos =
6103 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6104 double aT = pEPos->GetUParameter();
6105 aPrms.push_back( aT );
6107 list<SMESH_MeshEditor_PathPoint> LPP;
6108 //Extrusion_Error err =
6109 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6110 LLPPs.push_back(LPP);
6112 // update startN for search following egde
6113 if ( aN1isOK ) aVprev = aV2;
6118 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6119 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6120 fullList.splice( fullList.end(), firstList );
6122 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6123 fullList.pop_back();
6125 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6126 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6127 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6128 gp_Dir D1 = PP1.Tangent();
6129 gp_Dir D2 = PP2.Tangent();
6130 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6131 PP1.SetTangent(Dnew);
6132 fullList.push_back(PP1);
6133 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6134 PP1 = fullList.back();
6135 fullList.pop_back();
6137 // if wire not closed
6138 fullList.push_back(PP1);
6142 return EXTR_BAD_PATH_SHAPE;
6145 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6146 theHasRefPoint, theRefPoint, theMakeGroups);
6150 //=======================================================================
6151 //function : MakeEdgePathPoints
6152 //purpose : auxilary for ExtrusionAlongTrack
6153 //=======================================================================
6154 SMESH_MeshEditor::Extrusion_Error
6155 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6156 const TopoDS_Edge& aTrackEdge,
6158 list<SMESH_MeshEditor_PathPoint>& LPP)
6160 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6162 aTolVec2=aTolVec*aTolVec;
6164 TopoDS_Vertex aV1, aV2;
6165 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6166 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6167 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6168 // 2. Collect parameters on the track edge
6169 aPrms.push_front( aT1 );
6170 aPrms.push_back( aT2 );
6173 if( FirstIsStart ) {
6184 SMESH_MeshEditor_PathPoint aPP;
6185 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6186 std::list<double>::iterator aItD = aPrms.begin();
6187 for(; aItD != aPrms.end(); ++aItD) {
6191 aC3D->D1( aT, aP3D, aVec );
6192 aL2 = aVec.SquareMagnitude();
6193 if ( aL2 < aTolVec2 )
6194 return EXTR_CANT_GET_TANGENT;
6195 gp_Dir aTgt( aVec );
6197 aPP.SetTangent( aTgt );
6198 aPP.SetParameter( aT );
6205 //=======================================================================
6206 //function : MakeExtrElements
6207 //purpose : auxilary for ExtrusionAlongTrack
6208 //=======================================================================
6209 SMESH_MeshEditor::Extrusion_Error
6210 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
6211 list<SMESH_MeshEditor_PathPoint>& fullList,
6212 const bool theHasAngles,
6213 list<double>& theAngles,
6214 const bool theLinearVariation,
6215 const bool theHasRefPoint,
6216 const gp_Pnt& theRefPoint,
6217 const bool theMakeGroups)
6219 const int aNbTP = fullList.size();
6221 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6222 LinearAngleVariation(aNbTP-1, theAngles);
6223 // fill vector of path points with angles
6224 vector<SMESH_MeshEditor_PathPoint> aPPs;
6225 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6226 list<double>::iterator itAngles = theAngles.begin();
6227 aPPs.push_back( *itPP++ );
6228 for( ; itPP != fullList.end(); itPP++) {
6229 aPPs.push_back( *itPP );
6230 if ( theHasAngles && itAngles != theAngles.end() )
6231 aPPs.back().SetAngle( *itAngles++ );
6234 TNodeOfNodeListMap mapNewNodes;
6235 TElemOfVecOfNnlmiMap mapElemNewNodes;
6236 TTElemOfElemListMap newElemsMap;
6237 TIDSortedElemSet::iterator itElem;
6238 // source elements for each generated one
6239 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6241 // 3. Center of rotation aV0
6242 gp_Pnt aV0 = theRefPoint;
6243 if ( !theHasRefPoint )
6245 gp_XYZ aGC( 0.,0.,0. );
6246 TIDSortedElemSet newNodes;
6248 itElem = theElements.begin();
6249 for ( ; itElem != theElements.end(); itElem++ ) {
6250 const SMDS_MeshElement* elem = *itElem;
6252 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6253 while ( itN->more() ) {
6254 const SMDS_MeshElement* node = itN->next();
6255 if ( newNodes.insert( node ).second )
6256 aGC += SMESH_TNodeXYZ( node );
6259 aGC /= newNodes.size();
6261 } // if (!theHasRefPoint) {
6263 // 4. Processing the elements
6264 SMESHDS_Mesh* aMesh = GetMeshDS();
6266 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6267 // check element type
6268 const SMDS_MeshElement* elem = *itElem;
6269 SMDSAbs_ElementType aTypeE = elem->GetType();
6270 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6273 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6274 newNodesItVec.reserve( elem->NbNodes() );
6276 // loop on elem nodes
6278 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6279 while ( itN->more() )
6282 // check if a node has been already processed
6283 const SMDS_MeshNode* node =
6284 static_cast<const SMDS_MeshNode*>( itN->next() );
6285 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6286 if ( nIt == mapNewNodes.end() ) {
6287 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6288 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6291 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6292 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6293 gp_Ax1 anAx1, anAxT1T0;
6294 gp_Dir aDT1x, aDT0x, aDT1T0;
6299 aPN0 = SMESH_TNodeXYZ( node );
6301 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6303 aDT0x= aPP0.Tangent();
6304 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6306 for ( int j = 1; j < aNbTP; ++j ) {
6307 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6309 aDT1x = aPP1.Tangent();
6310 aAngle1x = aPP1.Angle();
6312 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6314 gp_Vec aV01x( aP0x, aP1x );
6315 aTrsf.SetTranslation( aV01x );
6318 aV1x = aV0x.Transformed( aTrsf );
6319 aPN1 = aPN0.Transformed( aTrsf );
6321 // rotation 1 [ T1,T0 ]
6322 aAngleT1T0=-aDT1x.Angle( aDT0x );
6323 if (fabs(aAngleT1T0) > aTolAng) {
6325 anAxT1T0.SetLocation( aV1x );
6326 anAxT1T0.SetDirection( aDT1T0 );
6327 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6329 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6333 if ( theHasAngles ) {
6334 anAx1.SetLocation( aV1x );
6335 anAx1.SetDirection( aDT1x );
6336 aTrsfRot.SetRotation( anAx1, aAngle1x );
6338 aPN1 = aPN1.Transformed( aTrsfRot );
6342 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6343 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6344 // create additional node
6345 double x = ( aPN1.X() + aPN0.X() )/2.;
6346 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6347 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6348 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6349 myLastCreatedNodes.Append(newNode);
6350 srcNodes.Append( node );
6351 listNewNodes.push_back( newNode );
6353 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6354 myLastCreatedNodes.Append(newNode);
6355 srcNodes.Append( node );
6356 listNewNodes.push_back( newNode );
6366 // if current elem is quadratic and current node is not medium
6367 // we have to check - may be it is needed to insert additional nodes
6368 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6369 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6370 if(listNewNodes.size()==aNbTP-1) {
6371 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6372 gp_XYZ P(node->X(), node->Y(), node->Z());
6373 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6375 for(i=0; i<aNbTP-1; i++) {
6376 const SMDS_MeshNode* N = *it;
6377 double x = ( N->X() + P.X() )/2.;
6378 double y = ( N->Y() + P.Y() )/2.;
6379 double z = ( N->Z() + P.Z() )/2.;
6380 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6381 srcNodes.Append( node );
6382 myLastCreatedNodes.Append(newN);
6385 P = gp_XYZ(N->X(),N->Y(),N->Z());
6387 listNewNodes.clear();
6388 for(i=0; i<2*(aNbTP-1); i++) {
6389 listNewNodes.push_back(aNodes[i]);
6395 newNodesItVec.push_back( nIt );
6397 // make new elements
6398 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6399 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6400 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6403 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6405 if ( theMakeGroups )
6406 generateGroups( srcNodes, srcElems, "extruded");
6412 //=======================================================================
6413 //function : LinearAngleVariation
6414 //purpose : auxilary for ExtrusionAlongTrack
6415 //=======================================================================
6416 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6417 list<double>& Angles)
6419 int nbAngles = Angles.size();
6420 if( nbSteps > nbAngles ) {
6421 vector<double> theAngles(nbAngles);
6422 list<double>::iterator it = Angles.begin();
6424 for(; it!=Angles.end(); it++) {
6426 theAngles[i] = (*it);
6429 double rAn2St = double( nbAngles ) / double( nbSteps );
6430 double angPrev = 0, angle;
6431 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6432 double angCur = rAn2St * ( iSt+1 );
6433 double angCurFloor = floor( angCur );
6434 double angPrevFloor = floor( angPrev );
6435 if ( angPrevFloor == angCurFloor )
6436 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6438 int iP = int( angPrevFloor );
6439 double angPrevCeil = ceil(angPrev);
6440 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6442 int iC = int( angCurFloor );
6443 if ( iC < nbAngles )
6444 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6446 iP = int( angPrevCeil );
6448 angle += theAngles[ iC ];
6450 res.push_back(angle);
6455 for(; it!=res.end(); it++)
6456 Angles.push_back( *it );
6461 //================================================================================
6463 * \brief Move or copy theElements applying theTrsf to their nodes
6464 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6465 * \param theTrsf - transformation to apply
6466 * \param theCopy - if true, create translated copies of theElems
6467 * \param theMakeGroups - if true and theCopy, create translated groups
6468 * \param theTargetMesh - mesh to copy translated elements into
6469 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6471 //================================================================================
6473 SMESH_MeshEditor::PGroupIDs
6474 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6475 const gp_Trsf& theTrsf,
6477 const bool theMakeGroups,
6478 SMESH_Mesh* theTargetMesh)
6480 myLastCreatedElems.Clear();
6481 myLastCreatedNodes.Clear();
6483 bool needReverse = false;
6484 string groupPostfix;
6485 switch ( theTrsf.Form() ) {
6487 MESSAGE("gp_PntMirror");
6489 groupPostfix = "mirrored";
6492 MESSAGE("gp_Ax1Mirror");
6493 groupPostfix = "mirrored";
6496 MESSAGE("gp_Ax2Mirror");
6498 groupPostfix = "mirrored";
6501 MESSAGE("gp_Rotation");
6502 groupPostfix = "rotated";
6504 case gp_Translation:
6505 MESSAGE("gp_Translation");
6506 groupPostfix = "translated";
6509 MESSAGE("gp_Scale");
6510 groupPostfix = "scaled";
6512 case gp_CompoundTrsf: // different scale by axis
6513 MESSAGE("gp_CompoundTrsf");
6514 groupPostfix = "scaled";
6518 needReverse = false;
6519 groupPostfix = "transformed";
6522 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6523 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6524 SMESHDS_Mesh* aMesh = GetMeshDS();
6527 // map old node to new one
6528 TNodeNodeMap nodeMap;
6530 // elements sharing moved nodes; those of them which have all
6531 // nodes mirrored but are not in theElems are to be reversed
6532 TIDSortedElemSet inverseElemSet;
6534 // source elements for each generated one
6535 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6537 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6538 TIDSortedElemSet orphanNode;
6540 if ( theElems.empty() ) // transform the whole mesh
6543 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6544 while ( eIt->more() ) theElems.insert( eIt->next() );
6546 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6547 while ( nIt->more() )
6549 const SMDS_MeshNode* node = nIt->next();
6550 if ( node->NbInverseElements() == 0)
6551 orphanNode.insert( node );
6555 // loop on elements to transform nodes : first orphan nodes then elems
6556 TIDSortedElemSet::iterator itElem;
6557 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6558 for (int i=0; i<2; i++)
6559 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6560 const SMDS_MeshElement* elem = *itElem;
6564 // loop on elem nodes
6565 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6566 while ( itN->more() ) {
6568 const SMDS_MeshNode* node = cast2Node( itN->next() );
6569 // check if a node has been already transformed
6570 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6571 nodeMap.insert( make_pair ( node, node ));
6572 if ( !n2n_isnew.second )
6576 coord[0] = node->X();
6577 coord[1] = node->Y();
6578 coord[2] = node->Z();
6579 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6580 if ( theTargetMesh ) {
6581 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6582 n2n_isnew.first->second = newNode;
6583 myLastCreatedNodes.Append(newNode);
6584 srcNodes.Append( node );
6586 else if ( theCopy ) {
6587 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6588 n2n_isnew.first->second = newNode;
6589 myLastCreatedNodes.Append(newNode);
6590 srcNodes.Append( node );
6593 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6594 // node position on shape becomes invalid
6595 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6596 ( SMDS_SpacePosition::originSpacePosition() );
6599 // keep inverse elements
6600 if ( !theCopy && !theTargetMesh && needReverse ) {
6601 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6602 while ( invElemIt->more() ) {
6603 const SMDS_MeshElement* iel = invElemIt->next();
6604 inverseElemSet.insert( iel );
6610 // either create new elements or reverse mirrored ones
6611 if ( !theCopy && !needReverse && !theTargetMesh )
6614 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6615 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6616 theElems.insert( *invElemIt );
6618 // Replicate or reverse elements
6620 std::vector<int> iForw;
6621 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6623 const SMDS_MeshElement* elem = *itElem;
6624 if ( !elem ) continue;
6626 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6627 int nbNodes = elem->NbNodes();
6628 if ( geomType == SMDSGeom_NONE ) continue; // node
6630 switch ( geomType ) {
6632 case SMDSGeom_POLYGON: // ---------------------- polygon
6634 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6636 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6637 while (itN->more()) {
6638 const SMDS_MeshNode* node =
6639 static_cast<const SMDS_MeshNode*>(itN->next());
6640 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6641 if (nodeMapIt == nodeMap.end())
6642 break; // not all nodes transformed
6644 // reverse mirrored faces and volumes
6645 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6647 poly_nodes[iNode] = (*nodeMapIt).second;
6651 if ( iNode != nbNodes )
6652 continue; // not all nodes transformed
6654 if ( theTargetMesh ) {
6655 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6656 srcElems.Append( elem );
6658 else if ( theCopy ) {
6659 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6660 srcElems.Append( elem );
6663 aMesh->ChangePolygonNodes(elem, poly_nodes);
6668 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6670 const SMDS_VtkVolume* aPolyedre =
6671 dynamic_cast<const SMDS_VtkVolume*>( elem );
6673 MESSAGE("Warning: bad volumic element");
6677 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6678 vector<int> quantities; quantities.reserve( nbNodes );
6680 bool allTransformed = true;
6681 int nbFaces = aPolyedre->NbFaces();
6682 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6683 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6684 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6685 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6686 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6687 if (nodeMapIt == nodeMap.end()) {
6688 allTransformed = false; // not all nodes transformed
6690 poly_nodes.push_back((*nodeMapIt).second);
6692 if ( needReverse && allTransformed )
6693 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6695 quantities.push_back(nbFaceNodes);
6697 if ( !allTransformed )
6698 continue; // not all nodes transformed
6700 if ( theTargetMesh ) {
6701 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6702 srcElems.Append( elem );
6704 else if ( theCopy ) {
6705 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6706 srcElems.Append( elem );
6709 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6714 case SMDSGeom_BALL: // -------------------- Ball
6716 if ( !theCopy && !theTargetMesh ) continue;
6718 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6719 if (nodeMapIt == nodeMap.end())
6720 continue; // not all nodes transformed
6722 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6723 if ( theTargetMesh ) {
6724 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6725 srcElems.Append( elem );
6728 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6729 srcElems.Append( elem );
6734 default: // ----------------------- Regular elements
6736 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6737 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6738 const std::vector<int>& i = needReverse ? iRev : iForw;
6740 // find transformed nodes
6741 vector<const SMDS_MeshNode*> nodes(nbNodes);
6743 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6744 while ( itN->more() ) {
6745 const SMDS_MeshNode* node =
6746 static_cast<const SMDS_MeshNode*>( itN->next() );
6747 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6748 if ( nodeMapIt == nodeMap.end() )
6749 break; // not all nodes transformed
6750 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6752 if ( iNode != nbNodes )
6753 continue; // not all nodes transformed
6755 if ( theTargetMesh ) {
6756 if ( SMDS_MeshElement* copy =
6757 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6758 myLastCreatedElems.Append( copy );
6759 srcElems.Append( elem );
6762 else if ( theCopy ) {
6763 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6764 srcElems.Append( elem );
6767 // reverse element as it was reversed by transformation
6769 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6771 } // switch ( geomType )
6773 } // loop on elements
6775 PGroupIDs newGroupIDs;
6777 if ( ( theMakeGroups && theCopy ) ||
6778 ( theMakeGroups && theTargetMesh ) )
6779 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6784 //=======================================================================
6786 * \brief Create groups of elements made during transformation
6787 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6788 * \param elemGens - elements making corresponding myLastCreatedElems
6789 * \param postfix - to append to names of new groups
6790 * \param targetMesh - mesh to create groups in
6791 * \param topPresent - is there "top" elements that are created by sweeping
6793 //=======================================================================
6795 SMESH_MeshEditor::PGroupIDs
6796 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6797 const SMESH_SequenceOfElemPtr& elemGens,
6798 const std::string& postfix,
6799 SMESH_Mesh* targetMesh,
6800 const bool topPresent)
6802 PGroupIDs newGroupIDs( new list<int> );
6803 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6805 // Sort existing groups by types and collect their names
6807 // containers to store an old group and generated new ones;
6808 // 1st new group is for result elems of different type than a source one;
6809 // 2nd new group is for same type result elems ("top" group at extrusion)
6811 using boost::make_tuple;
6812 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6813 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6814 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6816 set< string > groupNames;
6818 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6819 if ( !groupIt->more() ) return newGroupIDs;
6821 int newGroupID = mesh->GetGroupIds().back()+1;
6822 while ( groupIt->more() )
6824 SMESH_Group * group = groupIt->next();
6825 if ( !group ) continue;
6826 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6827 if ( !groupDS || groupDS->IsEmpty() ) continue;
6828 groupNames.insert ( group->GetName() );
6829 groupDS->SetStoreName( group->GetName() );
6830 const SMDSAbs_ElementType type = groupDS->GetType();
6831 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6832 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6833 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6834 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6837 // Loop on nodes and elements to add them in new groups
6839 vector< const SMDS_MeshElement* > resultElems;
6840 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6842 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6843 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6844 if ( gens.Length() != elems.Length() )
6845 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6847 // loop on created elements
6848 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6850 const SMDS_MeshElement* sourceElem = gens( iElem );
6851 if ( !sourceElem ) {
6852 MESSAGE("generateGroups(): NULL source element");
6855 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6856 if ( groupsOldNew.empty() ) { // no groups of this type at all
6857 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6858 ++iElem; // skip all elements made by sourceElem
6861 // collect all elements made by the iElem-th sourceElem
6862 resultElems.clear();
6863 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6864 if ( resElem != sourceElem )
6865 resultElems.push_back( resElem );
6866 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6867 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6868 if ( resElem != sourceElem )
6869 resultElems.push_back( resElem );
6871 const SMDS_MeshElement* topElem = 0;
6872 if ( isNodes ) // there must be a top element
6874 topElem = resultElems.back();
6875 resultElems.pop_back();
6879 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6880 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6881 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6883 topElem = *resElemIt;
6884 *resElemIt = 0; // erase *resElemIt
6888 // add resultElems to groups originted from ones the sourceElem belongs to
6889 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6890 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6892 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6893 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6895 // fill in a new group
6896 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6897 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6898 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6900 newGroup.Add( *resElemIt );
6902 // fill a "top" group
6905 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6906 newTopGroup.Add( topElem );
6910 } // loop on created elements
6911 }// loop on nodes and elements
6913 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6915 list<int> topGrouIds;
6916 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6918 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6919 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6920 orderedOldNewGroups[i]->get<2>() };
6921 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6923 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6924 if ( newGroupDS->IsEmpty() )
6926 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6931 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6934 const bool isTop = ( topPresent &&
6935 newGroupDS->GetType() == oldGroupDS->GetType() &&
6938 string name = oldGroupDS->GetStoreName();
6939 { // remove trailing whitespaces (issue 22599)
6940 size_t size = name.size();
6941 while ( size > 1 && isspace( name[ size-1 ]))
6943 if ( size != name.size() )
6945 name.resize( size );
6946 oldGroupDS->SetStoreName( name.c_str() );
6949 if ( !targetMesh ) {
6950 string suffix = ( isTop ? "top": postfix.c_str() );
6954 while ( !groupNames.insert( name ).second ) // name exists
6955 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6960 newGroupDS->SetStoreName( name.c_str() );
6962 // make a SMESH_Groups
6963 mesh->AddGroup( newGroupDS );
6965 topGrouIds.push_back( newGroupDS->GetID() );
6967 newGroupIDs->push_back( newGroupDS->GetID() );
6971 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6976 //================================================================================
6978 * \brief Return list of group of nodes close to each other within theTolerance
6979 * Search among theNodes or in the whole mesh if theNodes is empty using
6980 * an Octree algorithm
6982 //================================================================================
6984 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6985 const double theTolerance,
6986 TListOfListOfNodes & theGroupsOfNodes)
6988 myLastCreatedElems.Clear();
6989 myLastCreatedNodes.Clear();
6991 if ( theNodes.empty() )
6992 { // get all nodes in the mesh
6993 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6994 while ( nIt->more() )
6995 theNodes.insert( theNodes.end(),nIt->next());
6998 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7001 //=======================================================================
7002 //function : SimplifyFace
7004 //=======================================================================
7006 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7007 vector<const SMDS_MeshNode *>& poly_nodes,
7008 vector<int>& quantities) const
7010 int nbNodes = faceNodes.size();
7015 set<const SMDS_MeshNode*> nodeSet;
7017 // get simple seq of nodes
7018 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7019 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7020 int iSimple = 0, nbUnique = 0;
7022 simpleNodes[iSimple++] = faceNodes[0];
7024 for (int iCur = 1; iCur < nbNodes; iCur++) {
7025 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7026 simpleNodes[iSimple++] = faceNodes[iCur];
7027 if (nodeSet.insert( faceNodes[iCur] ).second)
7031 int nbSimple = iSimple;
7032 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7042 bool foundLoop = (nbSimple > nbUnique);
7045 set<const SMDS_MeshNode*> loopSet;
7046 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7047 const SMDS_MeshNode* n = simpleNodes[iSimple];
7048 if (!loopSet.insert( n ).second) {
7052 int iC = 0, curLast = iSimple;
7053 for (; iC < curLast; iC++) {
7054 if (simpleNodes[iC] == n) break;
7056 int loopLen = curLast - iC;
7058 // create sub-element
7060 quantities.push_back(loopLen);
7061 for (; iC < curLast; iC++) {
7062 poly_nodes.push_back(simpleNodes[iC]);
7065 // shift the rest nodes (place from the first loop position)
7066 for (iC = curLast + 1; iC < nbSimple; iC++) {
7067 simpleNodes[iC - loopLen] = simpleNodes[iC];
7069 nbSimple -= loopLen;
7072 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7073 } // while (foundLoop)
7077 quantities.push_back(iSimple);
7078 for (int i = 0; i < iSimple; i++)
7079 poly_nodes.push_back(simpleNodes[i]);
7085 //=======================================================================
7086 //function : MergeNodes
7087 //purpose : In each group, the cdr of nodes are substituted by the first one
7089 //=======================================================================
7091 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7093 MESSAGE("MergeNodes");
7094 myLastCreatedElems.Clear();
7095 myLastCreatedNodes.Clear();
7097 SMESHDS_Mesh* aMesh = GetMeshDS();
7099 TNodeNodeMap nodeNodeMap; // node to replace - new node
7100 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7101 list< int > rmElemIds, rmNodeIds;
7103 // Fill nodeNodeMap and elems
7105 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7106 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7107 list<const SMDS_MeshNode*>& nodes = *grIt;
7108 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7109 const SMDS_MeshNode* nToKeep = *nIt;
7110 //MESSAGE("node to keep " << nToKeep->GetID());
7111 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7112 const SMDS_MeshNode* nToRemove = *nIt;
7113 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7114 if ( nToRemove != nToKeep ) {
7115 //MESSAGE(" node to remove " << nToRemove->GetID());
7116 rmNodeIds.push_back( nToRemove->GetID() );
7117 AddToSameGroups( nToKeep, nToRemove, aMesh );
7118 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7119 // after MergeNodes() w/o creating node in place of merged ones.
7120 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7121 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7122 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7123 sm->SetIsAlwaysComputed( true );
7126 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7127 while ( invElemIt->more() ) {
7128 const SMDS_MeshElement* elem = invElemIt->next();
7133 // Change element nodes or remove an element
7135 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7136 for ( ; eIt != elems.end(); eIt++ ) {
7137 const SMDS_MeshElement* elem = *eIt;
7138 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7139 int nbNodes = elem->NbNodes();
7140 int aShapeId = FindShape( elem );
7142 set<const SMDS_MeshNode*> nodeSet;
7143 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7144 int iUnique = 0, iCur = 0, nbRepl = 0;
7145 vector<int> iRepl( nbNodes );
7147 // get new seq of nodes
7148 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7149 while ( itN->more() ) {
7150 const SMDS_MeshNode* n =
7151 static_cast<const SMDS_MeshNode*>( itN->next() );
7153 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7154 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7156 // BUG 0020185: begin
7158 bool stopRecur = false;
7159 set<const SMDS_MeshNode*> nodesRecur;
7160 nodesRecur.insert(n);
7161 while (!stopRecur) {
7162 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7163 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7164 n = (*nnIt_i).second;
7165 if (!nodesRecur.insert(n).second) {
7166 // error: recursive dependancy
7176 curNodes[ iCur ] = n;
7177 bool isUnique = nodeSet.insert( n ).second;
7179 uniqueNodes[ iUnique++ ] = n;
7181 iRepl[ nbRepl++ ] = iCur;
7185 // Analyse element topology after replacement
7188 int nbUniqueNodes = nodeSet.size();
7189 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7190 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7191 // Polygons and Polyhedral volumes
7192 if (elem->IsPoly()) {
7194 if (elem->GetType() == SMDSAbs_Face) {
7196 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7198 for (; inode < nbNodes; inode++) {
7199 face_nodes[inode] = curNodes[inode];
7202 vector<const SMDS_MeshNode *> polygons_nodes;
7203 vector<int> quantities;
7204 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7207 for (int iface = 0; iface < nbNew; iface++) {
7208 int nbNodes = quantities[iface];
7209 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7210 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7211 poly_nodes[ii] = polygons_nodes[inode];
7213 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7214 myLastCreatedElems.Append(newElem);
7216 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7219 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7220 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7221 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7223 if (nbNew > 0) quid = nbNew - 1;
7224 vector<int> newquant(quantities.begin()+quid, quantities.end());
7225 const SMDS_MeshElement* newElem = 0;
7226 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7227 myLastCreatedElems.Append(newElem);
7228 if ( aShapeId && newElem )
7229 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7230 rmElemIds.push_back(elem->GetID());
7233 rmElemIds.push_back(elem->GetID());
7237 else if (elem->GetType() == SMDSAbs_Volume) {
7238 // Polyhedral volume
7239 if (nbUniqueNodes < 4) {
7240 rmElemIds.push_back(elem->GetID());
7243 // each face has to be analyzed in order to check volume validity
7244 const SMDS_VtkVolume* aPolyedre =
7245 dynamic_cast<const SMDS_VtkVolume*>( elem );
7247 int nbFaces = aPolyedre->NbFaces();
7249 vector<const SMDS_MeshNode *> poly_nodes;
7250 vector<int> quantities;
7252 for (int iface = 1; iface <= nbFaces; iface++) {
7253 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7254 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7256 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7257 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7258 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7259 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7260 faceNode = (*nnIt).second;
7262 faceNodes[inode - 1] = faceNode;
7265 SimplifyFace(faceNodes, poly_nodes, quantities);
7268 if (quantities.size() > 3) {
7269 // to be done: remove coincident faces
7272 if (quantities.size() > 3)
7274 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7275 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7276 const SMDS_MeshElement* newElem = 0;
7277 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7278 myLastCreatedElems.Append(newElem);
7279 if ( aShapeId && newElem )
7280 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7281 rmElemIds.push_back(elem->GetID());
7285 rmElemIds.push_back(elem->GetID());
7296 // TODO not all the possible cases are solved. Find something more generic?
7297 switch ( nbNodes ) {
7298 case 2: ///////////////////////////////////// EDGE
7299 isOk = false; break;
7300 case 3: ///////////////////////////////////// TRIANGLE
7301 isOk = false; break;
7303 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7305 else { //////////////////////////////////// QUADRANGLE
7306 if ( nbUniqueNodes < 3 )
7308 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7309 isOk = false; // opposite nodes stick
7310 //MESSAGE("isOk " << isOk);
7313 case 6: ///////////////////////////////////// PENTAHEDRON
7314 if ( nbUniqueNodes == 4 ) {
7315 // ---------------------------------> tetrahedron
7317 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7318 // all top nodes stick: reverse a bottom
7319 uniqueNodes[ 0 ] = curNodes [ 1 ];
7320 uniqueNodes[ 1 ] = curNodes [ 0 ];
7322 else if (nbRepl == 3 &&
7323 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7324 // all bottom nodes stick: set a top before
7325 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7326 uniqueNodes[ 0 ] = curNodes [ 3 ];
7327 uniqueNodes[ 1 ] = curNodes [ 4 ];
7328 uniqueNodes[ 2 ] = curNodes [ 5 ];
7330 else if (nbRepl == 4 &&
7331 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7332 // a lateral face turns into a line: reverse a bottom
7333 uniqueNodes[ 0 ] = curNodes [ 1 ];
7334 uniqueNodes[ 1 ] = curNodes [ 0 ];
7339 else if ( nbUniqueNodes == 5 ) {
7340 // PENTAHEDRON --------------------> 2 tetrahedrons
7341 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7342 // a bottom node sticks with a linked top one
7344 SMDS_MeshElement* newElem =
7345 aMesh->AddVolume(curNodes[ 3 ],
7348 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7349 myLastCreatedElems.Append(newElem);
7351 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7352 // 2. : reverse a bottom
7353 uniqueNodes[ 0 ] = curNodes [ 1 ];
7354 uniqueNodes[ 1 ] = curNodes [ 0 ];
7364 if(elem->IsQuadratic()) { // Quadratic quadrangle
7376 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7379 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7381 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7382 uniqueNodes[0] = curNodes[0];
7383 uniqueNodes[1] = curNodes[2];
7384 uniqueNodes[2] = curNodes[3];
7385 uniqueNodes[3] = curNodes[5];
7386 uniqueNodes[4] = curNodes[6];
7387 uniqueNodes[5] = curNodes[7];
7390 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7391 uniqueNodes[0] = curNodes[0];
7392 uniqueNodes[1] = curNodes[1];
7393 uniqueNodes[2] = curNodes[2];
7394 uniqueNodes[3] = curNodes[4];
7395 uniqueNodes[4] = curNodes[5];
7396 uniqueNodes[5] = curNodes[6];
7399 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7400 uniqueNodes[0] = curNodes[1];
7401 uniqueNodes[1] = curNodes[2];
7402 uniqueNodes[2] = curNodes[3];
7403 uniqueNodes[3] = curNodes[5];
7404 uniqueNodes[4] = curNodes[6];
7405 uniqueNodes[5] = curNodes[0];
7408 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7409 uniqueNodes[0] = curNodes[0];
7410 uniqueNodes[1] = curNodes[1];
7411 uniqueNodes[2] = curNodes[3];
7412 uniqueNodes[3] = curNodes[4];
7413 uniqueNodes[4] = curNodes[6];
7414 uniqueNodes[5] = curNodes[7];
7417 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7418 uniqueNodes[0] = curNodes[0];
7419 uniqueNodes[1] = curNodes[2];
7420 uniqueNodes[2] = curNodes[3];
7421 uniqueNodes[3] = curNodes[1];
7422 uniqueNodes[4] = curNodes[6];
7423 uniqueNodes[5] = curNodes[7];
7426 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7427 uniqueNodes[0] = curNodes[0];
7428 uniqueNodes[1] = curNodes[1];
7429 uniqueNodes[2] = curNodes[2];
7430 uniqueNodes[3] = curNodes[4];
7431 uniqueNodes[4] = curNodes[5];
7432 uniqueNodes[5] = curNodes[7];
7435 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7436 uniqueNodes[0] = curNodes[0];
7437 uniqueNodes[1] = curNodes[1];
7438 uniqueNodes[2] = curNodes[3];
7439 uniqueNodes[3] = curNodes[4];
7440 uniqueNodes[4] = curNodes[2];
7441 uniqueNodes[5] = curNodes[7];
7444 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7445 uniqueNodes[0] = curNodes[0];
7446 uniqueNodes[1] = curNodes[1];
7447 uniqueNodes[2] = curNodes[2];
7448 uniqueNodes[3] = curNodes[4];
7449 uniqueNodes[4] = curNodes[5];
7450 uniqueNodes[5] = curNodes[3];
7455 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7458 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7462 //////////////////////////////////// HEXAHEDRON
7464 SMDS_VolumeTool hexa (elem);
7465 hexa.SetExternalNormal();
7466 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7467 //////////////////////// HEX ---> 1 tetrahedron
7468 for ( int iFace = 0; iFace < 6; iFace++ ) {
7469 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7470 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7471 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7472 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7473 // one face turns into a point ...
7474 int iOppFace = hexa.GetOppFaceIndex( iFace );
7475 ind = hexa.GetFaceNodesIndices( iOppFace );
7477 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7478 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7481 if ( nbStick == 1 ) {
7482 // ... and the opposite one - into a triangle.
7484 ind = hexa.GetFaceNodesIndices( iFace );
7485 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7492 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7493 //////////////////////// HEX ---> 1 prism
7494 int nbTria = 0, iTria[3];
7495 const int *ind; // indices of face nodes
7496 // look for triangular faces
7497 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7498 ind = hexa.GetFaceNodesIndices( iFace );
7499 TIDSortedNodeSet faceNodes;
7500 for ( iCur = 0; iCur < 4; iCur++ )
7501 faceNodes.insert( curNodes[ind[iCur]] );
7502 if ( faceNodes.size() == 3 )
7503 iTria[ nbTria++ ] = iFace;
7505 // check if triangles are opposite
7506 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7509 // set nodes of the bottom triangle
7510 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7512 for ( iCur = 0; iCur < 4; iCur++ )
7513 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7514 indB.push_back( ind[iCur] );
7515 if ( !hexa.IsForward() )
7516 std::swap( indB[0], indB[2] );
7517 for ( iCur = 0; iCur < 3; iCur++ )
7518 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7519 // set nodes of the top triangle
7520 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7521 for ( iCur = 0; iCur < 3; ++iCur )
7522 for ( int j = 0; j < 4; ++j )
7523 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7525 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7531 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7532 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7533 for ( int iFace = 0; iFace < 6; iFace++ ) {
7534 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7535 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7536 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7537 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7538 // one face turns into a point ...
7539 int iOppFace = hexa.GetOppFaceIndex( iFace );
7540 ind = hexa.GetFaceNodesIndices( iOppFace );
7542 iUnique = 2; // reverse a tetrahedron 1 bottom
7543 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7544 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7546 else if ( iUnique >= 0 )
7547 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7549 if ( nbStick == 0 ) {
7550 // ... and the opposite one is a quadrangle
7552 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7553 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7556 SMDS_MeshElement* newElem =
7557 aMesh->AddVolume(curNodes[ind[ 0 ]],
7560 curNodes[indTop[ 0 ]]);
7561 myLastCreatedElems.Append(newElem);
7563 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7570 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7571 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7572 // find indices of quad and tri faces
7573 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7574 for ( iFace = 0; iFace < 6; iFace++ ) {
7575 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7577 for ( iCur = 0; iCur < 4; iCur++ )
7578 nodeSet.insert( curNodes[ind[ iCur ]] );
7579 nbUniqueNodes = nodeSet.size();
7580 if ( nbUniqueNodes == 3 )
7581 iTriFace[ nbTri++ ] = iFace;
7582 else if ( nbUniqueNodes == 4 )
7583 iQuadFace[ nbQuad++ ] = iFace;
7585 if (nbQuad == 2 && nbTri == 4 &&
7586 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7587 // 2 opposite quadrangles stuck with a diagonal;
7588 // sample groups of merged indices: (0-4)(2-6)
7589 // --------------------------------------------> 2 tetrahedrons
7590 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7591 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7592 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7593 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7594 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7595 // stuck with 0-2 diagonal
7603 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7604 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7605 // stuck with 1-3 diagonal
7617 uniqueNodes[ 0 ] = curNodes [ i0 ];
7618 uniqueNodes[ 1 ] = curNodes [ i1d ];
7619 uniqueNodes[ 2 ] = curNodes [ i3d ];
7620 uniqueNodes[ 3 ] = curNodes [ i0t ];
7623 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7627 myLastCreatedElems.Append(newElem);
7629 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7632 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7633 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7634 // --------------------------------------------> prism
7635 // find 2 opposite triangles
7637 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7638 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7639 // find indices of kept and replaced nodes
7640 // and fill unique nodes of 2 opposite triangles
7641 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7642 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7643 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7644 // fill unique nodes
7647 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7648 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7649 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7651 // iCur of a linked node of the opposite face (make normals co-directed):
7652 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7653 // check that correspondent corners of triangles are linked
7654 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7657 uniqueNodes[ iUnique ] = n;
7658 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7667 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7670 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7677 } // switch ( nbNodes )
7679 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7681 if ( isOk ) { // the elem remains valid after sticking nodes
7682 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7684 // Change nodes of polyedre
7685 const SMDS_VtkVolume* aPolyedre =
7686 dynamic_cast<const SMDS_VtkVolume*>( elem );
7688 int nbFaces = aPolyedre->NbFaces();
7690 vector<const SMDS_MeshNode *> poly_nodes;
7691 vector<int> quantities (nbFaces);
7693 for (int iface = 1; iface <= nbFaces; iface++) {
7694 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7695 quantities[iface - 1] = nbFaceNodes;
7697 for (inode = 1; inode <= nbFaceNodes; inode++) {
7698 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7700 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7701 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7702 curNode = (*nnIt).second;
7704 poly_nodes.push_back(curNode);
7707 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7710 else // replace non-polyhedron elements
7712 const SMDSAbs_ElementType etyp = elem->GetType();
7713 const int elemId = elem->GetID();
7714 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7715 uniqueNodes.resize(nbUniqueNodes);
7717 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7719 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7720 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7721 if ( sm && newElem )
7722 sm->AddElement( newElem );
7723 if ( elem != newElem )
7724 ReplaceElemInGroups( elem, newElem, aMesh );
7728 // Remove invalid regular element or invalid polygon
7729 rmElemIds.push_back( elem->GetID() );
7732 } // loop on elements
7734 // Remove bad elements, then equal nodes (order important)
7736 Remove( rmElemIds, false );
7737 Remove( rmNodeIds, true );
7742 // ========================================================
7743 // class : SortableElement
7744 // purpose : allow sorting elements basing on their nodes
7745 // ========================================================
7746 class SortableElement : public set <const SMDS_MeshElement*>
7750 SortableElement( const SMDS_MeshElement* theElem )
7753 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7754 while ( nodeIt->more() )
7755 this->insert( nodeIt->next() );
7758 const SMDS_MeshElement* Get() const
7761 void Set(const SMDS_MeshElement* e) const
7766 mutable const SMDS_MeshElement* myElem;
7769 //=======================================================================
7770 //function : FindEqualElements
7771 //purpose : Return list of group of elements built on the same nodes.
7772 // Search among theElements or in the whole mesh if theElements is empty
7773 //=======================================================================
7775 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7776 TListOfListOfElementsID & theGroupsOfElementsID)
7778 myLastCreatedElems.Clear();
7779 myLastCreatedNodes.Clear();
7781 typedef map< SortableElement, int > TMapOfNodeSet;
7782 typedef list<int> TGroupOfElems;
7784 if ( theElements.empty() )
7785 { // get all elements in the mesh
7786 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7787 while ( eIt->more() )
7788 theElements.insert( theElements.end(), eIt->next());
7791 vector< TGroupOfElems > arrayOfGroups;
7792 TGroupOfElems groupOfElems;
7793 TMapOfNodeSet mapOfNodeSet;
7795 TIDSortedElemSet::iterator elemIt = theElements.begin();
7796 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7797 const SMDS_MeshElement* curElem = *elemIt;
7798 SortableElement SE(curElem);
7801 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7802 if( !(pp.second) ) {
7803 TMapOfNodeSet::iterator& itSE = pp.first;
7804 ind = (*itSE).second;
7805 arrayOfGroups[ind].push_back(curElem->GetID());
7808 groupOfElems.clear();
7809 groupOfElems.push_back(curElem->GetID());
7810 arrayOfGroups.push_back(groupOfElems);
7815 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7816 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7817 groupOfElems = *groupIt;
7818 if ( groupOfElems.size() > 1 ) {
7819 groupOfElems.sort();
7820 theGroupsOfElementsID.push_back(groupOfElems);
7825 //=======================================================================
7826 //function : MergeElements
7827 //purpose : In each given group, substitute all elements by the first one.
7828 //=======================================================================
7830 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7832 myLastCreatedElems.Clear();
7833 myLastCreatedNodes.Clear();
7835 typedef list<int> TListOfIDs;
7836 TListOfIDs rmElemIds; // IDs of elems to remove
7838 SMESHDS_Mesh* aMesh = GetMeshDS();
7840 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7841 while ( groupsIt != theGroupsOfElementsID.end() ) {
7842 TListOfIDs& aGroupOfElemID = *groupsIt;
7843 aGroupOfElemID.sort();
7844 int elemIDToKeep = aGroupOfElemID.front();
7845 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7846 aGroupOfElemID.pop_front();
7847 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7848 while ( idIt != aGroupOfElemID.end() ) {
7849 int elemIDToRemove = *idIt;
7850 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7851 // add the kept element in groups of removed one (PAL15188)
7852 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7853 rmElemIds.push_back( elemIDToRemove );
7859 Remove( rmElemIds, false );
7862 //=======================================================================
7863 //function : MergeEqualElements
7864 //purpose : Remove all but one of elements built on the same nodes.
7865 //=======================================================================
7867 void SMESH_MeshEditor::MergeEqualElements()
7869 TIDSortedElemSet aMeshElements; /* empty input ==
7870 to merge equal elements in the whole mesh */
7871 TListOfListOfElementsID aGroupsOfElementsID;
7872 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7873 MergeElements(aGroupsOfElementsID);
7876 //=======================================================================
7877 //function : findAdjacentFace
7879 //=======================================================================
7881 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7882 const SMDS_MeshNode* n2,
7883 const SMDS_MeshElement* elem)
7885 TIDSortedElemSet elemSet, avoidSet;
7887 avoidSet.insert ( elem );
7888 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7891 //=======================================================================
7892 //function : FindFreeBorder
7894 //=======================================================================
7896 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7898 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7899 const SMDS_MeshNode* theSecondNode,
7900 const SMDS_MeshNode* theLastNode,
7901 list< const SMDS_MeshNode* > & theNodes,
7902 list< const SMDS_MeshElement* >& theFaces)
7904 if ( !theFirstNode || !theSecondNode )
7906 // find border face between theFirstNode and theSecondNode
7907 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7911 theFaces.push_back( curElem );
7912 theNodes.push_back( theFirstNode );
7913 theNodes.push_back( theSecondNode );
7915 //vector<const SMDS_MeshNode*> nodes;
7916 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7917 TIDSortedElemSet foundElems;
7918 bool needTheLast = ( theLastNode != 0 );
7920 while ( nStart != theLastNode ) {
7921 if ( nStart == theFirstNode )
7922 return !needTheLast;
7924 // find all free border faces sharing form nStart
7926 list< const SMDS_MeshElement* > curElemList;
7927 list< const SMDS_MeshNode* > nStartList;
7928 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7929 while ( invElemIt->more() ) {
7930 const SMDS_MeshElement* e = invElemIt->next();
7931 if ( e == curElem || foundElems.insert( e ).second ) {
7933 int iNode = 0, nbNodes = e->NbNodes();
7934 //const SMDS_MeshNode* nodes[nbNodes+1];
7935 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7937 if(e->IsQuadratic()) {
7938 const SMDS_VtkFace* F =
7939 dynamic_cast<const SMDS_VtkFace*>(e);
7940 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7941 // use special nodes iterator
7942 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7943 while( anIter->more() ) {
7944 nodes[ iNode++ ] = cast2Node(anIter->next());
7948 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7949 while ( nIt->more() )
7950 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7952 nodes[ iNode ] = nodes[ 0 ];
7954 for ( iNode = 0; iNode < nbNodes; iNode++ )
7955 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7956 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7957 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7959 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7960 curElemList.push_back( e );
7964 // analyse the found
7966 int nbNewBorders = curElemList.size();
7967 if ( nbNewBorders == 0 ) {
7968 // no free border furthermore
7969 return !needTheLast;
7971 else if ( nbNewBorders == 1 ) {
7972 // one more element found
7974 nStart = nStartList.front();
7975 curElem = curElemList.front();
7976 theFaces.push_back( curElem );
7977 theNodes.push_back( nStart );
7980 // several continuations found
7981 list< const SMDS_MeshElement* >::iterator curElemIt;
7982 list< const SMDS_MeshNode* >::iterator nStartIt;
7983 // check if one of them reached the last node
7984 if ( needTheLast ) {
7985 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7986 curElemIt!= curElemList.end();
7987 curElemIt++, nStartIt++ )
7988 if ( *nStartIt == theLastNode ) {
7989 theFaces.push_back( *curElemIt );
7990 theNodes.push_back( *nStartIt );
7994 // find the best free border by the continuations
7995 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7996 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7997 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7998 curElemIt!= curElemList.end();
7999 curElemIt++, nStartIt++ )
8001 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8002 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8003 // find one more free border
8004 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8008 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8009 // choice: clear a worse one
8010 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8011 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8012 contNodes[ iWorse ].clear();
8013 contFaces[ iWorse ].clear();
8016 if ( contNodes[0].empty() && contNodes[1].empty() )
8019 // append the best free border
8020 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8021 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8022 theNodes.pop_back(); // remove nIgnore
8023 theNodes.pop_back(); // remove nStart
8024 theFaces.pop_back(); // remove curElem
8025 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8026 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8027 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8028 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8031 } // several continuations found
8032 } // while ( nStart != theLastNode )
8037 //=======================================================================
8038 //function : CheckFreeBorderNodes
8039 //purpose : Return true if the tree nodes are on a free border
8040 //=======================================================================
8042 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8043 const SMDS_MeshNode* theNode2,
8044 const SMDS_MeshNode* theNode3)
8046 list< const SMDS_MeshNode* > nodes;
8047 list< const SMDS_MeshElement* > faces;
8048 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8051 //=======================================================================
8052 //function : SewFreeBorder
8054 //=======================================================================
8056 SMESH_MeshEditor::Sew_Error
8057 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8058 const SMDS_MeshNode* theBordSecondNode,
8059 const SMDS_MeshNode* theBordLastNode,
8060 const SMDS_MeshNode* theSideFirstNode,
8061 const SMDS_MeshNode* theSideSecondNode,
8062 const SMDS_MeshNode* theSideThirdNode,
8063 const bool theSideIsFreeBorder,
8064 const bool toCreatePolygons,
8065 const bool toCreatePolyedrs)
8067 myLastCreatedElems.Clear();
8068 myLastCreatedNodes.Clear();
8070 MESSAGE("::SewFreeBorder()");
8071 Sew_Error aResult = SEW_OK;
8073 // ====================================
8074 // find side nodes and elements
8075 // ====================================
8077 list< const SMDS_MeshNode* > nSide[ 2 ];
8078 list< const SMDS_MeshElement* > eSide[ 2 ];
8079 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8080 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8084 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8085 nSide[0], eSide[0])) {
8086 MESSAGE(" Free Border 1 not found " );
8087 aResult = SEW_BORDER1_NOT_FOUND;
8089 if (theSideIsFreeBorder) {
8092 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8093 nSide[1], eSide[1])) {
8094 MESSAGE(" Free Border 2 not found " );
8095 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8098 if ( aResult != SEW_OK )
8101 if (!theSideIsFreeBorder) {
8105 // -------------------------------------------------------------------------
8107 // 1. If nodes to merge are not coincident, move nodes of the free border
8108 // from the coord sys defined by the direction from the first to last
8109 // nodes of the border to the correspondent sys of the side 2
8110 // 2. On the side 2, find the links most co-directed with the correspondent
8111 // links of the free border
8112 // -------------------------------------------------------------------------
8114 // 1. Since sewing may break if there are volumes to split on the side 2,
8115 // we wont move nodes but just compute new coordinates for them
8116 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8117 TNodeXYZMap nBordXYZ;
8118 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8119 list< const SMDS_MeshNode* >::iterator nBordIt;
8121 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8122 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8123 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8124 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8125 double tol2 = 1.e-8;
8126 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8127 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8128 // Need node movement.
8130 // find X and Z axes to create trsf
8131 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8133 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8135 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8138 gp_Ax3 toBordAx( Pb1, Zb, X );
8139 gp_Ax3 fromSideAx( Ps1, Zs, X );
8140 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8142 gp_Trsf toBordSys, fromSide2Sys;
8143 toBordSys.SetTransformation( toBordAx );
8144 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8145 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8148 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8149 const SMDS_MeshNode* n = *nBordIt;
8150 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8151 toBordSys.Transforms( xyz );
8152 fromSide2Sys.Transforms( xyz );
8153 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8157 // just insert nodes XYZ in the nBordXYZ map
8158 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8159 const SMDS_MeshNode* n = *nBordIt;
8160 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8164 // 2. On the side 2, find the links most co-directed with the correspondent
8165 // links of the free border
8167 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8168 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8169 sideNodes.push_back( theSideFirstNode );
8171 bool hasVolumes = false;
8172 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8173 set<long> foundSideLinkIDs, checkedLinkIDs;
8174 SMDS_VolumeTool volume;
8175 //const SMDS_MeshNode* faceNodes[ 4 ];
8177 const SMDS_MeshNode* sideNode;
8178 const SMDS_MeshElement* sideElem;
8179 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8180 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8181 nBordIt = bordNodes.begin();
8183 // border node position and border link direction to compare with
8184 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8185 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8186 // choose next side node by link direction or by closeness to
8187 // the current border node:
8188 bool searchByDir = ( *nBordIt != theBordLastNode );
8190 // find the next node on the Side 2
8192 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8194 checkedLinkIDs.clear();
8195 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8197 // loop on inverse elements of current node (prevSideNode) on the Side 2
8198 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8199 while ( invElemIt->more() )
8201 const SMDS_MeshElement* elem = invElemIt->next();
8202 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8203 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8204 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8205 bool isVolume = volume.Set( elem );
8206 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8207 if ( isVolume ) // --volume
8209 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8210 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8211 if(elem->IsQuadratic()) {
8212 const SMDS_VtkFace* F =
8213 dynamic_cast<const SMDS_VtkFace*>(elem);
8214 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8215 // use special nodes iterator
8216 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8217 while( anIter->more() ) {
8218 nodes[ iNode ] = cast2Node(anIter->next());
8219 if ( nodes[ iNode++ ] == prevSideNode )
8220 iPrevNode = iNode - 1;
8224 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8225 while ( nIt->more() ) {
8226 nodes[ iNode ] = cast2Node( nIt->next() );
8227 if ( nodes[ iNode++ ] == prevSideNode )
8228 iPrevNode = iNode - 1;
8231 // there are 2 links to check
8236 // loop on links, to be precise, on the second node of links
8237 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8238 const SMDS_MeshNode* n = nodes[ iNode ];
8240 if ( !volume.IsLinked( n, prevSideNode ))
8244 if ( iNode ) // a node before prevSideNode
8245 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8246 else // a node after prevSideNode
8247 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8249 // check if this link was already used
8250 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8251 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8252 if (!isJustChecked &&
8253 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8255 // test a link geometrically
8256 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8257 bool linkIsBetter = false;
8258 double dot = 0.0, dist = 0.0;
8259 if ( searchByDir ) { // choose most co-directed link
8260 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8261 linkIsBetter = ( dot > maxDot );
8263 else { // choose link with the node closest to bordPos
8264 dist = ( nextXYZ - bordPos ).SquareModulus();
8265 linkIsBetter = ( dist < minDist );
8267 if ( linkIsBetter ) {
8276 } // loop on inverse elements of prevSideNode
8279 MESSAGE(" Cant find path by links of the Side 2 ");
8280 return SEW_BAD_SIDE_NODES;
8282 sideNodes.push_back( sideNode );
8283 sideElems.push_back( sideElem );
8284 foundSideLinkIDs.insert ( linkID );
8285 prevSideNode = sideNode;
8287 if ( *nBordIt == theBordLastNode )
8288 searchByDir = false;
8290 // find the next border link to compare with
8291 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8292 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8293 // move to next border node if sideNode is before forward border node (bordPos)
8294 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8295 prevBordNode = *nBordIt;
8297 bordPos = nBordXYZ[ *nBordIt ];
8298 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8299 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8303 while ( sideNode != theSideSecondNode );
8305 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8306 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8307 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8309 } // end nodes search on the side 2
8311 // ============================
8312 // sew the border to the side 2
8313 // ============================
8315 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8316 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8318 TListOfListOfNodes nodeGroupsToMerge;
8319 if ( nbNodes[0] == nbNodes[1] ||
8320 ( theSideIsFreeBorder && !theSideThirdNode)) {
8322 // all nodes are to be merged
8324 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8325 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8326 nIt[0]++, nIt[1]++ )
8328 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8329 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8330 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8335 // insert new nodes into the border and the side to get equal nb of segments
8337 // get normalized parameters of nodes on the borders
8338 //double param[ 2 ][ maxNbNodes ];
8340 param[0] = new double [ maxNbNodes ];
8341 param[1] = new double [ maxNbNodes ];
8343 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8344 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8345 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8346 const SMDS_MeshNode* nPrev = *nIt;
8347 double bordLength = 0;
8348 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8349 const SMDS_MeshNode* nCur = *nIt;
8350 gp_XYZ segment (nCur->X() - nPrev->X(),
8351 nCur->Y() - nPrev->Y(),
8352 nCur->Z() - nPrev->Z());
8353 double segmentLen = segment.Modulus();
8354 bordLength += segmentLen;
8355 param[ iBord ][ iNode ] = bordLength;
8358 // normalize within [0,1]
8359 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8360 param[ iBord ][ iNode ] /= bordLength;
8364 // loop on border segments
8365 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8366 int i[ 2 ] = { 0, 0 };
8367 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8368 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8370 TElemOfNodeListMap insertMap;
8371 TElemOfNodeListMap::iterator insertMapIt;
8373 // key: elem to insert nodes into
8374 // value: 2 nodes to insert between + nodes to be inserted
8376 bool next[ 2 ] = { false, false };
8378 // find min adjacent segment length after sewing
8379 double nextParam = 10., prevParam = 0;
8380 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8381 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8382 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8383 if ( i[ iBord ] > 0 )
8384 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8386 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8387 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8388 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8390 // choose to insert or to merge nodes
8391 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8392 if ( Abs( du ) <= minSegLen * 0.2 ) {
8395 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8396 const SMDS_MeshNode* n0 = *nIt[0];
8397 const SMDS_MeshNode* n1 = *nIt[1];
8398 nodeGroupsToMerge.back().push_back( n1 );
8399 nodeGroupsToMerge.back().push_back( n0 );
8400 // position of node of the border changes due to merge
8401 param[ 0 ][ i[0] ] += du;
8402 // move n1 for the sake of elem shape evaluation during insertion.
8403 // n1 will be removed by MergeNodes() anyway
8404 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8405 next[0] = next[1] = true;
8410 int intoBord = ( du < 0 ) ? 0 : 1;
8411 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8412 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8413 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8414 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8415 if ( intoBord == 1 ) {
8416 // move node of the border to be on a link of elem of the side
8417 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8418 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8419 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8420 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8421 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8423 insertMapIt = insertMap.find( elem );
8424 bool notFound = ( insertMapIt == insertMap.end() );
8425 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8427 // insert into another link of the same element:
8428 // 1. perform insertion into the other link of the elem
8429 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8430 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8431 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8432 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8433 // 2. perform insertion into the link of adjacent faces
8435 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8437 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8441 if (toCreatePolyedrs) {
8442 // perform insertion into the links of adjacent volumes
8443 UpdateVolumes(n12, n22, nodeList);
8445 // 3. find an element appeared on n1 and n2 after the insertion
8446 insertMap.erase( elem );
8447 elem = findAdjacentFace( n1, n2, 0 );
8449 if ( notFound || otherLink ) {
8450 // add element and nodes of the side into the insertMap
8451 insertMapIt = insertMap.insert
8452 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8453 (*insertMapIt).second.push_back( n1 );
8454 (*insertMapIt).second.push_back( n2 );
8456 // add node to be inserted into elem
8457 (*insertMapIt).second.push_back( nIns );
8458 next[ 1 - intoBord ] = true;
8461 // go to the next segment
8462 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8463 if ( next[ iBord ] ) {
8464 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8466 nPrev[ iBord ] = *nIt[ iBord ];
8467 nIt[ iBord ]++; i[ iBord ]++;
8471 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8473 // perform insertion of nodes into elements
8475 for (insertMapIt = insertMap.begin();
8476 insertMapIt != insertMap.end();
8479 const SMDS_MeshElement* elem = (*insertMapIt).first;
8480 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8481 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8482 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8484 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8486 if ( !theSideIsFreeBorder ) {
8487 // look for and insert nodes into the faces adjacent to elem
8489 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8491 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8496 if (toCreatePolyedrs) {
8497 // perform insertion into the links of adjacent volumes
8498 UpdateVolumes(n1, n2, nodeList);
8504 } // end: insert new nodes
8506 MergeNodes ( nodeGroupsToMerge );
8511 //=======================================================================
8512 //function : InsertNodesIntoLink
8513 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8514 // and theBetweenNode2 and split theElement
8515 //=======================================================================
8517 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8518 const SMDS_MeshNode* theBetweenNode1,
8519 const SMDS_MeshNode* theBetweenNode2,
8520 list<const SMDS_MeshNode*>& theNodesToInsert,
8521 const bool toCreatePoly)
8523 if ( theFace->GetType() != SMDSAbs_Face ) return;
8525 // find indices of 2 link nodes and of the rest nodes
8526 int iNode = 0, il1, il2, i3, i4;
8527 il1 = il2 = i3 = i4 = -1;
8528 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8529 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8531 if(theFace->IsQuadratic()) {
8532 const SMDS_VtkFace* F =
8533 dynamic_cast<const SMDS_VtkFace*>(theFace);
8534 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8535 // use special nodes iterator
8536 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8537 while( anIter->more() ) {
8538 const SMDS_MeshNode* n = cast2Node(anIter->next());
8539 if ( n == theBetweenNode1 )
8541 else if ( n == theBetweenNode2 )
8547 nodes[ iNode++ ] = n;
8551 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8552 while ( nodeIt->more() ) {
8553 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8554 if ( n == theBetweenNode1 )
8556 else if ( n == theBetweenNode2 )
8562 nodes[ iNode++ ] = n;
8565 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8568 // arrange link nodes to go one after another regarding the face orientation
8569 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8570 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8575 aNodesToInsert.reverse();
8577 // check that not link nodes of a quadrangles are in good order
8578 int nbFaceNodes = theFace->NbNodes();
8579 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8585 if (toCreatePoly || theFace->IsPoly()) {
8588 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8590 // add nodes of face up to first node of link
8593 if(theFace->IsQuadratic()) {
8594 const SMDS_VtkFace* F =
8595 dynamic_cast<const SMDS_VtkFace*>(theFace);
8596 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8597 // use special nodes iterator
8598 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8599 while( anIter->more() && !isFLN ) {
8600 const SMDS_MeshNode* n = cast2Node(anIter->next());
8601 poly_nodes[iNode++] = n;
8602 if (n == nodes[il1]) {
8606 // add nodes to insert
8607 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8608 for (; nIt != aNodesToInsert.end(); nIt++) {
8609 poly_nodes[iNode++] = *nIt;
8611 // add nodes of face starting from last node of link
8612 while ( anIter->more() ) {
8613 poly_nodes[iNode++] = cast2Node(anIter->next());
8617 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8618 while ( nodeIt->more() && !isFLN ) {
8619 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8620 poly_nodes[iNode++] = n;
8621 if (n == nodes[il1]) {
8625 // add nodes to insert
8626 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8627 for (; nIt != aNodesToInsert.end(); nIt++) {
8628 poly_nodes[iNode++] = *nIt;
8630 // add nodes of face starting from last node of link
8631 while ( nodeIt->more() ) {
8632 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8633 poly_nodes[iNode++] = n;
8637 // edit or replace the face
8638 SMESHDS_Mesh *aMesh = GetMeshDS();
8640 if (theFace->IsPoly()) {
8641 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8644 int aShapeId = FindShape( theFace );
8646 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8647 myLastCreatedElems.Append(newElem);
8648 if ( aShapeId && newElem )
8649 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8651 aMesh->RemoveElement(theFace);
8656 SMESHDS_Mesh *aMesh = GetMeshDS();
8657 if( !theFace->IsQuadratic() ) {
8659 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8660 int nbLinkNodes = 2 + aNodesToInsert.size();
8661 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8662 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8663 linkNodes[ 0 ] = nodes[ il1 ];
8664 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8665 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8666 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8667 linkNodes[ iNode++ ] = *nIt;
8669 // decide how to split a quadrangle: compare possible variants
8670 // and choose which of splits to be a quadrangle
8671 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8672 if ( nbFaceNodes == 3 ) {
8673 iBestQuad = nbSplits;
8676 else if ( nbFaceNodes == 4 ) {
8677 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8678 double aBestRate = DBL_MAX;
8679 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8681 double aBadRate = 0;
8682 // evaluate elements quality
8683 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8684 if ( iSplit == iQuad ) {
8685 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8689 aBadRate += getBadRate( &quad, aCrit );
8692 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8694 nodes[ iSplit < iQuad ? i4 : i3 ]);
8695 aBadRate += getBadRate( &tria, aCrit );
8699 if ( aBadRate < aBestRate ) {
8701 aBestRate = aBadRate;
8706 // create new elements
8707 int aShapeId = FindShape( theFace );
8710 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8711 SMDS_MeshElement* newElem = 0;
8712 if ( iSplit == iBestQuad )
8713 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8718 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8720 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8721 myLastCreatedElems.Append(newElem);
8722 if ( aShapeId && newElem )
8723 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8726 // change nodes of theFace
8727 const SMDS_MeshNode* newNodes[ 4 ];
8728 newNodes[ 0 ] = linkNodes[ i1 ];
8729 newNodes[ 1 ] = linkNodes[ i2 ];
8730 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8731 newNodes[ 3 ] = nodes[ i4 ];
8732 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8733 const SMDS_MeshElement* newElem = 0;
8734 if (iSplit == iBestQuad)
8735 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8737 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8738 myLastCreatedElems.Append(newElem);
8739 if ( aShapeId && newElem )
8740 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8741 } // end if(!theFace->IsQuadratic())
8742 else { // theFace is quadratic
8743 // we have to split theFace on simple triangles and one simple quadrangle
8745 int nbshift = tmp*2;
8746 // shift nodes in nodes[] by nbshift
8748 for(i=0; i<nbshift; i++) {
8749 const SMDS_MeshNode* n = nodes[0];
8750 for(j=0; j<nbFaceNodes-1; j++) {
8751 nodes[j] = nodes[j+1];
8753 nodes[nbFaceNodes-1] = n;
8755 il1 = il1 - nbshift;
8756 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8757 // n0 n1 n2 n0 n1 n2
8758 // +-----+-----+ +-----+-----+
8767 // create new elements
8768 int aShapeId = FindShape( theFace );
8771 if(nbFaceNodes==6) { // quadratic triangle
8772 SMDS_MeshElement* newElem =
8773 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8774 myLastCreatedElems.Append(newElem);
8775 if ( aShapeId && newElem )
8776 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8777 if(theFace->IsMediumNode(nodes[il1])) {
8778 // create quadrangle
8779 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8780 myLastCreatedElems.Append(newElem);
8781 if ( aShapeId && newElem )
8782 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8788 // create quadrangle
8789 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8790 myLastCreatedElems.Append(newElem);
8791 if ( aShapeId && newElem )
8792 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8798 else { // nbFaceNodes==8 - quadratic quadrangle
8799 SMDS_MeshElement* newElem =
8800 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8801 myLastCreatedElems.Append(newElem);
8802 if ( aShapeId && newElem )
8803 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8804 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8805 myLastCreatedElems.Append(newElem);
8806 if ( aShapeId && newElem )
8807 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8808 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8809 myLastCreatedElems.Append(newElem);
8810 if ( aShapeId && newElem )
8811 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8812 if(theFace->IsMediumNode(nodes[il1])) {
8813 // create quadrangle
8814 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8815 myLastCreatedElems.Append(newElem);
8816 if ( aShapeId && newElem )
8817 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8823 // create quadrangle
8824 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8825 myLastCreatedElems.Append(newElem);
8826 if ( aShapeId && newElem )
8827 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8833 // create needed triangles using n1,n2,n3 and inserted nodes
8834 int nbn = 2 + aNodesToInsert.size();
8835 //const SMDS_MeshNode* aNodes[nbn];
8836 vector<const SMDS_MeshNode*> aNodes(nbn);
8837 aNodes[0] = nodes[n1];
8838 aNodes[nbn-1] = nodes[n2];
8839 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8840 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8841 aNodes[iNode++] = *nIt;
8843 for(i=1; i<nbn; i++) {
8844 SMDS_MeshElement* newElem =
8845 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8846 myLastCreatedElems.Append(newElem);
8847 if ( aShapeId && newElem )
8848 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8852 aMesh->RemoveElement(theFace);
8855 //=======================================================================
8856 //function : UpdateVolumes
8858 //=======================================================================
8859 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8860 const SMDS_MeshNode* theBetweenNode2,
8861 list<const SMDS_MeshNode*>& theNodesToInsert)
8863 myLastCreatedElems.Clear();
8864 myLastCreatedNodes.Clear();
8866 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8867 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8868 const SMDS_MeshElement* elem = invElemIt->next();
8870 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8871 SMDS_VolumeTool aVolume (elem);
8872 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8875 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8876 int iface, nbFaces = aVolume.NbFaces();
8877 vector<const SMDS_MeshNode *> poly_nodes;
8878 vector<int> quantities (nbFaces);
8880 for (iface = 0; iface < nbFaces; iface++) {
8881 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8882 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8883 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8885 for (int inode = 0; inode < nbFaceNodes; inode++) {
8886 poly_nodes.push_back(faceNodes[inode]);
8888 if (nbInserted == 0) {
8889 if (faceNodes[inode] == theBetweenNode1) {
8890 if (faceNodes[inode + 1] == theBetweenNode2) {
8891 nbInserted = theNodesToInsert.size();
8893 // add nodes to insert
8894 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8895 for (; nIt != theNodesToInsert.end(); nIt++) {
8896 poly_nodes.push_back(*nIt);
8900 else if (faceNodes[inode] == theBetweenNode2) {
8901 if (faceNodes[inode + 1] == theBetweenNode1) {
8902 nbInserted = theNodesToInsert.size();
8904 // add nodes to insert in reversed order
8905 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8907 for (; nIt != theNodesToInsert.begin(); nIt--) {
8908 poly_nodes.push_back(*nIt);
8910 poly_nodes.push_back(*nIt);
8917 quantities[iface] = nbFaceNodes + nbInserted;
8920 // Replace or update the volume
8921 SMESHDS_Mesh *aMesh = GetMeshDS();
8923 if (elem->IsPoly()) {
8924 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8928 int aShapeId = FindShape( elem );
8930 SMDS_MeshElement* newElem =
8931 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8932 myLastCreatedElems.Append(newElem);
8933 if (aShapeId && newElem)
8934 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8936 aMesh->RemoveElement(elem);
8943 //================================================================================
8945 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8947 //================================================================================
8949 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8950 vector<const SMDS_MeshNode *> & nodes,
8951 vector<int> & nbNodeInFaces )
8954 nbNodeInFaces.clear();
8955 SMDS_VolumeTool vTool ( elem );
8956 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8958 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8959 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8960 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8965 //=======================================================================
8967 * \brief Convert elements contained in a submesh to quadratic
8968 * \return int - nb of checked elements
8970 //=======================================================================
8972 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8973 SMESH_MesherHelper& theHelper,
8974 const bool theForce3d)
8977 if( !theSm ) return nbElem;
8979 vector<int> nbNodeInFaces;
8980 vector<const SMDS_MeshNode *> nodes;
8981 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8982 while(ElemItr->more())
8985 const SMDS_MeshElement* elem = ElemItr->next();
8986 if( !elem ) continue;
8988 // analyse a necessity of conversion
8989 const SMDSAbs_ElementType aType = elem->GetType();
8990 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8992 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8993 bool hasCentralNodes = false;
8994 if ( elem->IsQuadratic() )
8997 switch ( aGeomType ) {
8998 case SMDSEntity_Quad_Triangle:
8999 case SMDSEntity_Quad_Quadrangle:
9000 case SMDSEntity_Quad_Hexa:
9001 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9003 case SMDSEntity_BiQuad_Triangle:
9004 case SMDSEntity_BiQuad_Quadrangle:
9005 case SMDSEntity_TriQuad_Hexa:
9006 alreadyOK = theHelper.GetIsBiQuadratic();
9007 hasCentralNodes = true;
9012 // take into account already present modium nodes
9014 case SMDSAbs_Volume:
9015 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9017 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9019 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9025 // get elem data needed to re-create it
9027 const int id = elem->GetID();
9028 const int nbNodes = elem->NbCornerNodes();
9029 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9030 if ( aGeomType == SMDSEntity_Polyhedra )
9031 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9032 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9033 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9035 // remove a linear element
9036 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9038 // remove central nodes of biquadratic elements (biquad->quad convertion)
9039 if ( hasCentralNodes )
9040 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9041 if ( nodes[i]->NbInverseElements() == 0 )
9042 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9044 const SMDS_MeshElement* NewElem = 0;
9050 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9058 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9061 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9064 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9068 case SMDSAbs_Volume :
9072 case SMDSEntity_Tetra:
9073 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9075 case SMDSEntity_Pyramid:
9076 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9078 case SMDSEntity_Penta:
9079 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9081 case SMDSEntity_Hexa:
9082 case SMDSEntity_Quad_Hexa:
9083 case SMDSEntity_TriQuad_Hexa:
9084 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9085 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9087 case SMDSEntity_Hexagonal_Prism:
9089 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9096 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9097 if( NewElem && NewElem->getshapeId() < 1 )
9098 theSm->AddElement( NewElem );
9102 //=======================================================================
9103 //function : ConvertToQuadratic
9105 //=======================================================================
9107 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9109 SMESHDS_Mesh* meshDS = GetMeshDS();
9111 SMESH_MesherHelper aHelper(*myMesh);
9113 aHelper.SetIsQuadratic( true );
9114 aHelper.SetIsBiQuadratic( theToBiQuad );
9115 aHelper.SetElementsOnShape(true);
9116 aHelper.ToFixNodeParameters( true );
9118 // convert elements assigned to sub-meshes
9119 int nbCheckedElems = 0;
9120 if ( myMesh->HasShapeToMesh() )
9122 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9124 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9125 while ( smIt->more() ) {
9126 SMESH_subMesh* sm = smIt->next();
9127 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9128 aHelper.SetSubShape( sm->GetSubShape() );
9129 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9135 // convert elements NOT assigned to sub-meshes
9136 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9137 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9139 aHelper.SetElementsOnShape(false);
9140 SMESHDS_SubMesh *smDS = 0;
9143 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9144 while( aEdgeItr->more() )
9146 const SMDS_MeshEdge* edge = aEdgeItr->next();
9147 if ( !edge->IsQuadratic() )
9149 int id = edge->GetID();
9150 const SMDS_MeshNode* n1 = edge->GetNode(0);
9151 const SMDS_MeshNode* n2 = edge->GetNode(1);
9153 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9155 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9156 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9160 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9165 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9166 while( aFaceItr->more() )
9168 const SMDS_MeshFace* face = aFaceItr->next();
9169 if ( !face ) continue;
9171 const SMDSAbs_EntityType type = face->GetEntityType();
9175 case SMDSEntity_Quad_Triangle:
9176 case SMDSEntity_Quad_Quadrangle:
9177 alreadyOK = !theToBiQuad;
9178 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9180 case SMDSEntity_BiQuad_Triangle:
9181 case SMDSEntity_BiQuad_Quadrangle:
9182 alreadyOK = theToBiQuad;
9183 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9185 default: alreadyOK = false;
9190 const int id = face->GetID();
9191 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9193 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9195 SMDS_MeshFace * NewFace = 0;
9198 case SMDSEntity_Triangle:
9199 case SMDSEntity_Quad_Triangle:
9200 case SMDSEntity_BiQuad_Triangle:
9201 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9202 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9203 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9206 case SMDSEntity_Quadrangle:
9207 case SMDSEntity_Quad_Quadrangle:
9208 case SMDSEntity_BiQuad_Quadrangle:
9209 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9210 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9211 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9215 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9217 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9221 vector<int> nbNodeInFaces;
9222 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9223 while(aVolumeItr->more())
9225 const SMDS_MeshVolume* volume = aVolumeItr->next();
9226 if ( !volume ) continue;
9228 const SMDSAbs_EntityType type = volume->GetEntityType();
9229 if ( volume->IsQuadratic() )
9234 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9235 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9236 default: alreadyOK = true;
9240 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9244 const int id = volume->GetID();
9245 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9246 if ( type == SMDSEntity_Polyhedra )
9247 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9248 else if ( type == SMDSEntity_Hexagonal_Prism )
9249 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9251 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9253 SMDS_MeshVolume * NewVolume = 0;
9256 case SMDSEntity_Tetra:
9257 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9259 case SMDSEntity_Hexa:
9260 case SMDSEntity_Quad_Hexa:
9261 case SMDSEntity_TriQuad_Hexa:
9262 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9263 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9264 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9265 if ( nodes[i]->NbInverseElements() == 0 )
9266 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9268 case SMDSEntity_Pyramid:
9269 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9270 nodes[3], nodes[4], id, theForce3d);
9272 case SMDSEntity_Penta:
9273 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9274 nodes[3], nodes[4], nodes[5], id, theForce3d);
9276 case SMDSEntity_Hexagonal_Prism:
9278 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9280 ReplaceElemInGroups(volume, NewVolume, meshDS);
9285 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9286 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9287 // aHelper.FixQuadraticElements(myError);
9288 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9292 //================================================================================
9294 * \brief Makes given elements quadratic
9295 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9296 * \param theElements - elements to make quadratic
9298 //================================================================================
9300 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9301 TIDSortedElemSet& theElements,
9302 const bool theToBiQuad)
9304 if ( theElements.empty() ) return;
9306 // we believe that all theElements are of the same type
9307 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9309 // get all nodes shared by theElements
9310 TIDSortedNodeSet allNodes;
9311 TIDSortedElemSet::iterator eIt = theElements.begin();
9312 for ( ; eIt != theElements.end(); ++eIt )
9313 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9315 // complete theElements with elements of lower dim whose all nodes are in allNodes
9317 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9318 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9319 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9320 for ( ; nIt != allNodes.end(); ++nIt )
9322 const SMDS_MeshNode* n = *nIt;
9323 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9324 while ( invIt->more() )
9326 const SMDS_MeshElement* e = invIt->next();
9327 const SMDSAbs_ElementType type = e->GetType();
9328 if ( e->IsQuadratic() )
9330 quadAdjacentElems[ type ].insert( e );
9333 switch ( e->GetEntityType() ) {
9334 case SMDSEntity_Quad_Triangle:
9335 case SMDSEntity_Quad_Quadrangle:
9336 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9337 case SMDSEntity_BiQuad_Triangle:
9338 case SMDSEntity_BiQuad_Quadrangle:
9339 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9340 default: alreadyOK = true;
9345 if ( type >= elemType )
9346 continue; // same type or more complex linear element
9348 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9349 continue; // e is already checked
9353 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9354 while ( nodeIt->more() && allIn )
9355 allIn = allNodes.count( nodeIt->next() );
9357 theElements.insert(e );
9361 SMESH_MesherHelper helper(*myMesh);
9362 helper.SetIsQuadratic( true );
9363 helper.SetIsBiQuadratic( theToBiQuad );
9365 // add links of quadratic adjacent elements to the helper
9367 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9368 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9369 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9371 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9373 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9374 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9375 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9377 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9379 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9380 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9381 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9383 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9386 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9388 SMESHDS_Mesh* meshDS = GetMeshDS();
9389 SMESHDS_SubMesh* smDS = 0;
9390 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9392 const SMDS_MeshElement* elem = *eIt;
9395 int nbCentralNodes = 0;
9396 switch ( elem->GetEntityType() ) {
9397 // linear convertible
9398 case SMDSEntity_Edge:
9399 case SMDSEntity_Triangle:
9400 case SMDSEntity_Quadrangle:
9401 case SMDSEntity_Tetra:
9402 case SMDSEntity_Pyramid:
9403 case SMDSEntity_Hexa:
9404 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9405 // quadratic that can become bi-quadratic
9406 case SMDSEntity_Quad_Triangle:
9407 case SMDSEntity_Quad_Quadrangle:
9408 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9410 case SMDSEntity_BiQuad_Triangle:
9411 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9412 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9414 default: alreadyOK = true;
9416 if ( alreadyOK ) continue;
9418 const SMDSAbs_ElementType type = elem->GetType();
9419 const int id = elem->GetID();
9420 const int nbNodes = elem->NbCornerNodes();
9421 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9423 helper.SetSubShape( elem->getshapeId() );
9425 if ( !smDS || !smDS->Contains( elem ))
9426 smDS = meshDS->MeshElements( elem->getshapeId() );
9427 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9429 SMDS_MeshElement * newElem = 0;
9432 case 4: // cases for most frequently used element types go first (for optimization)
9433 if ( type == SMDSAbs_Volume )
9434 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9436 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9439 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9440 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9443 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9446 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9449 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9450 nodes[4], id, theForce3d);
9453 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9454 nodes[4], nodes[5], id, theForce3d);
9458 ReplaceElemInGroups( elem, newElem, meshDS);
9459 if( newElem && smDS )
9460 smDS->AddElement( newElem );
9462 // remove central nodes
9463 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9464 if ( nodes[i]->NbInverseElements() == 0 )
9465 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9467 } // loop on theElements
9470 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9471 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9472 // helper.FixQuadraticElements( myError );
9473 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9477 //=======================================================================
9479 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9480 * \return int - nb of checked elements
9482 //=======================================================================
9484 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9485 SMDS_ElemIteratorPtr theItr,
9486 const int theShapeID)
9489 SMESHDS_Mesh* meshDS = GetMeshDS();
9491 while( theItr->more() )
9493 const SMDS_MeshElement* elem = theItr->next();
9495 if( elem && elem->IsQuadratic())
9497 int id = elem->GetID();
9498 int nbCornerNodes = elem->NbCornerNodes();
9499 SMDSAbs_ElementType aType = elem->GetType();
9501 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9503 //remove a quadratic element
9504 if ( !theSm || !theSm->Contains( elem ))
9505 theSm = meshDS->MeshElements( elem->getshapeId() );
9506 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9508 // remove medium nodes
9509 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9510 if ( nodes[i]->NbInverseElements() == 0 )
9511 meshDS->RemoveFreeNode( nodes[i], theSm );
9513 // add a linear element
9514 nodes.resize( nbCornerNodes );
9515 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9516 ReplaceElemInGroups(elem, newElem, meshDS);
9517 if( theSm && newElem )
9518 theSm->AddElement( newElem );
9524 //=======================================================================
9525 //function : ConvertFromQuadratic
9527 //=======================================================================
9529 bool SMESH_MeshEditor::ConvertFromQuadratic()
9531 int nbCheckedElems = 0;
9532 if ( myMesh->HasShapeToMesh() )
9534 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9536 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9537 while ( smIt->more() ) {
9538 SMESH_subMesh* sm = smIt->next();
9539 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9540 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9546 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9547 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9549 SMESHDS_SubMesh *aSM = 0;
9550 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9558 //================================================================================
9560 * \brief Return true if all medium nodes of the element are in the node set
9562 //================================================================================
9564 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9566 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9567 if ( !nodeSet.count( elem->GetNode(i) ))
9573 //================================================================================
9575 * \brief Makes given elements linear
9577 //================================================================================
9579 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9581 if ( theElements.empty() ) return;
9583 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9584 set<int> mediumNodeIDs;
9585 TIDSortedElemSet::iterator eIt = theElements.begin();
9586 for ( ; eIt != theElements.end(); ++eIt )
9588 const SMDS_MeshElement* e = *eIt;
9589 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9590 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9593 // replace given elements by linear ones
9594 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9595 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9597 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9598 // except those elements sharing medium nodes of quadratic element whose medium nodes
9599 // are not all in mediumNodeIDs
9601 // get remaining medium nodes
9602 TIDSortedNodeSet mediumNodes;
9603 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9604 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9605 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9606 mediumNodes.insert( mediumNodes.end(), n );
9608 // find more quadratic elements to convert
9609 TIDSortedElemSet moreElemsToConvert;
9610 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9611 for ( ; nIt != mediumNodes.end(); ++nIt )
9613 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9614 while ( invIt->more() )
9616 const SMDS_MeshElement* e = invIt->next();
9617 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9619 // find a more complex element including e and
9620 // whose medium nodes are not in mediumNodes
9621 bool complexFound = false;
9622 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9624 SMDS_ElemIteratorPtr invIt2 =
9625 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9626 while ( invIt2->more() )
9628 const SMDS_MeshElement* eComplex = invIt2->next();
9629 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9631 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9632 if ( nbCommonNodes == e->NbNodes())
9634 complexFound = true;
9635 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9641 if ( !complexFound )
9642 moreElemsToConvert.insert( e );
9646 elemIt = elemSetIterator( moreElemsToConvert );
9647 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9650 //=======================================================================
9651 //function : SewSideElements
9653 //=======================================================================
9655 SMESH_MeshEditor::Sew_Error
9656 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9657 TIDSortedElemSet& theSide2,
9658 const SMDS_MeshNode* theFirstNode1,
9659 const SMDS_MeshNode* theFirstNode2,
9660 const SMDS_MeshNode* theSecondNode1,
9661 const SMDS_MeshNode* theSecondNode2)
9663 myLastCreatedElems.Clear();
9664 myLastCreatedNodes.Clear();
9666 MESSAGE ("::::SewSideElements()");
9667 if ( theSide1.size() != theSide2.size() )
9668 return SEW_DIFF_NB_OF_ELEMENTS;
9670 Sew_Error aResult = SEW_OK;
9672 // 1. Build set of faces representing each side
9673 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9674 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9676 // =======================================================================
9677 // 1. Build set of faces representing each side:
9678 // =======================================================================
9679 // a. build set of nodes belonging to faces
9680 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9681 // c. create temporary faces representing side of volumes if correspondent
9682 // face does not exist
9684 SMESHDS_Mesh* aMesh = GetMeshDS();
9685 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9686 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9687 TIDSortedElemSet faceSet1, faceSet2;
9688 set<const SMDS_MeshElement*> volSet1, volSet2;
9689 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9690 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9691 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9692 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9693 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9694 int iSide, iFace, iNode;
9696 list<const SMDS_MeshElement* > tempFaceList;
9697 for ( iSide = 0; iSide < 2; iSide++ ) {
9698 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9699 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9700 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9701 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9702 set<const SMDS_MeshElement*>::iterator vIt;
9703 TIDSortedElemSet::iterator eIt;
9704 set<const SMDS_MeshNode*>::iterator nIt;
9706 // check that given nodes belong to given elements
9707 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9708 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9709 int firstIndex = -1, secondIndex = -1;
9710 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9711 const SMDS_MeshElement* elem = *eIt;
9712 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9713 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9714 if ( firstIndex > -1 && secondIndex > -1 ) break;
9716 if ( firstIndex < 0 || secondIndex < 0 ) {
9717 // we can simply return until temporary faces created
9718 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9721 // -----------------------------------------------------------
9722 // 1a. Collect nodes of existing faces
9723 // and build set of face nodes in order to detect missing
9724 // faces corresponding to sides of volumes
9725 // -----------------------------------------------------------
9727 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9729 // loop on the given element of a side
9730 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9731 //const SMDS_MeshElement* elem = *eIt;
9732 const SMDS_MeshElement* elem = *eIt;
9733 if ( elem->GetType() == SMDSAbs_Face ) {
9734 faceSet->insert( elem );
9735 set <const SMDS_MeshNode*> faceNodeSet;
9736 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9737 while ( nodeIt->more() ) {
9738 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9739 nodeSet->insert( n );
9740 faceNodeSet.insert( n );
9742 setOfFaceNodeSet.insert( faceNodeSet );
9744 else if ( elem->GetType() == SMDSAbs_Volume )
9745 volSet->insert( elem );
9747 // ------------------------------------------------------------------------------
9748 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9749 // ------------------------------------------------------------------------------
9751 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9752 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9753 while ( fIt->more() ) { // loop on faces sharing a node
9754 const SMDS_MeshElement* f = fIt->next();
9755 if ( faceSet->find( f ) == faceSet->end() ) {
9756 // check if all nodes are in nodeSet and
9757 // complete setOfFaceNodeSet if they are
9758 set <const SMDS_MeshNode*> faceNodeSet;
9759 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9760 bool allInSet = true;
9761 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9762 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9763 if ( nodeSet->find( n ) == nodeSet->end() )
9766 faceNodeSet.insert( n );
9769 faceSet->insert( f );
9770 setOfFaceNodeSet.insert( faceNodeSet );
9776 // -------------------------------------------------------------------------
9777 // 1c. Create temporary faces representing sides of volumes if correspondent
9778 // face does not exist
9779 // -------------------------------------------------------------------------
9781 if ( !volSet->empty() ) {
9782 //int nodeSetSize = nodeSet->size();
9784 // loop on given volumes
9785 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9786 SMDS_VolumeTool vol (*vIt);
9787 // loop on volume faces: find free faces
9788 // --------------------------------------
9789 list<const SMDS_MeshElement* > freeFaceList;
9790 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9791 if ( !vol.IsFreeFace( iFace ))
9793 // check if there is already a face with same nodes in a face set
9794 const SMDS_MeshElement* aFreeFace = 0;
9795 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9796 int nbNodes = vol.NbFaceNodes( iFace );
9797 set <const SMDS_MeshNode*> faceNodeSet;
9798 vol.GetFaceNodes( iFace, faceNodeSet );
9799 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9801 // no such a face is given but it still can exist, check it
9802 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9803 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9806 // create a temporary face
9807 if ( nbNodes == 3 ) {
9808 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9809 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9811 else if ( nbNodes == 4 ) {
9812 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9813 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9816 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9817 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9818 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9821 tempFaceList.push_back( aFreeFace );
9825 freeFaceList.push_back( aFreeFace );
9827 } // loop on faces of a volume
9829 // choose one of several free faces of a volume
9830 // --------------------------------------------
9831 if ( freeFaceList.size() > 1 ) {
9832 // choose a face having max nb of nodes shared by other elems of a side
9833 int maxNbNodes = -1;
9834 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9835 while ( fIt != freeFaceList.end() ) { // loop on free faces
9836 int nbSharedNodes = 0;
9837 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9838 while ( nodeIt->more() ) { // loop on free face nodes
9839 const SMDS_MeshNode* n =
9840 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9841 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9842 while ( invElemIt->more() ) {
9843 const SMDS_MeshElement* e = invElemIt->next();
9844 nbSharedNodes += faceSet->count( e );
9845 nbSharedNodes += elemSet->count( e );
9848 if ( nbSharedNodes > maxNbNodes ) {
9849 maxNbNodes = nbSharedNodes;
9850 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9852 else if ( nbSharedNodes == maxNbNodes ) {
9856 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9859 if ( freeFaceList.size() > 1 )
9861 // could not choose one face, use another way
9862 // choose a face most close to the bary center of the opposite side
9863 gp_XYZ aBC( 0., 0., 0. );
9864 set <const SMDS_MeshNode*> addedNodes;
9865 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9866 eIt = elemSet2->begin();
9867 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9868 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9869 while ( nodeIt->more() ) { // loop on free face nodes
9870 const SMDS_MeshNode* n =
9871 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9872 if ( addedNodes.insert( n ).second )
9873 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9876 aBC /= addedNodes.size();
9877 double minDist = DBL_MAX;
9878 fIt = freeFaceList.begin();
9879 while ( fIt != freeFaceList.end() ) { // loop on free faces
9881 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9882 while ( nodeIt->more() ) { // loop on free face nodes
9883 const SMDS_MeshNode* n =
9884 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9885 gp_XYZ p( n->X(),n->Y(),n->Z() );
9886 dist += ( aBC - p ).SquareModulus();
9888 if ( dist < minDist ) {
9890 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9893 fIt = freeFaceList.erase( fIt++ );
9896 } // choose one of several free faces of a volume
9898 if ( freeFaceList.size() == 1 ) {
9899 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9900 faceSet->insert( aFreeFace );
9901 // complete a node set with nodes of a found free face
9902 // for ( iNode = 0; iNode < ; iNode++ )
9903 // nodeSet->insert( fNodes[ iNode ] );
9906 } // loop on volumes of a side
9908 // // complete a set of faces if new nodes in a nodeSet appeared
9909 // // ----------------------------------------------------------
9910 // if ( nodeSetSize != nodeSet->size() ) {
9911 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9912 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9913 // while ( fIt->more() ) { // loop on faces sharing a node
9914 // const SMDS_MeshElement* f = fIt->next();
9915 // if ( faceSet->find( f ) == faceSet->end() ) {
9916 // // check if all nodes are in nodeSet and
9917 // // complete setOfFaceNodeSet if they are
9918 // set <const SMDS_MeshNode*> faceNodeSet;
9919 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9920 // bool allInSet = true;
9921 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9922 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9923 // if ( nodeSet->find( n ) == nodeSet->end() )
9924 // allInSet = false;
9926 // faceNodeSet.insert( n );
9928 // if ( allInSet ) {
9929 // faceSet->insert( f );
9930 // setOfFaceNodeSet.insert( faceNodeSet );
9936 } // Create temporary faces, if there are volumes given
9939 if ( faceSet1.size() != faceSet2.size() ) {
9940 // delete temporary faces: they are in reverseElements of actual nodes
9941 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9942 // while ( tmpFaceIt->more() )
9943 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9944 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9945 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9946 // aMesh->RemoveElement(*tmpFaceIt);
9947 MESSAGE("Diff nb of faces");
9948 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9951 // ============================================================
9952 // 2. Find nodes to merge:
9953 // bind a node to remove to a node to put instead
9954 // ============================================================
9956 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9957 if ( theFirstNode1 != theFirstNode2 )
9958 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9959 if ( theSecondNode1 != theSecondNode2 )
9960 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9962 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9963 set< long > linkIdSet; // links to process
9964 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9966 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9967 list< NLink > linkList[2];
9968 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9969 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9970 // loop on links in linkList; find faces by links and append links
9971 // of the found faces to linkList
9972 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9973 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9975 NLink link[] = { *linkIt[0], *linkIt[1] };
9976 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9977 if ( !linkIdSet.count( linkID ) )
9980 // by links, find faces in the face sets,
9981 // and find indices of link nodes in the found faces;
9982 // in a face set, there is only one or no face sharing a link
9983 // ---------------------------------------------------------------
9985 const SMDS_MeshElement* face[] = { 0, 0 };
9986 vector<const SMDS_MeshNode*> fnodes[2];
9987 int iLinkNode[2][2];
9988 TIDSortedElemSet avoidSet;
9989 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9990 const SMDS_MeshNode* n1 = link[iSide].first;
9991 const SMDS_MeshNode* n2 = link[iSide].second;
9992 //cout << "Side " << iSide << " ";
9993 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9994 // find a face by two link nodes
9995 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9996 *faceSetPtr[ iSide ], avoidSet,
9997 &iLinkNode[iSide][0],
9998 &iLinkNode[iSide][1] );
10001 //cout << " F " << face[ iSide]->GetID() <<endl;
10002 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10003 // put face nodes to fnodes
10004 if ( face[ iSide ]->IsQuadratic() )
10006 // use interlaced nodes iterator
10007 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10008 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10009 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10010 while ( nIter->more() )
10011 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10015 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10016 face[ iSide ]->end_nodes() );
10018 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10022 // check similarity of elements of the sides
10023 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10024 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10025 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10026 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10029 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10031 break; // do not return because it's necessary to remove tmp faces
10034 // set nodes to merge
10035 // -------------------
10037 if ( face[0] && face[1] ) {
10038 const int nbNodes = face[0]->NbNodes();
10039 if ( nbNodes != face[1]->NbNodes() ) {
10040 MESSAGE("Diff nb of face nodes");
10041 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10042 break; // do not return because it s necessary to remove tmp faces
10044 bool reverse[] = { false, false }; // order of nodes in the link
10045 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10046 // analyse link orientation in faces
10047 int i1 = iLinkNode[ iSide ][ 0 ];
10048 int i2 = iLinkNode[ iSide ][ 1 ];
10049 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10051 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10052 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10053 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10055 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10056 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10059 // add other links of the faces to linkList
10060 // -----------------------------------------
10062 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10063 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10064 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10065 if ( !iter_isnew.second ) { // already in a set: no need to process
10066 linkIdSet.erase( iter_isnew.first );
10068 else // new in set == encountered for the first time: add
10070 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10071 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10072 linkList[0].push_back ( NLink( n1, n2 ));
10073 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10078 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10081 } // loop on link lists
10083 if ( aResult == SEW_OK &&
10084 ( //linkIt[0] != linkList[0].end() ||
10085 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10086 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10087 " " << (faceSetPtr[1]->empty()));
10088 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10091 // ====================================================================
10092 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10093 // ====================================================================
10095 // delete temporary faces
10096 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10097 // while ( tmpFaceIt->more() )
10098 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10099 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10100 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10101 aMesh->RemoveElement(*tmpFaceIt);
10103 if ( aResult != SEW_OK)
10106 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10107 // loop on nodes replacement map
10108 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10109 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10110 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10111 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10112 nodeIDsToRemove.push_back( nToRemove->GetID() );
10113 // loop on elements sharing nToRemove
10114 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10115 while ( invElemIt->more() ) {
10116 const SMDS_MeshElement* e = invElemIt->next();
10117 // get a new suite of nodes: make replacement
10118 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10119 vector< const SMDS_MeshNode*> nodes( nbNodes );
10120 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10121 while ( nIt->more() ) {
10122 const SMDS_MeshNode* n =
10123 static_cast<const SMDS_MeshNode*>( nIt->next() );
10124 nnIt = nReplaceMap.find( n );
10125 if ( nnIt != nReplaceMap.end() ) {
10127 n = (*nnIt).second;
10131 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10132 // elemIDsToRemove.push_back( e->GetID() );
10136 SMDSAbs_ElementType etyp = e->GetType();
10137 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10140 myLastCreatedElems.Append(newElem);
10141 AddToSameGroups(newElem, e, aMesh);
10142 int aShapeId = e->getshapeId();
10145 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10148 aMesh->RemoveElement(e);
10153 Remove( nodeIDsToRemove, true );
10158 //================================================================================
10160 * \brief Find corresponding nodes in two sets of faces
10161 * \param theSide1 - first face set
10162 * \param theSide2 - second first face
10163 * \param theFirstNode1 - a boundary node of set 1
10164 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10165 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10166 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10167 * \param nReplaceMap - output map of corresponding nodes
10168 * \return bool - is a success or not
10170 //================================================================================
10173 //#define DEBUG_MATCHING_NODES
10176 SMESH_MeshEditor::Sew_Error
10177 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10178 set<const SMDS_MeshElement*>& theSide2,
10179 const SMDS_MeshNode* theFirstNode1,
10180 const SMDS_MeshNode* theFirstNode2,
10181 const SMDS_MeshNode* theSecondNode1,
10182 const SMDS_MeshNode* theSecondNode2,
10183 TNodeNodeMap & nReplaceMap)
10185 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10187 nReplaceMap.clear();
10188 if ( theFirstNode1 != theFirstNode2 )
10189 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10190 if ( theSecondNode1 != theSecondNode2 )
10191 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10193 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10194 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10196 list< NLink > linkList[2];
10197 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10198 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10200 // loop on links in linkList; find faces by links and append links
10201 // of the found faces to linkList
10202 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10203 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10204 NLink link[] = { *linkIt[0], *linkIt[1] };
10205 if ( linkSet.find( link[0] ) == linkSet.end() )
10208 // by links, find faces in the face sets,
10209 // and find indices of link nodes in the found faces;
10210 // in a face set, there is only one or no face sharing a link
10211 // ---------------------------------------------------------------
10213 const SMDS_MeshElement* face[] = { 0, 0 };
10214 list<const SMDS_MeshNode*> notLinkNodes[2];
10215 //bool reverse[] = { false, false }; // order of notLinkNodes
10217 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10219 const SMDS_MeshNode* n1 = link[iSide].first;
10220 const SMDS_MeshNode* n2 = link[iSide].second;
10221 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10222 set< const SMDS_MeshElement* > facesOfNode1;
10223 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10225 // during a loop of the first node, we find all faces around n1,
10226 // during a loop of the second node, we find one face sharing both n1 and n2
10227 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10228 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10229 while ( fIt->more() ) { // loop on faces sharing a node
10230 const SMDS_MeshElement* f = fIt->next();
10231 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10232 ! facesOfNode1.insert( f ).second ) // f encounters twice
10234 if ( face[ iSide ] ) {
10235 MESSAGE( "2 faces per link " );
10236 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10239 faceSet->erase( f );
10241 // get not link nodes
10242 int nbN = f->NbNodes();
10243 if ( f->IsQuadratic() )
10245 nbNodes[ iSide ] = nbN;
10246 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10247 int i1 = f->GetNodeIndex( n1 );
10248 int i2 = f->GetNodeIndex( n2 );
10249 int iEnd = nbN, iBeg = -1, iDelta = 1;
10250 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10252 std::swap( iEnd, iBeg ); iDelta = -1;
10257 if ( i == iEnd ) i = iBeg + iDelta;
10258 if ( i == i1 ) break;
10259 nodes.push_back ( f->GetNode( i ) );
10265 // check similarity of elements of the sides
10266 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10267 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10268 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10269 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10272 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10276 // set nodes to merge
10277 // -------------------
10279 if ( face[0] && face[1] ) {
10280 if ( nbNodes[0] != nbNodes[1] ) {
10281 MESSAGE("Diff nb of face nodes");
10282 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10284 #ifdef DEBUG_MATCHING_NODES
10285 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10286 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10287 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10289 int nbN = nbNodes[0];
10291 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10292 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10293 for ( int i = 0 ; i < nbN - 2; ++i ) {
10294 #ifdef DEBUG_MATCHING_NODES
10295 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10297 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10301 // add other links of the face 1 to linkList
10302 // -----------------------------------------
10304 const SMDS_MeshElement* f0 = face[0];
10305 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10306 for ( int i = 0; i < nbN; i++ )
10308 const SMDS_MeshNode* n2 = f0->GetNode( i );
10309 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10310 linkSet.insert( SMESH_TLink( n1, n2 ));
10311 if ( !iter_isnew.second ) { // already in a set: no need to process
10312 linkSet.erase( iter_isnew.first );
10314 else // new in set == encountered for the first time: add
10316 #ifdef DEBUG_MATCHING_NODES
10317 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10318 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10320 linkList[0].push_back ( NLink( n1, n2 ));
10321 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10326 } // loop on link lists
10331 //================================================================================
10333 * \brief Create elements equal (on same nodes) to given ones
10334 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10335 * elements of the uppest dimension are duplicated.
10337 //================================================================================
10339 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10341 CrearLastCreated();
10342 SMESHDS_Mesh* mesh = GetMeshDS();
10344 // get an element type and an iterator over elements
10346 SMDSAbs_ElementType type;
10347 SMDS_ElemIteratorPtr elemIt;
10348 vector< const SMDS_MeshElement* > allElems;
10349 if ( theElements.empty() )
10351 if ( mesh->NbNodes() == 0 )
10353 // get most complex type
10354 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10355 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10356 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10358 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10359 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10364 // put all elements in the vector <allElems>
10365 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10366 elemIt = mesh->elementsIterator( type );
10367 while ( elemIt->more() )
10368 allElems.push_back( elemIt->next());
10369 elemIt = elemSetIterator( allElems );
10373 type = (*theElements.begin())->GetType();
10374 elemIt = elemSetIterator( theElements );
10377 // duplicate elements
10379 if ( type == SMDSAbs_Ball )
10381 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10382 while ( elemIt->more() )
10384 const SMDS_MeshElement* elem = elemIt->next();
10385 if ( elem->GetType() != SMDSAbs_Ball )
10387 if (( elem = mesh->AddBall( elem->GetNode(0),
10388 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10389 myLastCreatedElems.Append( elem );
10394 vector< const SMDS_MeshNode* > nodes;
10395 while ( elemIt->more() )
10397 const SMDS_MeshElement* elem = elemIt->next();
10398 if ( elem->GetType() != type )
10401 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10403 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10405 std::vector<int> quantities =
10406 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10407 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10411 AddElement( nodes, type, elem->IsPoly() );
10412 elem = 0; // myLastCreatedElems is already filled
10415 myLastCreatedElems.Append( elem );
10420 //================================================================================
10422 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10423 \param theElems - the list of elements (edges or faces) to be replicated
10424 The nodes for duplication could be found from these elements
10425 \param theNodesNot - list of nodes to NOT replicate
10426 \param theAffectedElems - the list of elements (cells and edges) to which the
10427 replicated nodes should be associated to.
10428 \return TRUE if operation has been completed successfully, FALSE otherwise
10430 //================================================================================
10432 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10433 const TIDSortedElemSet& theNodesNot,
10434 const TIDSortedElemSet& theAffectedElems )
10436 myLastCreatedElems.Clear();
10437 myLastCreatedNodes.Clear();
10439 if ( theElems.size() == 0 )
10442 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10447 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10448 // duplicate elements and nodes
10449 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10450 // replce nodes by duplications
10451 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10455 //================================================================================
10457 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10458 \param theMeshDS - mesh instance
10459 \param theElems - the elements replicated or modified (nodes should be changed)
10460 \param theNodesNot - nodes to NOT replicate
10461 \param theNodeNodeMap - relation of old node to new created node
10462 \param theIsDoubleElem - flag os to replicate element or modify
10463 \return TRUE if operation has been completed successfully, FALSE otherwise
10465 //================================================================================
10467 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10468 const TIDSortedElemSet& theElems,
10469 const TIDSortedElemSet& theNodesNot,
10470 std::map< const SMDS_MeshNode*,
10471 const SMDS_MeshNode* >& theNodeNodeMap,
10472 const bool theIsDoubleElem )
10474 MESSAGE("doubleNodes");
10475 // iterate on through element and duplicate them (by nodes duplication)
10477 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10478 for ( ; elemItr != theElems.end(); ++elemItr )
10480 const SMDS_MeshElement* anElem = *elemItr;
10484 bool isDuplicate = false;
10485 // duplicate nodes to duplicate element
10486 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10487 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10489 while ( anIter->more() )
10492 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10493 SMDS_MeshNode* aNewNode = aCurrNode;
10494 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10495 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10496 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10499 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10500 copyPosition( aCurrNode, aNewNode );
10501 theNodeNodeMap[ aCurrNode ] = aNewNode;
10502 myLastCreatedNodes.Append( aNewNode );
10504 isDuplicate |= (aCurrNode != aNewNode);
10505 newNodes[ ind++ ] = aNewNode;
10507 if ( !isDuplicate )
10510 if ( theIsDoubleElem )
10511 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10513 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10520 //================================================================================
10522 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10523 \param theNodes - identifiers of nodes to be doubled
10524 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10525 nodes. If list of element identifiers is empty then nodes are doubled but
10526 they not assigned to elements
10527 \return TRUE if operation has been completed successfully, FALSE otherwise
10529 //================================================================================
10531 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10532 const std::list< int >& theListOfModifiedElems )
10534 MESSAGE("DoubleNodes");
10535 myLastCreatedElems.Clear();
10536 myLastCreatedNodes.Clear();
10538 if ( theListOfNodes.size() == 0 )
10541 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10545 // iterate through nodes and duplicate them
10547 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10549 std::list< int >::const_iterator aNodeIter;
10550 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10552 int aCurr = *aNodeIter;
10553 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10559 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10562 copyPosition( aNode, aNewNode );
10563 anOldNodeToNewNode[ aNode ] = aNewNode;
10564 myLastCreatedNodes.Append( aNewNode );
10568 // Create map of new nodes for modified elements
10570 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10572 std::list< int >::const_iterator anElemIter;
10573 for ( anElemIter = theListOfModifiedElems.begin();
10574 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10576 int aCurr = *anElemIter;
10577 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10581 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10583 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10585 while ( anIter->more() )
10587 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10588 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10590 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10591 aNodeArr[ ind++ ] = aNewNode;
10594 aNodeArr[ ind++ ] = aCurrNode;
10596 anElemToNodes[ anElem ] = aNodeArr;
10599 // Change nodes of elements
10601 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10602 anElemToNodesIter = anElemToNodes.begin();
10603 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10605 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10606 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10609 MESSAGE("ChangeElementNodes");
10610 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10619 //================================================================================
10621 \brief Check if element located inside shape
10622 \return TRUE if IN or ON shape, FALSE otherwise
10624 //================================================================================
10626 template<class Classifier>
10627 bool isInside(const SMDS_MeshElement* theElem,
10628 Classifier& theClassifier,
10629 const double theTol)
10631 gp_XYZ centerXYZ (0, 0, 0);
10632 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10633 while (aNodeItr->more())
10634 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10636 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10637 theClassifier.Perform(aPnt, theTol);
10638 TopAbs_State aState = theClassifier.State();
10639 return (aState == TopAbs_IN || aState == TopAbs_ON );
10642 //================================================================================
10644 * \brief Classifier of the 3D point on the TopoDS_Face
10645 * with interaface suitable for isInside()
10647 //================================================================================
10649 struct _FaceClassifier
10651 Extrema_ExtPS _extremum;
10652 BRepAdaptor_Surface _surface;
10653 TopAbs_State _state;
10655 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10657 _extremum.Initialize( _surface,
10658 _surface.FirstUParameter(), _surface.LastUParameter(),
10659 _surface.FirstVParameter(), _surface.LastVParameter(),
10660 _surface.Tolerance(), _surface.Tolerance() );
10662 void Perform(const gp_Pnt& aPnt, double theTol)
10665 _state = TopAbs_OUT;
10666 _extremum.Perform(aPnt);
10667 if ( _extremum.IsDone() )
10668 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10669 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10671 TopAbs_State State() const
10678 //================================================================================
10680 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10681 This method is the first step of DoubleNodeElemGroupsInRegion.
10682 \param theElems - list of groups of elements (edges or faces) to be replicated
10683 \param theNodesNot - list of groups of nodes not to replicated
10684 \param theShape - shape to detect affected elements (element which geometric center
10685 located on or inside shape). If the shape is null, detection is done on faces orientations
10686 (select elements with a gravity center on the side given by faces normals).
10687 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10688 The replicated nodes should be associated to affected elements.
10689 \return groups of affected elements
10690 \sa DoubleNodeElemGroupsInRegion()
10692 //================================================================================
10694 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10695 const TIDSortedElemSet& theNodesNot,
10696 const TopoDS_Shape& theShape,
10697 TIDSortedElemSet& theAffectedElems)
10699 if ( theShape.IsNull() )
10701 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10702 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10703 std::set<const SMDS_MeshElement*> edgesToCheck;
10704 alreadyCheckedNodes.clear();
10705 alreadyCheckedElems.clear();
10706 edgesToCheck.clear();
10708 // --- iterates on elements to be replicated and get elements by back references from their nodes
10710 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10712 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10714 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10715 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10718 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10719 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10720 std::set<const SMDS_MeshNode*> nodesElem;
10722 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10723 while ( nodeItr->more() )
10725 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10726 nodesElem.insert(aNode);
10728 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10729 for (; nodit != nodesElem.end(); nodit++)
10731 MESSAGE(" noeud ");
10732 const SMDS_MeshNode* aNode = *nodit;
10733 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10735 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10737 alreadyCheckedNodes.insert(aNode);
10738 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10739 while ( backElemItr->more() )
10741 MESSAGE(" backelem ");
10742 const SMDS_MeshElement* curElem = backElemItr->next();
10743 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10745 if (theElems.find(curElem) != theElems.end())
10747 alreadyCheckedElems.insert(curElem);
10748 double x=0, y=0, z=0;
10750 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10751 while ( nodeItr2->more() )
10753 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10754 x += anotherNode->X();
10755 y += anotherNode->Y();
10756 z += anotherNode->Z();
10760 p.SetCoord( x/nb -aNode->X(),
10762 z/nb -aNode->Z() );
10763 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10766 MESSAGE(" --- inserted")
10767 theAffectedElems.insert( curElem );
10769 else if (curElem->GetType() == SMDSAbs_Edge)
10770 edgesToCheck.insert(curElem);
10774 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10775 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10776 for( ; eit != edgesToCheck.end(); eit++)
10778 bool onside = true;
10779 const SMDS_MeshElement* anEdge = *eit;
10780 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10781 while ( nodeItr->more() )
10783 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10784 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10792 MESSAGE(" --- edge onside inserted")
10793 theAffectedElems.insert(anEdge);
10799 const double aTol = Precision::Confusion();
10800 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10801 auto_ptr<_FaceClassifier> aFaceClassifier;
10802 if ( theShape.ShapeType() == TopAbs_SOLID )
10804 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10805 bsc3d->PerformInfinitePoint(aTol);
10807 else if (theShape.ShapeType() == TopAbs_FACE )
10809 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10812 // iterates on indicated elements and get elements by back references from their nodes
10813 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10815 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10817 MESSAGE("element " << ielem++);
10818 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10821 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10822 while ( nodeItr->more() )
10824 MESSAGE(" noeud ");
10825 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10826 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10828 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10829 while ( backElemItr->more() )
10831 MESSAGE(" backelem ");
10832 const SMDS_MeshElement* curElem = backElemItr->next();
10833 if ( curElem && theElems.find(curElem) == theElems.end() &&
10835 isInside( curElem, *bsc3d, aTol ) :
10836 isInside( curElem, *aFaceClassifier, aTol )))
10837 theAffectedElems.insert( curElem );
10845 //================================================================================
10847 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10848 \param theElems - group of of elements (edges or faces) to be replicated
10849 \param theNodesNot - group of nodes not to replicate
10850 \param theShape - shape to detect affected elements (element which geometric center
10851 located on or inside shape).
10852 The replicated nodes should be associated to affected elements.
10853 \return TRUE if operation has been completed successfully, FALSE otherwise
10855 //================================================================================
10857 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10858 const TIDSortedElemSet& theNodesNot,
10859 const TopoDS_Shape& theShape )
10861 if ( theShape.IsNull() )
10864 const double aTol = Precision::Confusion();
10865 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10866 auto_ptr<_FaceClassifier> aFaceClassifier;
10867 if ( theShape.ShapeType() == TopAbs_SOLID )
10869 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10870 bsc3d->PerformInfinitePoint(aTol);
10872 else if (theShape.ShapeType() == TopAbs_FACE )
10874 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10877 // iterates on indicated elements and get elements by back references from their nodes
10878 TIDSortedElemSet anAffected;
10879 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10880 for ( ; elemItr != theElems.end(); ++elemItr )
10882 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10886 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10887 while ( nodeItr->more() )
10889 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10890 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10892 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10893 while ( backElemItr->more() )
10895 const SMDS_MeshElement* curElem = backElemItr->next();
10896 if ( curElem && theElems.find(curElem) == theElems.end() &&
10898 isInside( curElem, *bsc3d, aTol ) :
10899 isInside( curElem, *aFaceClassifier, aTol )))
10900 anAffected.insert( curElem );
10904 return DoubleNodes( theElems, theNodesNot, anAffected );
10908 * \brief compute an oriented angle between two planes defined by four points.
10909 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10910 * @param p0 base of the rotation axe
10911 * @param p1 extremity of the rotation axe
10912 * @param g1 belongs to the first plane
10913 * @param g2 belongs to the second plane
10915 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10917 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10918 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10919 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10920 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10921 gp_Vec vref(p0, p1);
10924 gp_Vec n1 = vref.Crossed(v1);
10925 gp_Vec n2 = vref.Crossed(v2);
10927 return n2.AngleWithRef(n1, vref);
10929 catch ( Standard_Failure ) {
10931 return Max( v1.Magnitude(), v2.Magnitude() );
10935 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10936 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10937 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10938 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10939 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10940 * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
10941 * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
10942 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10943 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10944 * \param theElems - list of groups of volumes, where a group of volume is a set of
10945 * SMDS_MeshElements sorted by Id.
10946 * \param createJointElems - if TRUE, create the elements
10947 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10948 * the boundary between \a theDomains and the rest mesh
10949 * \return TRUE if operation has been completed successfully, FALSE otherwise
10951 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10952 bool createJointElems,
10953 bool onAllBoundaries)
10955 MESSAGE("----------------------------------------------");
10956 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10957 MESSAGE("----------------------------------------------");
10959 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10960 meshDS->BuildDownWardConnectivity(true);
10962 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10964 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10965 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10966 // build the list of nodes shared by 2 or more domains, with their domain indexes
10968 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10969 std::map<int,int>celldom; // cell vtkId --> domain
10970 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10971 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10972 faceDomains.clear();
10974 cellDomains.clear();
10975 nodeDomains.clear();
10976 std::map<int,int> emptyMap;
10977 std::set<int> emptySet;
10980 MESSAGE(".. Number of domains :"<<theElems.size());
10982 TIDSortedElemSet theRestDomElems;
10983 const int iRestDom = -1;
10984 const int idom0 = onAllBoundaries ? iRestDom : 0;
10985 const int nbDomains = theElems.size();
10987 // Check if the domains do not share an element
10988 for (int idom = 0; idom < nbDomains-1; idom++)
10990 // MESSAGE("... Check of domain #" << idom);
10991 const TIDSortedElemSet& domain = theElems[idom];
10992 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10993 for (; elemItr != domain.end(); ++elemItr)
10995 const SMDS_MeshElement* anElem = *elemItr;
10996 int idombisdeb = idom + 1 ;
10997 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10999 const TIDSortedElemSet& domainbis = theElems[idombis];
11000 if ( domainbis.count(anElem) )
11002 MESSAGE(".... Domain #" << idom);
11003 MESSAGE(".... Domain #" << idombis);
11004 throw SALOME_Exception("The domains are not disjoint.");
11011 for (int idom = 0; idom < nbDomains; idom++)
11014 // --- build a map (face to duplicate --> volume to modify)
11015 // with all the faces shared by 2 domains (group of elements)
11016 // and corresponding volume of this domain, for each shared face.
11017 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11019 MESSAGE("... Neighbors of domain #" << idom);
11020 const TIDSortedElemSet& domain = theElems[idom];
11021 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11022 for (; elemItr != domain.end(); ++elemItr)
11024 const SMDS_MeshElement* anElem = *elemItr;
11027 int vtkId = anElem->getVtkId();
11028 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11029 int neighborsVtkIds[NBMAXNEIGHBORS];
11030 int downIds[NBMAXNEIGHBORS];
11031 unsigned char downTypes[NBMAXNEIGHBORS];
11032 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11033 for (int n = 0; n < nbNeighbors; n++)
11035 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11036 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11037 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11040 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11042 // MESSAGE("Domain " << idombis);
11043 const TIDSortedElemSet& domainbis = theElems[idombis];
11044 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11046 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11048 DownIdType face(downIds[n], downTypes[n]);
11049 if (!faceDomains[face].count(idom))
11051 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11052 celldom[vtkId] = idom;
11053 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11057 theRestDomElems.insert( elem );
11058 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11059 celldom[neighborsVtkIds[n]] = iRestDom;
11067 //MESSAGE("Number of shared faces " << faceDomains.size());
11068 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11070 // --- explore the shared faces domain by domain,
11071 // explore the nodes of the face and see if they belong to a cell in the domain,
11072 // which has only a node or an edge on the border (not a shared face)
11074 for (int idomain = idom0; idomain < nbDomains; idomain++)
11076 //MESSAGE("Domain " << idomain);
11077 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11078 itface = faceDomains.begin();
11079 for (; itface != faceDomains.end(); ++itface)
11081 const std::map<int, int>& domvol = itface->second;
11082 if (!domvol.count(idomain))
11084 DownIdType face = itface->first;
11085 //MESSAGE(" --- face " << face.cellId);
11086 std::set<int> oldNodes;
11088 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11089 std::set<int>::iterator itn = oldNodes.begin();
11090 for (; itn != oldNodes.end(); ++itn)
11093 //MESSAGE(" node " << oldId);
11094 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11095 for (int i=0; i<l.ncells; i++)
11097 int vtkId = l.cells[i];
11098 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11099 if (!domain.count(anElem))
11101 int vtkType = grid->GetCellType(vtkId);
11102 int downId = grid->CellIdToDownId(vtkId);
11105 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11106 continue; // not OK at this stage of the algorithm:
11107 //no cells created after BuildDownWardConnectivity
11109 DownIdType aCell(downId, vtkType);
11110 cellDomains[aCell][idomain] = vtkId;
11111 celldom[vtkId] = idomain;
11112 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11118 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11119 // for each shared face, get the nodes
11120 // for each node, for each domain of the face, create a clone of the node
11122 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11123 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11124 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11126 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11127 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11128 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11130 MESSAGE(".. Duplication of the nodes");
11131 for (int idomain = idom0; idomain < nbDomains; idomain++)
11133 itface = faceDomains.begin();
11134 for (; itface != faceDomains.end(); ++itface)
11136 const std::map<int, int>& domvol = itface->second;
11137 if (!domvol.count(idomain))
11139 DownIdType face = itface->first;
11140 //MESSAGE(" --- face " << face.cellId);
11141 std::set<int> oldNodes;
11143 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11144 std::set<int>::iterator itn = oldNodes.begin();
11145 for (; itn != oldNodes.end(); ++itn)
11148 if (nodeDomains[oldId].empty())
11150 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11151 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11153 std::map<int, int>::const_iterator itdom = domvol.begin();
11154 for (; itdom != domvol.end(); ++itdom)
11156 int idom = itdom->first;
11157 //MESSAGE(" domain " << idom);
11158 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11160 if (nodeDomains[oldId].size() >= 2) // a multiple node
11162 vector<int> orderedDoms;
11163 //MESSAGE("multiple node " << oldId);
11164 if (mutipleNodes.count(oldId))
11165 orderedDoms = mutipleNodes[oldId];
11168 map<int,int>::iterator it = nodeDomains[oldId].begin();
11169 for (; it != nodeDomains[oldId].end(); ++it)
11170 orderedDoms.push_back(it->first);
11172 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11173 //stringstream txt;
11174 //for (int i=0; i<orderedDoms.size(); i++)
11175 // txt << orderedDoms[i] << " ";
11176 //MESSAGE("orderedDoms " << txt.str());
11177 mutipleNodes[oldId] = orderedDoms;
11179 double *coords = grid->GetPoint(oldId);
11180 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11181 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11182 int newId = newNode->getVtkId();
11183 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11184 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11191 MESSAGE(".. Creation of elements");
11192 for (int idomain = idom0; idomain < nbDomains; idomain++)
11194 itface = faceDomains.begin();
11195 for (; itface != faceDomains.end(); ++itface)
11197 std::map<int, int> domvol = itface->second;
11198 if (!domvol.count(idomain))
11200 DownIdType face = itface->first;
11201 //MESSAGE(" --- face " << face.cellId);
11202 std::set<int> oldNodes;
11204 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11205 int nbMultipleNodes = 0;
11206 std::set<int>::iterator itn = oldNodes.begin();
11207 for (; itn != oldNodes.end(); ++itn)
11210 if (mutipleNodes.count(oldId))
11213 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11215 //MESSAGE("multiple Nodes detected on a shared face");
11216 int downId = itface->first.cellId;
11217 unsigned char cellType = itface->first.cellType;
11218 // --- shared edge or shared face ?
11219 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11222 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11223 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11224 if (mutipleNodes.count(nodes[i]))
11225 if (!mutipleNodesToFace.count(nodes[i]))
11226 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11228 else // shared face (between two volumes)
11230 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11231 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11232 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11233 for (int ie =0; ie < nbEdges; ie++)
11236 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11237 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11239 vector<int> vn0 = mutipleNodes[nodes[0]];
11240 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11242 for (int i0 = 0; i0 < vn0.size(); i0++)
11243 for (int i1 = 0; i1 < vn1.size(); i1++)
11244 if (vn0[i0] == vn1[i1])
11245 doms.push_back(vn0[i0]);
11246 if (doms.size() >2)
11248 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11249 double *coords = grid->GetPoint(nodes[0]);
11250 gp_Pnt p0(coords[0], coords[1], coords[2]);
11251 coords = grid->GetPoint(nodes[nbNodes - 1]);
11252 gp_Pnt p1(coords[0], coords[1], coords[2]);
11254 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11255 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11256 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11257 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11258 for (int id=0; id < doms.size(); id++)
11260 int idom = doms[id];
11261 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11262 for (int ivol=0; ivol<nbvol; ivol++)
11264 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11265 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11266 if (domain.count(elem))
11268 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11269 domvol[idom] = svol;
11270 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11272 vtkIdType npts = 0;
11273 vtkIdType* pts = 0;
11274 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11275 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11278 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11279 angleDom[idom] = 0;
11283 gp_Pnt g(values[0], values[1], values[2]);
11284 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11285 //MESSAGE(" angle=" << angleDom[idom]);
11291 map<double, int> sortedDom; // sort domains by angle
11292 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11293 sortedDom[ia->second] = ia->first;
11294 vector<int> vnodes;
11296 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11298 vdom.push_back(ib->second);
11299 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11301 for (int ino = 0; ino < nbNodes; ino++)
11302 vnodes.push_back(nodes[ino]);
11303 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11312 // --- iterate on shared faces (volumes to modify, face to extrude)
11313 // get node id's of the face (id SMDS = id VTK)
11314 // create flat element with old and new nodes if requested
11316 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11317 // (domain1 X domain2) = domain1 + MAXINT*domain2
11319 std::map<int, std::map<long,int> > nodeQuadDomains;
11320 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11322 MESSAGE(".. Creation of elements: simple junction");
11323 if (createJointElems)
11326 string joints2DName = "joints2D";
11327 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11328 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11329 string joints3DName = "joints3D";
11330 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11331 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11333 itface = faceDomains.begin();
11334 for (; itface != faceDomains.end(); ++itface)
11336 DownIdType face = itface->first;
11337 std::set<int> oldNodes;
11338 std::set<int>::iterator itn;
11340 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11342 std::map<int, int> domvol = itface->second;
11343 std::map<int, int>::iterator itdom = domvol.begin();
11344 int dom1 = itdom->first;
11345 int vtkVolId = itdom->second;
11347 int dom2 = itdom->first;
11348 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11350 stringstream grpname;
11353 grpname << dom1 << "_" << dom2;
11355 grpname << dom2 << "_" << dom1;
11356 string namegrp = grpname.str();
11357 if (!mapOfJunctionGroups.count(namegrp))
11358 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11359 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11361 sgrp->Add(vol->GetID());
11362 if (vol->GetType() == SMDSAbs_Volume)
11363 joints3DGrp->Add(vol->GetID());
11364 else if (vol->GetType() == SMDSAbs_Face)
11365 joints2DGrp->Add(vol->GetID());
11369 // --- create volumes on multiple domain intersection if requested
11370 // iterate on mutipleNodesToFace
11371 // iterate on edgesMultiDomains
11373 MESSAGE(".. Creation of elements: multiple junction");
11374 if (createJointElems)
11376 // --- iterate on mutipleNodesToFace
11378 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11379 for (; itn != mutipleNodesToFace.end(); ++itn)
11381 int node = itn->first;
11382 vector<int> orderDom = itn->second;
11383 vector<vtkIdType> orderedNodes;
11384 for (int idom = 0; idom <orderDom.size(); idom++)
11385 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11386 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11388 stringstream grpname;
11390 grpname << 0 << "_" << 0;
11392 string namegrp = grpname.str();
11393 if (!mapOfJunctionGroups.count(namegrp))
11394 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11395 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11397 sgrp->Add(face->GetID());
11400 // --- iterate on edgesMultiDomains
11402 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11403 for (; ite != edgesMultiDomains.end(); ++ite)
11405 vector<int> nodes = ite->first;
11406 vector<int> orderDom = ite->second;
11407 vector<vtkIdType> orderedNodes;
11408 if (nodes.size() == 2)
11410 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11411 for (int ino=0; ino < nodes.size(); ino++)
11412 if (orderDom.size() == 3)
11413 for (int idom = 0; idom <orderDom.size(); idom++)
11414 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11416 for (int idom = orderDom.size()-1; idom >=0; idom--)
11417 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11418 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11421 string namegrp = "jointsMultiples";
11422 if (!mapOfJunctionGroups.count(namegrp))
11423 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11424 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11426 sgrp->Add(vol->GetID());
11430 INFOS("Quadratic multiple joints not implemented");
11431 // TODO quadratic nodes
11436 // --- list the explicit faces and edges of the mesh that need to be modified,
11437 // i.e. faces and edges built with one or more duplicated nodes.
11438 // associate these faces or edges to their corresponding domain.
11439 // only the first domain found is kept when a face or edge is shared
11441 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11442 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11443 faceOrEdgeDom.clear();
11446 MESSAGE(".. Modification of elements");
11447 for (int idomain = idom0; idomain < nbDomains; idomain++)
11449 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11450 for (; itnod != nodeDomains.end(); ++itnod)
11452 int oldId = itnod->first;
11453 //MESSAGE(" node " << oldId);
11454 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11455 for (int i = 0; i < l.ncells; i++)
11457 int vtkId = l.cells[i];
11458 int vtkType = grid->GetCellType(vtkId);
11459 int downId = grid->CellIdToDownId(vtkId);
11461 continue; // new cells: not to be modified
11462 DownIdType aCell(downId, vtkType);
11463 int volParents[1000];
11464 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11465 for (int j = 0; j < nbvol; j++)
11466 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11467 if (!feDom.count(vtkId))
11469 feDom[vtkId] = idomain;
11470 faceOrEdgeDom[aCell] = emptyMap;
11471 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11472 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11473 // << " type " << vtkType << " downId " << downId);
11479 // --- iterate on shared faces (volumes to modify, face to extrude)
11480 // get node id's of the face
11481 // replace old nodes by new nodes in volumes, and update inverse connectivity
11483 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11484 for (int m=0; m<3; m++)
11486 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11487 itface = (*amap).begin();
11488 for (; itface != (*amap).end(); ++itface)
11490 DownIdType face = itface->first;
11491 std::set<int> oldNodes;
11492 std::set<int>::iterator itn;
11494 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11495 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11496 std::map<int, int> localClonedNodeIds;
11498 std::map<int, int> domvol = itface->second;
11499 std::map<int, int>::iterator itdom = domvol.begin();
11500 for (; itdom != domvol.end(); ++itdom)
11502 int idom = itdom->first;
11503 int vtkVolId = itdom->second;
11504 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11505 localClonedNodeIds.clear();
11506 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11509 if (nodeDomains[oldId].count(idom))
11511 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11512 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11515 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11520 // Remove empty groups (issue 0022812)
11521 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11522 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11524 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11525 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11528 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11529 grid->BuildLinks();
11537 * \brief Double nodes on some external faces and create flat elements.
11538 * Flat elements are mainly used by some types of mechanic calculations.
11540 * Each group of the list must be constituted of faces.
11541 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11542 * @param theElems - list of groups of faces, where a group of faces is a set of
11543 * SMDS_MeshElements sorted by Id.
11544 * @return TRUE if operation has been completed successfully, FALSE otherwise
11546 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11548 MESSAGE("-------------------------------------------------");
11549 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11550 MESSAGE("-------------------------------------------------");
11552 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11554 // --- For each group of faces
11555 // duplicate the nodes, create a flat element based on the face
11556 // replace the nodes of the faces by their clones
11558 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11559 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11560 clonedNodes.clear();
11561 intermediateNodes.clear();
11562 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11563 mapOfJunctionGroups.clear();
11565 for (int idom = 0; idom < theElems.size(); idom++)
11567 const TIDSortedElemSet& domain = theElems[idom];
11568 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11569 for (; elemItr != domain.end(); ++elemItr)
11571 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11572 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11575 // MESSAGE("aFace=" << aFace->GetID());
11576 bool isQuad = aFace->IsQuadratic();
11577 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11579 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11581 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11582 while (nodeIt->more())
11584 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11585 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11587 ln2.push_back(node);
11589 ln0.push_back(node);
11591 const SMDS_MeshNode* clone = 0;
11592 if (!clonedNodes.count(node))
11594 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11595 copyPosition( node, clone );
11596 clonedNodes[node] = clone;
11599 clone = clonedNodes[node];
11602 ln3.push_back(clone);
11604 ln1.push_back(clone);
11606 const SMDS_MeshNode* inter = 0;
11607 if (isQuad && (!isMedium))
11609 if (!intermediateNodes.count(node))
11611 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11612 copyPosition( node, inter );
11613 intermediateNodes[node] = inter;
11616 inter = intermediateNodes[node];
11617 ln4.push_back(inter);
11621 // --- extrude the face
11623 vector<const SMDS_MeshNode*> ln;
11624 SMDS_MeshVolume* vol = 0;
11625 vtkIdType aType = aFace->GetVtkType();
11629 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11630 // MESSAGE("vol prism " << vol->GetID());
11631 ln.push_back(ln1[0]);
11632 ln.push_back(ln1[1]);
11633 ln.push_back(ln1[2]);
11636 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11637 // MESSAGE("vol hexa " << vol->GetID());
11638 ln.push_back(ln1[0]);
11639 ln.push_back(ln1[1]);
11640 ln.push_back(ln1[2]);
11641 ln.push_back(ln1[3]);
11643 case VTK_QUADRATIC_TRIANGLE:
11644 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11645 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11646 // MESSAGE("vol quad prism " << vol->GetID());
11647 ln.push_back(ln1[0]);
11648 ln.push_back(ln1[1]);
11649 ln.push_back(ln1[2]);
11650 ln.push_back(ln3[0]);
11651 ln.push_back(ln3[1]);
11652 ln.push_back(ln3[2]);
11654 case VTK_QUADRATIC_QUAD:
11655 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11656 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11657 // ln4[0], ln4[1], ln4[2], ln4[3]);
11658 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11659 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11660 ln4[0], ln4[1], ln4[2], ln4[3]);
11661 // MESSAGE("vol quad hexa " << vol->GetID());
11662 ln.push_back(ln1[0]);
11663 ln.push_back(ln1[1]);
11664 ln.push_back(ln1[2]);
11665 ln.push_back(ln1[3]);
11666 ln.push_back(ln3[0]);
11667 ln.push_back(ln3[1]);
11668 ln.push_back(ln3[2]);
11669 ln.push_back(ln3[3]);
11679 stringstream grpname;
11683 string namegrp = grpname.str();
11684 if (!mapOfJunctionGroups.count(namegrp))
11685 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11686 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11688 sgrp->Add(vol->GetID());
11691 // --- modify the face
11693 aFace->ChangeNodes(&ln[0], ln.size());
11700 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11701 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11702 * groups of faces to remove inside the object, (idem edges).
11703 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11705 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11706 const TopoDS_Shape& theShape,
11707 SMESH_NodeSearcher* theNodeSearcher,
11708 const char* groupName,
11709 std::vector<double>& nodesCoords,
11710 std::vector<std::vector<int> >& listOfListOfNodes)
11712 MESSAGE("--------------------------------");
11713 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11714 MESSAGE("--------------------------------");
11716 // --- zone of volumes to remove is given :
11717 // 1 either by a geom shape (one or more vertices) and a radius,
11718 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11719 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11720 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11721 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11722 // defined by it's name.
11724 SMESHDS_GroupBase* groupDS = 0;
11725 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11726 while ( groupIt->more() )
11729 SMESH_Group * group = groupIt->next();
11730 if ( !group ) continue;
11731 groupDS = group->GetGroupDS();
11732 if ( !groupDS || groupDS->IsEmpty() ) continue;
11733 std::string grpName = group->GetName();
11734 //MESSAGE("grpName=" << grpName);
11735 if (grpName == groupName)
11741 bool isNodeGroup = false;
11742 bool isNodeCoords = false;
11745 if (groupDS->GetType() != SMDSAbs_Node)
11747 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11750 if (nodesCoords.size() > 0)
11751 isNodeCoords = true; // a list o nodes given by their coordinates
11752 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11754 // --- define groups to build
11756 int idg; // --- group of SMDS volumes
11757 string grpvName = groupName;
11758 grpvName += "_vol";
11759 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11762 MESSAGE("group not created " << grpvName);
11765 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11767 int idgs; // --- group of SMDS faces on the skin
11768 string grpsName = groupName;
11769 grpsName += "_skin";
11770 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11773 MESSAGE("group not created " << grpsName);
11776 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11778 int idgi; // --- group of SMDS faces internal (several shapes)
11779 string grpiName = groupName;
11780 grpiName += "_internalFaces";
11781 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11784 MESSAGE("group not created " << grpiName);
11787 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11789 int idgei; // --- group of SMDS faces internal (several shapes)
11790 string grpeiName = groupName;
11791 grpeiName += "_internalEdges";
11792 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11795 MESSAGE("group not created " << grpeiName);
11798 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11800 // --- build downward connectivity
11802 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11803 meshDS->BuildDownWardConnectivity(true);
11804 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11806 // --- set of volumes detected inside
11808 std::set<int> setOfInsideVol;
11809 std::set<int> setOfVolToCheck;
11811 std::vector<gp_Pnt> gpnts;
11814 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11816 MESSAGE("group of nodes provided");
11817 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11818 while ( elemIt->more() )
11820 const SMDS_MeshElement* elem = elemIt->next();
11823 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11826 SMDS_MeshElement* vol = 0;
11827 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11828 while (volItr->more())
11830 vol = (SMDS_MeshElement*)volItr->next();
11831 setOfInsideVol.insert(vol->getVtkId());
11832 sgrp->Add(vol->GetID());
11836 else if (isNodeCoords)
11838 MESSAGE("list of nodes coordinates provided");
11841 while (i < nodesCoords.size()-2)
11843 double x = nodesCoords[i++];
11844 double y = nodesCoords[i++];
11845 double z = nodesCoords[i++];
11846 gp_Pnt p = gp_Pnt(x, y ,z);
11847 gpnts.push_back(p);
11848 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11852 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11854 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11855 TopTools_IndexedMapOfShape vertexMap;
11856 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11857 gp_Pnt p = gp_Pnt(0,0,0);
11858 if (vertexMap.Extent() < 1)
11861 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11863 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11864 p = BRep_Tool::Pnt(vertex);
11865 gpnts.push_back(p);
11866 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11870 if (gpnts.size() > 0)
11873 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11875 nodeId = startNode->GetID();
11876 MESSAGE("nodeId " << nodeId);
11878 double radius2 = radius*radius;
11879 MESSAGE("radius2 " << radius2);
11881 // --- volumes on start node
11883 setOfVolToCheck.clear();
11884 SMDS_MeshElement* startVol = 0;
11885 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11886 while (volItr->more())
11888 startVol = (SMDS_MeshElement*)volItr->next();
11889 setOfVolToCheck.insert(startVol->getVtkId());
11891 if (setOfVolToCheck.empty())
11893 MESSAGE("No volumes found");
11897 // --- starting with central volumes then their neighbors, check if they are inside
11898 // or outside the domain, until no more new neighbor volume is inside.
11899 // Fill the group of inside volumes
11901 std::map<int, double> mapOfNodeDistance2;
11902 mapOfNodeDistance2.clear();
11903 std::set<int> setOfOutsideVol;
11904 while (!setOfVolToCheck.empty())
11906 std::set<int>::iterator it = setOfVolToCheck.begin();
11908 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11909 bool volInside = false;
11910 vtkIdType npts = 0;
11911 vtkIdType* pts = 0;
11912 grid->GetCellPoints(vtkId, npts, pts);
11913 for (int i=0; i<npts; i++)
11915 double distance2 = 0;
11916 if (mapOfNodeDistance2.count(pts[i]))
11918 distance2 = mapOfNodeDistance2[pts[i]];
11919 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11923 double *coords = grid->GetPoint(pts[i]);
11924 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11926 for (int j=0; j<gpnts.size(); j++)
11928 double d2 = aPoint.SquareDistance(gpnts[j]);
11929 if (d2 < distance2)
11932 if (distance2 < radius2)
11936 mapOfNodeDistance2[pts[i]] = distance2;
11937 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11939 if (distance2 < radius2)
11941 volInside = true; // one or more nodes inside the domain
11942 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11948 setOfInsideVol.insert(vtkId);
11949 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11950 int neighborsVtkIds[NBMAXNEIGHBORS];
11951 int downIds[NBMAXNEIGHBORS];
11952 unsigned char downTypes[NBMAXNEIGHBORS];
11953 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11954 for (int n = 0; n < nbNeighbors; n++)
11955 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11956 setOfVolToCheck.insert(neighborsVtkIds[n]);
11960 setOfOutsideVol.insert(vtkId);
11961 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11963 setOfVolToCheck.erase(vtkId);
11967 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11968 // If yes, add the volume to the inside set
11970 bool addedInside = true;
11971 std::set<int> setOfVolToReCheck;
11972 while (addedInside)
11974 MESSAGE(" --------------------------- re check");
11975 addedInside = false;
11976 std::set<int>::iterator itv = setOfInsideVol.begin();
11977 for (; itv != setOfInsideVol.end(); ++itv)
11980 int neighborsVtkIds[NBMAXNEIGHBORS];
11981 int downIds[NBMAXNEIGHBORS];
11982 unsigned char downTypes[NBMAXNEIGHBORS];
11983 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11984 for (int n = 0; n < nbNeighbors; n++)
11985 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11986 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11988 setOfVolToCheck = setOfVolToReCheck;
11989 setOfVolToReCheck.clear();
11990 while (!setOfVolToCheck.empty())
11992 std::set<int>::iterator it = setOfVolToCheck.begin();
11994 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11996 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11997 int countInside = 0;
11998 int neighborsVtkIds[NBMAXNEIGHBORS];
11999 int downIds[NBMAXNEIGHBORS];
12000 unsigned char downTypes[NBMAXNEIGHBORS];
12001 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12002 for (int n = 0; n < nbNeighbors; n++)
12003 if (setOfInsideVol.count(neighborsVtkIds[n]))
12005 MESSAGE("countInside " << countInside);
12006 if (countInside > 1)
12008 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12009 setOfInsideVol.insert(vtkId);
12010 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12011 addedInside = true;
12014 setOfVolToReCheck.insert(vtkId);
12016 setOfVolToCheck.erase(vtkId);
12020 // --- map of Downward faces at the boundary, inside the global volume
12021 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12022 // fill group of SMDS faces inside the volume (when several volume shapes)
12023 // fill group of SMDS faces on the skin of the global volume (if skin)
12025 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12026 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12027 std::set<int>::iterator it = setOfInsideVol.begin();
12028 for (; it != setOfInsideVol.end(); ++it)
12031 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12032 int neighborsVtkIds[NBMAXNEIGHBORS];
12033 int downIds[NBMAXNEIGHBORS];
12034 unsigned char downTypes[NBMAXNEIGHBORS];
12035 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12036 for (int n = 0; n < nbNeighbors; n++)
12038 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12039 if (neighborDim == 3)
12041 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12043 DownIdType face(downIds[n], downTypes[n]);
12044 boundaryFaces[face] = vtkId;
12046 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12047 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12048 if (vtkFaceId >= 0)
12050 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12051 // find also the smds edges on this face
12052 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12053 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12054 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12055 for (int i = 0; i < nbEdges; i++)
12057 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12058 if (vtkEdgeId >= 0)
12059 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12063 else if (neighborDim == 2) // skin of the volume
12065 DownIdType face(downIds[n], downTypes[n]);
12066 skinFaces[face] = vtkId;
12067 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12068 if (vtkFaceId >= 0)
12069 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12074 // --- identify the edges constituting the wire of each subshape on the skin
12075 // define polylines with the nodes of edges, equivalent to wires
12076 // project polylines on subshapes, and partition, to get geom faces
12078 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12079 std::set<int> emptySet;
12081 std::set<int> shapeIds;
12083 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12084 while (itelem->more())
12086 const SMDS_MeshElement *elem = itelem->next();
12087 int shapeId = elem->getshapeId();
12088 int vtkId = elem->getVtkId();
12089 if (!shapeIdToVtkIdSet.count(shapeId))
12091 shapeIdToVtkIdSet[shapeId] = emptySet;
12092 shapeIds.insert(shapeId);
12094 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12097 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12098 std::set<DownIdType, DownIdCompare> emptyEdges;
12099 emptyEdges.clear();
12101 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12102 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12104 int shapeId = itShape->first;
12105 MESSAGE(" --- Shape ID --- "<< shapeId);
12106 shapeIdToEdges[shapeId] = emptyEdges;
12108 std::vector<int> nodesEdges;
12110 std::set<int>::iterator its = itShape->second.begin();
12111 for (; its != itShape->second.end(); ++its)
12114 MESSAGE(" " << vtkId);
12115 int neighborsVtkIds[NBMAXNEIGHBORS];
12116 int downIds[NBMAXNEIGHBORS];
12117 unsigned char downTypes[NBMAXNEIGHBORS];
12118 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12119 for (int n = 0; n < nbNeighbors; n++)
12121 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12123 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12124 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12125 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12127 DownIdType edge(downIds[n], downTypes[n]);
12128 if (!shapeIdToEdges[shapeId].count(edge))
12130 shapeIdToEdges[shapeId].insert(edge);
12132 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12133 nodesEdges.push_back(vtkNodeId[0]);
12134 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12135 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12141 std::list<int> order;
12143 if (nodesEdges.size() > 0)
12145 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12146 nodesEdges[0] = -1;
12147 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12148 nodesEdges[1] = -1; // do not reuse this edge
12152 int nodeTofind = order.back(); // try first to push back
12154 for (i = 0; i<nodesEdges.size(); i++)
12155 if (nodesEdges[i] == nodeTofind)
12157 if (i == nodesEdges.size())
12158 found = false; // no follower found on back
12161 if (i%2) // odd ==> use the previous one
12162 if (nodesEdges[i-1] < 0)
12166 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12167 nodesEdges[i-1] = -1;
12169 else // even ==> use the next one
12170 if (nodesEdges[i+1] < 0)
12174 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12175 nodesEdges[i+1] = -1;
12180 // try to push front
12182 nodeTofind = order.front(); // try to push front
12183 for (i = 0; i<nodesEdges.size(); i++)
12184 if (nodesEdges[i] == nodeTofind)
12186 if (i == nodesEdges.size())
12188 found = false; // no predecessor found on front
12191 if (i%2) // odd ==> use the previous one
12192 if (nodesEdges[i-1] < 0)
12196 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12197 nodesEdges[i-1] = -1;
12199 else // even ==> use the next one
12200 if (nodesEdges[i+1] < 0)
12204 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12205 nodesEdges[i+1] = -1;
12211 std::vector<int> nodes;
12212 nodes.push_back(shapeId);
12213 std::list<int>::iterator itl = order.begin();
12214 for (; itl != order.end(); itl++)
12216 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12217 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12219 listOfListOfNodes.push_back(nodes);
12222 // partition geom faces with blocFissure
12223 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12224 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12230 //================================================================================
12232 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12233 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12234 * \return TRUE if operation has been completed successfully, FALSE otherwise
12236 //================================================================================
12238 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12240 // iterates on volume elements and detect all free faces on them
12241 SMESHDS_Mesh* aMesh = GetMeshDS();
12244 //bool res = false;
12245 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12246 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12249 const SMDS_MeshVolume* volume = vIt->next();
12250 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12251 vTool.SetExternalNormal();
12252 //const bool isPoly = volume->IsPoly();
12253 const int iQuad = volume->IsQuadratic();
12254 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12256 if (!vTool.IsFreeFace(iface))
12259 vector<const SMDS_MeshNode *> nodes;
12260 int nbFaceNodes = vTool.NbFaceNodes(iface);
12261 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12263 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12264 nodes.push_back(faceNodes[inode]);
12265 if (iQuad) { // add medium nodes
12266 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12267 nodes.push_back(faceNodes[inode]);
12268 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12269 nodes.push_back(faceNodes[8]);
12271 // add new face based on volume nodes
12272 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12274 continue; // face already exsist
12276 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12280 return ( nbFree==(nbExisted+nbCreated) );
12285 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12287 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12289 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12292 //================================================================================
12294 * \brief Creates missing boundary elements
12295 * \param elements - elements whose boundary is to be checked
12296 * \param dimension - defines type of boundary elements to create
12297 * \param group - a group to store created boundary elements in
12298 * \param targetMesh - a mesh to store created boundary elements in
12299 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12300 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12301 * boundary elements will be copied into the targetMesh
12302 * \param toAddExistingBondary - if true, not only new but also pre-existing
12303 * boundary elements will be added into the new group
12304 * \param aroundElements - if true, elements will be created on boundary of given
12305 * elements else, on boundary of the whole mesh.
12306 * \return nb of added boundary elements
12308 //================================================================================
12310 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12311 Bnd_Dimension dimension,
12312 SMESH_Group* group/*=0*/,
12313 SMESH_Mesh* targetMesh/*=0*/,
12314 bool toCopyElements/*=false*/,
12315 bool toCopyExistingBoundary/*=false*/,
12316 bool toAddExistingBondary/*= false*/,
12317 bool aroundElements/*= false*/)
12319 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12320 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12321 // hope that all elements are of the same type, do not check them all
12322 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12323 throw SALOME_Exception(LOCALIZED("wrong element type"));
12326 toCopyElements = toCopyExistingBoundary = false;
12328 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12329 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12330 int nbAddedBnd = 0;
12332 // editor adding present bnd elements and optionally holding elements to add to the group
12333 SMESH_MeshEditor* presentEditor;
12334 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12335 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12337 SMESH_MesherHelper helper( *myMesh );
12338 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12339 SMDS_VolumeTool vTool;
12340 TIDSortedElemSet avoidSet;
12341 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12344 typedef vector<const SMDS_MeshNode*> TConnectivity;
12346 SMDS_ElemIteratorPtr eIt;
12347 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12348 else eIt = elemSetIterator( elements );
12350 while (eIt->more())
12352 const SMDS_MeshElement* elem = eIt->next();
12353 const int iQuad = elem->IsQuadratic();
12355 // ------------------------------------------------------------------------------------
12356 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12357 // ------------------------------------------------------------------------------------
12358 vector<const SMDS_MeshElement*> presentBndElems;
12359 vector<TConnectivity> missingBndElems;
12360 TConnectivity nodes, elemNodes;
12361 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12363 vTool.SetExternalNormal();
12364 const SMDS_MeshElement* otherVol = 0;
12365 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12367 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12368 ( !aroundElements || elements.count( otherVol )))
12370 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12371 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12372 if ( missType == SMDSAbs_Edge ) // boundary edges
12374 nodes.resize( 2+iQuad );
12375 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12377 for ( int j = 0; j < nodes.size(); ++j )
12379 if ( const SMDS_MeshElement* edge =
12380 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12381 presentBndElems.push_back( edge );
12383 missingBndElems.push_back( nodes );
12386 else // boundary face
12389 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12390 nodes.push_back( nn[inode] ); // add corner nodes
12392 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12393 nodes.push_back( nn[inode] ); // add medium nodes
12394 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12396 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12398 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12399 SMDSAbs_Face, /*noMedium=*/false ))
12400 presentBndElems.push_back( f );
12402 missingBndElems.push_back( nodes );
12404 if ( targetMesh != myMesh )
12406 // add 1D elements on face boundary to be added to a new mesh
12407 const SMDS_MeshElement* edge;
12408 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12411 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12413 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12414 if ( edge && avoidSet.insert( edge ).second )
12415 presentBndElems.push_back( edge );
12421 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12423 avoidSet.clear(), avoidSet.insert( elem );
12424 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12425 SMDS_MeshElement::iterator() );
12426 elemNodes.push_back( elemNodes[0] );
12427 nodes.resize( 2 + iQuad );
12428 const int nbLinks = elem->NbCornerNodes();
12429 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12431 nodes[0] = elemNodes[iN];
12432 nodes[1] = elemNodes[iN+1+iQuad];
12433 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12434 continue; // not free link
12436 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12437 if ( const SMDS_MeshElement* edge =
12438 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12439 presentBndElems.push_back( edge );
12441 missingBndElems.push_back( nodes );
12445 // ---------------------------------
12446 // 2. Add missing boundary elements
12447 // ---------------------------------
12448 if ( targetMesh != myMesh )
12449 // instead of making a map of nodes in this mesh and targetMesh,
12450 // we create nodes with same IDs.
12451 for ( int i = 0; i < missingBndElems.size(); ++i )
12453 TConnectivity& srcNodes = missingBndElems[i];
12454 TConnectivity nodes( srcNodes.size() );
12455 for ( inode = 0; inode < nodes.size(); ++inode )
12456 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12457 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12459 /*noMedium=*/false))
12461 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12465 for ( int i = 0; i < missingBndElems.size(); ++i )
12467 TConnectivity& nodes = missingBndElems[i];
12468 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12470 /*noMedium=*/false))
12472 SMDS_MeshElement* elem =
12473 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12476 // try to set a new element to a shape
12477 if ( myMesh->HasShapeToMesh() )
12480 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12481 const int nbN = nodes.size() / (iQuad+1 );
12482 for ( inode = 0; inode < nbN && ok; ++inode )
12484 pair<int, TopAbs_ShapeEnum> i_stype =
12485 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12486 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12487 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12489 if ( ok && mediumShapes.size() > 1 )
12491 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12492 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12493 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12495 if (( ok = ( stype_i->first != stype_i_0.first )))
12496 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12497 aMesh->IndexToShape( stype_i_0.second ));
12500 if ( ok && mediumShapes.begin()->first == missShapeType )
12501 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12505 // ----------------------------------
12506 // 3. Copy present boundary elements
12507 // ----------------------------------
12508 if ( toCopyExistingBoundary )
12509 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12511 const SMDS_MeshElement* e = presentBndElems[i];
12512 TConnectivity nodes( e->NbNodes() );
12513 for ( inode = 0; inode < nodes.size(); ++inode )
12514 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12515 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12517 else // store present elements to add them to a group
12518 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12520 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12523 } // loop on given elements
12525 // ---------------------------------------------
12526 // 4. Fill group with boundary elements
12527 // ---------------------------------------------
12530 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12531 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12532 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12534 tgtEditor.myLastCreatedElems.Clear();
12535 tgtEditor2.myLastCreatedElems.Clear();
12537 // -----------------------
12538 // 5. Copy given elements
12539 // -----------------------
12540 if ( toCopyElements && targetMesh != myMesh )
12542 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12543 else eIt = elemSetIterator( elements );
12544 while (eIt->more())
12546 const SMDS_MeshElement* elem = eIt->next();
12547 TConnectivity nodes( elem->NbNodes() );
12548 for ( inode = 0; inode < nodes.size(); ++inode )
12549 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12550 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12552 tgtEditor.myLastCreatedElems.Clear();
12558 //================================================================================
12560 * \brief Copy node position and set \a to node on the same geometry
12562 //================================================================================
12564 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12565 const SMDS_MeshNode* to )
12567 if ( !from || !to ) return;
12569 SMDS_PositionPtr pos = from->GetPosition();
12570 if ( !pos || from->getshapeId() < 1 ) return;
12572 switch ( pos->GetTypeOfPosition() )
12574 case SMDS_TOP_3DSPACE: break;
12576 case SMDS_TOP_FACE:
12578 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12579 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12580 fPos->GetUParameter(), fPos->GetVParameter() );
12583 case SMDS_TOP_EDGE:
12585 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12586 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12587 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12590 case SMDS_TOP_VERTEX:
12592 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12595 case SMDS_TOP_UNSPEC: